LogoLogo
SlackLogin
  • Overview
    • Welcome
  • Setup & Configuration
    • Connecting to Trunk
    • Managing Your Organization
      • GitHub App Permissions
  • Integration with Slack
  • Flaky Tests (Beta)
    • Overview
    • Get Started
      • Test Frameworks
        • Android
        • Bazel
        • Behave
        • cargo-nextest
        • Cypress
        • Dart Test
        • Go
        • GoogleTest
        • Gradle
        • Jasmine
        • Jest
        • Karma
        • Maven
        • minitest
        • Mocha
        • Nightwatch
        • NUnit
        • Pest
        • PHPUnit
        • Playwright
        • Pytest
        • Robot Framework
        • RSpec
        • Swift Testing
        • Vitest
        • XCTest
        • Other Test Frameworks
      • CI Providers
        • Azure DevOps Pipelines
        • BitBucket Pipelines
        • Buildkite
        • CircleCI
        • Drone CI
        • GitHub Actions
        • GitLab
        • Jenkins
        • Semaphore CI
        • TeamCity
        • Travis CI
        • Other CI Providers
    • Dashboard
    • Flaky Test Detection
    • Quarantining
    • PR Comments
    • Ticketing Integrations
      • Jira Integration
      • Linear Integration
      • Other Ticketing Platforms
    • Webhooks
      • Slack Integration
      • Microsoft Teams Integration
      • GitHub Issues Integration
      • Linear Integration
    • Uploader CLI Reference
  • Merge Queue
    • Overview
    • How does it work?
    • Setup
      • Quick Start
      • Settings
      • Integration for Slack
    • Concepts and Optimizations
      • Predictive Testing
      • Optimistic Merging
      • Pending Failure Depth
      • Anti-Flake Protection
      • Batching
      • Parallel Queues
        • Bazel
        • Nx
        • API
      • FAQ
    • Priority
    • Managing Merge Queue
      • Using the Merge UI
      • Metrics
      • Command Line
    • Webhooks
    • Reference
  • Code Quality
    • Overview
    • Why Metalinters?
      • How does it work?
      • Why Code Quality?
    • Setup & Installation
      • Initialize Trunk
      • Local Linting
      • Linting in CI
      • Nightly Report (Deprecated)
    • IDE Integration
      • VSCode
      • Neovim
      • GitHub Codespaces
    • Linters
      • Supported Linters
        • Actionlint
        • Ansible-lint
        • Autopep8
        • Bandit
        • Biome
        • Black
        • Brakeman
        • buf
        • Buildifier
        • cfnlint
        • Checkov
        • circleci
        • ClangFormat
        • clang-tidy
        • Clippy
        • cmake-format
        • codespell
        • cspell
        • cue-fmt
        • dart
        • deno
        • Detekt
        • djlint
        • dotenv-linter
        • dotnet-format
        • dustilock
        • ESLint
        • Flake8
        • git-diff-check
        • Gitleaks
        • Gofmt
        • gofumpt
        • goimports
        • gokart
        • golangci-lint
        • golines
        • google-java-format
        • graphql-schema-linter
        • hadolint
        • haml-lint
        • isort
        • iwyu
        • ktlint
        • kube-linter
        • markdown-link-check
        • markdown-table-prettify
        • Markdownlint
        • markdownlint-cli2
        • mypy
        • nancy
        • nixpkgs-fmt
        • opa
        • OSV-Scanner
        • Oxipng
        • perlcritic
        • perltidy
        • php-cs-fixer
        • phpstan
        • pmd
        • pragma-once
        • pre-commit-hooks
        • Prettier
        • prisma
        • psscriptanalyzer
        • Pylint
        • pyright
        • regal
        • remark-lint
        • renovate
        • rome
        • rubocop
        • Ruff
        • rufo
        • rustfmt
        • scalafmt
        • semgrep
        • ShellCheck
        • shfmt
        • sort-package-json
        • sourcery
        • sql-formatter
        • SQLFluff
        • sqlfmt
        • squawk
        • standardrb
        • stringslint
        • stylelint
        • stylua
        • SVGO
        • swiftformat
        • swiftlint
        • taplo
        • Terraform
        • terragrunt
        • terrascan
        • TFLint
        • tfsec
        • tofu
        • Trivy
        • Trufflehog
        • txtpbfmt
        • vale
        • Yamllint
        • yapf
      • Run Linters
      • Manage Linters
      • Configure Linters
      • Ignoring Issues and Files
      • Custom Linters
      • Shared Configs
      • Upgrades
    • Debugging
    • Licensing
  • CLI & API References
    • CLI Reference
      • Install
      • Getting Started
        • Code Quality
        • Merge Queue
        • Flaky Tests
        • Tools
        • Actions
          • Git Hooks
        • Announce
      • Compatibility
      • Caching
      • Commands Reference
        • Code Quality
        • Actions
        • Merge
      • Configuration
        • Plugins
          • Share Config Between Codebases
          • Exporting Linter Configs
        • Runtimes
        • Tools
        • Lint
          • Definitions
          • Commands
          • Output
          • Output Parsing
          • Files and Caching
          • Dependencies
          • Auto-Enable
        • Actions
          • Notifications
          • Logging and Troubleshooting
        • Merge
        • Telemetry
        • Per User Overrides
    • API Reference
      • Flaky Tests
      • Merge Queue
      • Webhooks Reference
  • Pricing & Security
    • Security
  • Billing
  • Community & Support
  • Links
    • Open App
    • Slack Community
    • Changelog
    • Feature Requests
