0 Post Notifications to Microsoft Teams Shared by Zack Dutra, Bayside Covenant Church 10 days ago 16.0 Workflow Advanced Send Rock notifications directly to a user’s Teams Activity Feed with a link that opens your Rock site in the system browser. Think of it like this: When Rock creates a notification, the recipient instantly gets a Teams alert that says “New Rock notification.” Clicking it jumps into a light Teams app that immediately opens your Rock page in the system browser (not the in-Teams iframe) with your desired URL. No bots, channels, or tenant-wide message spam — just a clean, user-targeted activity feed notification. How it works A Workflow Trigger runs Post-Add on the Notification Message entity. That workflow builds a Teams link, then calls the Graph sendActivityNotification API for the target user. The Teams app tab reads the link context and immediately opens the final URL in the default browser for a seamless hand-off. Prerequisites Ability to create a simple custom Microsoft Teams app (personal app with one static tab). Microsoft Entra app registration with Delegated permission: TeamsActivity.Send. Rock admin access to set Global Attributes, import or create a workflow type, add an HTML Content block, and create a Workflow Trigger. Single Sign On by BEMA Information Technologies plugin or alternative to map Microsoft usernames to people in Rock. See Workflow section for details. Global Attributes (Microsoft values) MicrosoftAzureTenantId – your Tenant ID (find it on Entra overview). MicrosoftRockRMSEnterpriseApplicationID – your App Registration’s Application (client) ID. MicrosoftRockEnterpriseApplicationSecret – client secret (remember: you can only view at creation and it expires). Step 1: Create the Entra App Registration Register a new application for Rock. Add the Delegated API permission TeamsActivity.Send; grant admin consent. Create a client secret; record the value in Rock Global Attributes above. Step 2: Build the Teams App Package Your app is a simple personal app with a single static tab. Package consists of three files zipped together (not a parent folder): color.png – 192×192, full-color icon. outline.png – 32×32, white on transparent (enterprise-safe) icon. manifest.json – app definition. Microsoft Documentation: https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/apps-package Upload via Teams admin center → Manage apps → Actions → Upload new app. Test as a user, then deploy org-wide as desired. A sample package is available on GitHub here: https://github.com/bayside-church/rock-notifications-sample/tree/main Manifest fields to update Use the json template provided (key pieces shown here); update the items called out in ALL CAPS. Also bump version when you change the manifest (e.g., 1.0.0 → 1.0.1). { "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.23/MicrosoftTeams.schema.json", "manifestVersion": "1.23", "version": "1.0.0", // bump when you update "id": "INSERT YOUR RANDOM GUID", // app id (not your Entra app id) "developer": { "name": "Rock RMS", "websiteUrl": "https://YOUR-SITE", "privacyUrl": "https://YOUR-SITE/privacy", "termsOfUseUrl": "https://YOUR-SITE/terms" }, "name": { "short": "Rock Notifications", "full": "Rock Notifications" }, "description": { "short": "Activity-feed notifications from Rock.", "full": "Sends Teams activity-feed notifications with link to Rock." }, "icons": { "outline": "outline.png", "color": "color.png" }, "accentColor": "#3c7f6c", // tie the Teams app to your Entra app registration "webApplicationInfo": { "id": "GUID OF APP REGISTRATION", "resource": "api://GUID OF APP REGISTRATION" }, // activity feed type used by the workflow call "activities": { "activityTypes": [ { "type": "rockAlert", "description": "Notification from Rock", "templateText": "{title} — {summary}" } ] }, // personal tab that immediately opens your Rock URL "staticTabs": [ { "entityId": "home", "name": "Rock Notifications", "contentUrl": "https://YOUR-ROCK-SITE/path/to/teams-launch-page", // set to page you add below "websiteUrl": "https://YOUR-ROCK-SITE", "scopes": ["personal"] } ], "defaultInstallScope": "personal", "validDomains": ["YOUR-ROCK-SITE"] } Step 3: Add the Teams “launch” page in Rock Create an internal support page and drop in an HTML Content block with this code. The tab initializes, reads the deep-link context, and opens that target URL in the system browser. This page needs view permissions for All Users, publicly accessible so that Teams can process the content. It will also display in Teams to your user, it can be a simple splash page. {% comment %} Launch page that immediately opens an external URL in the system browser {% endcomment %} <script src="https://res.cdn.office.net/teams-js/2.0.0/js/MicrosoftTeams.min.js"></script> <div style="width:100%;text-align:center;">Opened in Browser</div> <script> let lastOpened = null; async function openFromContext() { await microsoftTeams.app.initialize(); const ctx = await microsoftTeams.app.getContext(); const qs = new URLSearchParams(location.search); const target = (ctx && ctx.page && ctx.page.subPageId) || qs.get('to'); if (!target || target === lastOpened) return; lastOpened = target; try { await microsoftTeams.app.openLink(target); } catch(e) { window.top.location.href = target; } } // Run on first load… openFromContext(); // …and whenever the tab becomes visible again (e.g., user clicks a new notification) document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') openFromContext(); }); </script> Important: Set staticTabs[0].contentUrl in your manifest to the URL of this page so the app opens it on launch. Step 4: Import the Workflow Type Import the provided workflow type: Teams Notification – New Rock Notification. It includes: Start activity. Set Notification Message Id From Entity (SetAttributeFromEntity) — stores the newly created NotificationMessage Id into a workflow attribute. Set Person from Notification Message (RunLava) — resolves the Person.PrimaryAlias Guid from the notification (used to map your Teams user). POST Teams Notification (RunLava) — builds the Teams deep link and calls Graph /users/{username}/teamwork/sendActivityNotification. Workflow Complete. The POST action’s Lava (excerpt) shows required attributes and payload shape. You only need to set the attributes once, then the workflow handles each new notification. {% comment %} Build deep link and send activity notification {% endcomment %} {% assign msUsername = Workflow | Attribute:'MicrosoftUserName' %} {% assign tenantId = 'Global' | Attribute:'MicrosoftAzureTenantId' %} {% assign clientId = 'Global' | Attribute:'MicrosoftRockRMSEnterpriseApplicationID' %} {% assign clientSecret = 'Global' | Attribute:'MicrosoftRockEnterpriseApplicationSecret' %} {% assign teamsAppId = Workflow | Attribute:'TeamsAppId' %} {% assign access_token = Workflow | Attribute:'MicrosoftToken' %} {% assign manifestAppId = Workflow | Attribute:'ManifestAppId' %} {% comment %} Force refresh in Teams with a timestamp {% endcomment %} {% capture targetUrl %}{{ 'Global' | Attribute:'InternalApplicationRoot' }}notifications?via=teams&n={{ 'Now' | Date:'yyyyMMddHHmmssfff' }}{% endcapture %} {% capture contextJson %}{ "subEntityId": "{{ targetUrl }}" }{% endcapture %} {% capture deepLink %}https://teams.microsoft.com/l/entity/{{ manifestAppId }}/redirect?context={{ contextJson | EscapeDataString }}{% endcapture %} {% capture payload %} { "teamsAppId": "{{ teamsAppId }}", "topic": { "source": "text", "value": "Rock Notification", "webUrl": "{{ deepLink }}" }, "activityType": "rockAlert", "previewText": { "content": "{{ Workflow | Attribute:'NotificationDescription' }}" }, "templateParameters": [ { "name": "title", "value": "Rock RMS" }, { "name": "summary", "value": "New notification" } ] } {% endcapture %} {% webrequest url:'https://graph.microsoft.com/v1.0/users/{{ msUsername }}/teamwork/sendActivityNotification' method:'POST' headers:'Authorization^Bearer {{ access_token }}' body:'{{ payload | Trim | StripNewLines }}' requestcontenttype:'application/json' %} {{ results | ToJSON }} {% endwebrequest %} Workflow Attributes to provide TeamsAppId – the Teams catalog App ID of your uploaded app. ManifestAppId – the same app’s manifest id (GUID used in the deep link). The workflow uses the UserLogin table to grab the Person's UserLogin with a UserName containing Office365_. This UserLogin is provided by the Single Sign On by BEMA Information Technologies plugin.. If you do not have that plugin, you need to come up with another way to map Microsoft user names to your Rock users. A person attribute could do the trick, or the person's email if it stays up to date. Step 5: Create the Workflow Trigger Go to Settings → General → Workflow Triggers → New and set: Trigger Type: Post-Add Entity Type: Notification Message Workflow Type: Teams Notification – New Rock Notification Active: ✅ Step 6: Package & Install the Teams App Create your color.png (192×192) and outline.png (32×32, white on transparent). Edit manifest.json fields listed above (ids, URLs, domains). Bump the version string when you change anything after your first upload to Teams admin center. Select the three files and zip them (zip the files themselves — not a parent folder). In Teams admin center → Manage apps, choose Actions → Upload new app and upload the zip. Test as a user; then deploy per policy. Microsoft Documentation: https://learn.microsoft.com/en-us/microsoftteams/teams-custom-app-policies-and-settings. Testing Log in to Rock as a user who will receive a Notification Message. Cause a notification (e.g., mention the user in a Note). Confirm a Teams activity appears; click it — the personal tab opens and immediately launches your Rock URL in the system browser. Notes & Tips Keep your client secret rotated and stored in Rock Global Attributes with appropriate security. If you edit the manifest, increment the version, re-zip, and re-upload to update the app across the tenant. Included with this recipe is a workflow export named Teams Notification – New Rock Notification so you can import and review all attributes, activities, and actions exactly as referenced above. If you import that file, replace the default values for ManifestAppId and TeamsAppId, they are currently randomly generated guids. Questions/Issues? Reach out on Rocket Chat. Download File