Install Camunda for Microsoft Teams
Install and configure Camunda for Microsoft Teams in a Self-Managed environment using Docker.
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 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.
- Access the Identity management in your Camunda Self-Managed distribution.
- 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://<your-public-url>/* |
Note down the generated clientId and clientSecret. You will need them for auth.spa in the configuration file.
Grant offline access role
- Keycloak
- Entra
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
npm install -g @camunda/teams-app-integration-cli
Create a new Teams app project
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
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:
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.
If you need to retrieve the configuration snippet again later, run:
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:
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: <your-app-integrations-url>/api/event/exporter/batch
-
Replace
<your-app-integrations-url>with the base URL of your App Integrations backend. The URL must point to the/api/event/exporter/batchendpoint (for example,https://app-integrations.camunda.your-domain.com/api/event/exporter/batch). -
The
args.urlmust 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
existingSecretreferences a Kubernetes Secret containing the API key. Create this secret in your cluster before deploying:kubectl create secret generic app-integrations-secret --from-literal=apiKey=<your-exporter-api-key>
The same API key must be set in the exporter.apiKey field of the App Integrations 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.
- Keycloak
- Entra
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://<your-camunda-host>/auth/realms/camunda-platform. |
auth.audience | Yes | Typically camunda-platform. |
auth.m2m.clientId | Yes | M2M client ID from Step 1. |
auth.m2m.clientSecret | Yes | M2M client secret from Step 1. |
auth.spa.clientId | Yes | SPA client ID from Step 1. |
auth.spa.clientSecret | Yes | SPA client secret from Step 1. |
Example:
auth:
kind: keycloak
m2m:
clientId: <your-m2m-client-id>
clientSecret: <your-m2m-client-secret>
spa:
clientId: <your-spa-client-id>
clientSecret: <your-spa-client-secret>
issuer: https://<your-camunda-host>/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/<tenantId>/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/<tenantId>/oauth2/v2.0/token. |
auth.entra.jwksUrl | No | Defaults to https://login.microsoftonline.com/<tenantId>/discovery/v2.0/keys. |
auth.entra.scope | No | Defaults to openid profile offline_access <spa.clientId>/.default. |
auth.entra.usernameClaim | No | The ID token claim used to resolve the user's email. Default: preferred_username. |
Example:
auth:
kind: entra
m2m:
clientId: <your-entra-m2m-client-id>
clientSecret: <your-entra-m2m-client-secret>
spa:
clientId: <your-entra-spa-client-id>
clientSecret: <your-entra-spa-client-secret>
entra:
tenantId: <your-entra-tenant-id>
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.
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:
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 and the Teams configuration from Step 2.
- The
exporter.apiKeymust match the API key configured in the Orchestration Cluster Helm chart in Step 3. - For production deployments, replace sensitive values with environment variable references as described in Secret management.
- See Auth configuration for the full
authblock reference.
serverPort: 8080
stage: prod
# See "Auth configuration" above for Keycloak and Entra variants.
auth:
kind: keycloak
m2m:
clientId: <your-m2m-client-id>
clientSecret: <your-m2m-client-secret>
spa:
clientId: <your-spa-client-id>
clientSecret: <your-spa-client-secret>
issuer: https://<your-camunda-host>/auth/realms/camunda-platform
audience: camunda-platform
db:
username: <your-postgres-username>
password: <your-postgres-password>
database: <your-database-name>
host: <your-postgres-host>
loginType: password
encryptionKey: "<your-32-character-encryption-key>"
# Paste the output of `c8teams show-config` here:
teams:
clientId: <your-azure-ad-client-id>
appId: <your-teams-app-id>
appPassword: <your-azure-ad-app-password>
tenantId: <your-azure-ad-tenant-id>
tabEndpoint: https://<your-public-url>/ms-teams-app
session:
secure: true
secret: <your-random-session-secret>
frontendUrl: https://<your-public-url>
backendUrl: https://<your-public-url>
flavor: self-managed
organisation:
name: <your-organization-name>
clusters:
- uuid: <unique-cluster-uuid>
name: <cluster-display-name>
urls:
orchestration: https://<your-camunda-host>/orchestration
tasklist: https://<your-camunda-host>/tasklist
# Extended format (object with base and task):
# tasklist:
# base: https://<your-camunda-host>/tasklist
# task: https://<your-camunda-host>/tasklist/tasks/:userTaskKey/view
operate: https://<your-camunda-host>/operate
exporter:
apiKey: <your-exporter-api-key>
subscriptions: {}
The urls.tasklist field supports two formats:
- Simple (legacy) format — a plain URL string (for example,
https://<your-camunda-host>/tasklist). Deep links to tasks fall back to{tasklist-url}/tasklist/{userTaskKey}. - Extended format — an object with
baseandtaskfields. Thetaskfield is a URL template containing a:userTaskKeyplaceholder (for example,https://<your-camunda-host>/tasklist/tasks/:userTaskKey/view). When the app generates deep links to tasks (for example, in Teams notification cards), it replaces:userTaskKeywith 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.kindand the corresponding auth fields for your identity provider (see Auth configuration). - Set each entry in
clusters[].urlsto the correct Camunda service URLs. - Set
frontendUrlandbackendUrlto your public deployment URL.
Start the backend
Run the App Integrations backend container with the configuration file mounted:
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
CONFIGenvironment variable: Must be set toconfig/app-integrations.yamlto point to the mounted configuration file.NODE_ENVenvironment variable: Set toproductionfor deployed environments.- Volume mount: The
config.yamlfile is mounted to/app/apps/backend/config/app-integrations.yamlinside 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. | |
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/<tenantId>/oauth2/v2.0/token. | |
entra.jwksUrl | (Entra only, optional) JWKS endpoint. Defaults to https://login.microsoftonline.com/<tenantId>/discovery/v2.0/keys. | |
entra.scope | (Entra only, optional) OAuth2 scope. Defaults to openid profile offline_access <spa.clientId>/.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. | |
serviceUrl | Bot Framework service URL (default: https://smba.trafficmanager.net/teams). See note on serviceUrl. | |
| 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. | |
| 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 setmultitenanttotrueif 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 tofalse. -
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.