On this page
  • 1. Configure Incoming Webhooks For Your Team
  • 2. Add a New Webhook
  • 3. Customize Your Transformation
  • 4. Test Your Webhook
  • 5. Monitoring Webhooks
  • Congratulations!
Edit on GitHub
  1. Flaky Tests (Beta)
  2. Webhooks

Microsoft Teams Integration

Last updated 1 month ago

Trunk allows you to create custom workflows to send customized messages to Microsoft Teams channels through webhooks.

This guide will walk you through sending Microsoft Teams messages using event-triggered webhooks. By the end of this tutorial, you'll receive Microsoft Teams messages for test status changes. This guide should take 10 minutes to complete.

1. Configure Incoming Webhooks For Your Team

  1. Open the team in which you want to add the webhook and select the kebab menu (•••) from the upper-right corner.

  2. Select Workflows from the dropdown menu.

  3. Search for Post to a channel when a webhook request is received, select the workflow, then click Next.

  4. Configure the workflow's Microsoft Teams Team and Microsoft Teams Channel, then click Add workflow.

  5. When the workflow is added correctly, you can copy the URL displayed, then click Done.

2. Add a New Webhook

Trunk uses Svix to integrate with other services, such as Microsoft Teams messages through webhooks.

You can create a new endpoint by:

  1. From your profile on the top right, navigate to Settings

  2. Under Organization > Webhooks, click Teams

  3. Create the new endpoint. You will be redirected to the endpoint configuration view.

3. Customize Your Transformation

Transformations are custom code snippets you can write to customize the Microsoft Teams messages created by the webhook. A working template transformation will be added automatically for your webhook, but you can further customize the behavior of this webhook.

  1. In the endpoint configuration view, navigate to the Advanced tab. Under Transformation, toggle the Enabled switch.

  2. Click Edit transformation to update your transformation code, and click Save to update the transformation.

/**
 * @param webhook the webhook object
 * @param webhook.method destination method. Allowed values: "POST", "PUT"
 * @param webhook.url current destination address
 * @param webhook.eventType current webhook Event Type
 * @param webhook.payload JSON payload
 * @param webhook.cancel whether to cancel dispatch of the given webhook
 */
function handler(webhook) {
  // See https://learn.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#send-adaptive-cards-using-an-incoming-webhook
  webhook.payload = summarizeTestCase(webhook.payload);

  return webhook;
}

function summarizeTestCase(payload) {
    if (!payload || typeof payload !== 'object' || !payload.test_case) {
        return {
            type: "message",
            attachments: [{
                contentType: "application/vnd.microsoft.card.adaptive",
                contentUrl: null,
                content: {
                    $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
                    type: "AdaptiveCard",
                    version: "1.2",
                    body: [{
                        type: "TextBlock",
                        text: "Error: Invalid or missing payload received by Trunk Flaky Test Webhook Transformation.",
                        color: "attention"
                    }]
                }
            }]
        };
    }

    const {
        test_case: {
            name = "N/A",
            file_path = "N/A",
            status = {},
            quarantine = false,
            repository = {},
            codeowners = [],
            failure_rate_last_7d = 0,
            most_common_failures = [],
            pull_requests_impacted_last_7d = 0,
            ticket = {},
            html_url = "N/A"
        }
    } = payload;

    const statusTimestamp = status.timestamp 
        ? new Date(status.timestamp).toLocaleString()
        : "Unknown";

    const failureBlocks = most_common_failures.map(failure => ({
        type: "TextBlock",
        text: `• ${failure.summary}`,
        wrap: true,
        spacing: "small"
    }));

    return {
        type: "message",
        attachments: [{
            contentType: "application/vnd.microsoft.card.adaptive",
            contentUrl: null,
            content: {
                $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
                type: "AdaptiveCard",
                version: "1.2",
                body: [
                    {
                        type: "TextBlock",
                        text: name,
                        size: "large",
                        weight: "bolder"
                    },
                    {
                        type: "TextBlock",
                        text: file_path,
                        isSubtle: true,
                        spacing: "none"
                    },
                    {
                        type: "FactSet",
                        facts: [
                            {
                                title: "Status",
                                value: `${status.value || "Unknown"} (${status.reason?.trim() || "N/A"})`
                            },
                            {
                                title: "Last Updated",
                                value: statusTimestamp
                            },
                            {
                                title: "Quarantine Status",
                                value: quarantine ? "Quarantined" : "Not Quarantined"
                            },
                            {
                                title: "Failure Rate (7d)",
                                value: `${(failure_rate_last_7d * 100).toFixed(1)}%`
                            },
                            {
                                title: "PRs Impacted (7d)",
                                value: pull_requests_impacted_last_7d.toString()
                            },
                            {
                                title: "Codeowners",
                                value: codeowners.join(", ") || "None"
                            }
                        ]
                    },
                    {
                        type: "TextBlock",
                        text: "Most Common Failures",
                        weight: "bolder",
                        spacing: "medium"
                    },
                    ...failureBlocks,
                    {
                        type: "ActionSet",
                        actions: [
                            {
                                type: "Action.OpenUrl",
                                title: "View Repository",
                                url: repository.html_url || "#"
                            },
                            {
                                type: "Action.OpenUrl",
                                title: "View Test Details",
                                url: html_url || "#"
                            },
                            {
                                type: "Action.OpenUrl",
                                title: "View Related Ticket",
                                url: ticket.html_url || "#"
                            }
                        ]
                    }
                ]
            }
        }]
    };
}

4. Test Your Webhook

You can send test messages to your Microsoft Teams channels as you make updates. You can do this by:

  1. In the endpoint configuration view, navigate to the Testing tab and select a Send event

  2. Under Subscribed events, select test_case.status_changedas the event type to send.

  3. Click Send Example to test your webhook

5. Monitoring Webhooks

You can monitor the events and the webhook's delivery logs in the Overview tab of an endpoint configuration view.

You can see an overview of how many webhook deliveries have been attempted, how many are successful, how many are in flight, and how many fail in the Attempt Delivery Status modal.

Congratulations!

You should now receive notifications in your Teams channel when a test's status changes. You can further modify your transformation script to customize your messages.

Microsoft has two different concepts for accepting incoming webhooks. Connectors that are being deprecated and Workflows that are for newly created teams. This guide is for the newer Workflows. The workflow for configuring webhooks is similar, but you may see small differences. You can find more about the soon to be deprecated connectors in .

Login to

Paste your Microsoft Teams Workflow URL from into Endpoint URL.

Review the transformation code automatically generated for Teams messages. You can customize this transformation at any time. Learn more about .

You can test the transformation by selecting the test_case.status_changed payload and clicking Run Test. This will test the transformation but not send a message. You will learn to send a test message in .

Below is an example of a webhook transformation to format the messages as . If you're having trouble adding a new webhook endpoint with Svix, please see the .

You can see a list of past delivery attempts in the Message Attempts modal. You can filter this list by Succeeded and Failed status, and you can click on each message to see the Message content, response code, and error message of each attempt. You can learn more about and in the Svix docs.

Microsoft's documentation
Trunk Flaky Tests
Actionable Messages
Adding Endpoint docs from Svix
replaying messages
filtering logs
See the Trunk webhook event catalog
Learn more about consuming webhooks in the Svix docs
Learn more about Microsoft Teams Workflow Webhooks
the previous step
customizing transformations
step 4