Microsoft Teams Integration
Deliver rich Adaptive Card notifications directly to your Microsoft Teams channels. Get instant visibility into scan results, score changes, and security alerts with interactive cards that include grade displays, finding summaries, and action buttons linking back to full reports.
Overview
The Teams integration connects ComplianceLayer to your Microsoft Teams workspace using the Bot Framework and Microsoft Graph API. When configured, it delivers:
- Adaptive Card notifications with structured layouts, grade columns, and action buttons
- Rich formatting including ColumnSets for side-by-side data, FactSets for key-value pairs, and color-coded severity indicators
- Interactive action buttons that link directly to full scan reports in ComplianceLayer
- Weekly security digests summarizing all monitored domains with ranked scores and trend indicators
- Configurable event routing so different channels receive different notification types
Setup
Connect ComplianceLayer to your Teams workspace in a few steps:
- Navigate to Settings → Integrations → Teams
- Click Connect to Teams
- Sign in with your Microsoft account when the OAuth prompt appears
- Authorize ComplianceLayer to send messages on behalf of the bot
- Select the team and channel where notifications should be posted
- Choose which events you want to receive (scan completed, score changed, alerts)
- Optionally enable the weekly security digest
- Click Save Configuration
Architecture
The Teams integration uses Microsoft Bot Framework v4 to deliver Adaptive Cards to your channels. Here is how notifications flow from a scan event to your Teams channel:
When a subscribed event fires, the Teams Service constructs an Adaptive Card payload tailored to the event type, then delivers it through the Bot Framework to your configured channel. Cards are rendered natively by the Teams client with full support for dark mode, mobile, and desktop layouts.
Notification Types
scan.completedDelivered when a domain scan finishes successfully. The Adaptive Card uses a ColumnSet layout to display the grade prominently alongside the score and issue breakdown, with an action button linking to the full report.
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "Scan Completed: example.com",
"weight": "Bolder",
"size": "Large",
"wrap": true
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "B",
"size": "ExtraLarge",
"weight": "Bolder",
"color": "Warning"
},
{
"type": "TextBlock",
"text": "Grade",
"size": "Small",
"isSubtle": true,
"spacing": "None"
}
]
},
{
"type": "Column",
"width": "auto",
"items": [
{
"type": "TextBlock",
"text": "85",
"size": "ExtraLarge",
"weight": "Bolder"
},
{
"type": "TextBlock",
"text": "Score",
"size": "Small",
"isSubtle": true,
"spacing": "None"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "FactSet",
"facts": [
{ "title": "Critical", "value": "0" },
{ "title": "High", "value": "2" },
{ "title": "Medium", "value": "3" },
{ "title": "Low", "value": "3" }
]
}
]
}
]
},
{
"type": "TextBlock",
"text": "Scanned at 2026-03-08 12:34 UTC in 2.8s",
"size": "Small",
"isSubtle": true,
"spacing": "Medium"
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Full Report",
"url": "https://compliancelayer.net/report/12345"
}
]
}score.changedDelivered when a domain's security score changes between consecutive scans. Uses a FactSet to display old and new values with a delta indicator, making it easy to spot improvements or regressions at a glance.
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "Score Changed: example.com",
"weight": "Bolder",
"size": "Large",
"wrap": true
},
{
"type": "FactSet",
"facts": [
{ "title": "Previous Score", "value": "92 (A)" },
{ "title": "Current Score", "value": "78 (C+)" },
{ "title": "Change", "value": "-14 points" },
{ "title": "Previous Grade", "value": "A" },
{ "title": "Current Grade", "value": "C+" }
]
},
{
"type": "TextBlock",
"text": "Score decreased by 14 points since the last scan on 2026-03-01.",
"wrap": true,
"spacing": "Medium"
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Report",
"url": "https://compliancelayer.net/report/12346"
},
{
"type": "Action.OpenUrl",
"title": "Compare Scans",
"url": "https://compliancelayer.net/domains/example.com/history"
}
]
}alert.triggeredDelivered when an alert condition is met, such as a critical vulnerability detected, certificate expiration, or score drop below threshold. The card uses color-coded attention styles based on severity level.
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "Security Alert: example.com",
"weight": "Bolder",
"size": "Large",
"color": "Attention",
"wrap": true
},
{
"type": "Container",
"style": "attention",
"items": [
{
"type": "TextBlock",
"text": "CRITICAL",
"weight": "Bolder",
"color": "Attention"
},
{
"type": "TextBlock",
"text": "SSL Certificate Expired",
"weight": "Bolder",
"size": "Medium",
"wrap": true
},
{
"type": "TextBlock",
"text": "The SSL/TLS certificate for example.com expired on 2026-03-07. Visitors will see browser security warnings and connections are no longer encrypted.",
"wrap": true
}
]
},
{
"type": "FactSet",
"facts": [
{ "title": "Alert Type", "value": "cert_expired" },
{ "title": "Severity", "value": "Critical" },
{ "title": "Domain", "value": "example.com" },
{ "title": "Detected At", "value": "2026-03-08 12:34 UTC" }
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Alert Details",
"url": "https://compliancelayer.net/alerts/456"
},
{
"type": "Action.OpenUrl",
"title": "View Domain Dashboard",
"url": "https://compliancelayer.net/domains/example.com"
}
]
}Severity Styles: critical uses Attention (red) container style, high uses Warning (yellow), medium uses Accent (blue), low uses Default styling.
weekly.digestDelivered once per week (configurable day and time) with a summary of all monitored domains. Domains are ranked by score with trend indicators showing improvement or regression since the previous week.
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "Weekly Security Digest",
"weight": "Bolder",
"size": "Large"
},
{
"type": "TextBlock",
"text": "Week of March 3 - March 9, 2026",
"isSubtle": true,
"spacing": "None"
},
{
"type": "FactSet",
"facts": [
{ "title": "Domains Monitored", "value": "12" },
{ "title": "Average Score", "value": "81 (B)" },
{ "title": "Scans Run", "value": "48" },
{ "title": "Alerts Triggered", "value": "3" }
]
},
{
"type": "TextBlock",
"text": "Domain Rankings",
"weight": "Bolder",
"size": "Medium",
"spacing": "Large"
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{ "type": "TextBlock", "text": "Domain", "weight": "Bolder", "size": "Small" },
{ "type": "TextBlock", "text": "secure-app.com" },
{ "type": "TextBlock", "text": "api.example.com" },
{ "type": "TextBlock", "text": "dashboard.acme.io" },
{ "type": "TextBlock", "text": "store.example.com" }
]
},
{
"type": "Column",
"width": "auto",
"items": [
{ "type": "TextBlock", "text": "Grade", "weight": "Bolder", "size": "Small" },
{ "type": "TextBlock", "text": "A (95)" },
{ "type": "TextBlock", "text": "B+ (88)" },
{ "type": "TextBlock", "text": "B (82)" },
{ "type": "TextBlock", "text": "C (71)" }
]
},
{
"type": "Column",
"width": "auto",
"items": [
{ "type": "TextBlock", "text": "Trend", "weight": "Bolder", "size": "Small" },
{ "type": "TextBlock", "text": "+3" },
{ "type": "TextBlock", "text": "0" },
{ "type": "TextBlock", "text": "-2" },
{ "type": "TextBlock", "text": "+5" }
]
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Full Dashboard",
"url": "https://compliancelayer.net/dashboard"
}
]
}Adaptive Card Format
ComplianceLayer uses Adaptive Cards schema version 1.4, which is supported by Microsoft Teams on desktop, web, and mobile clients. Cards are constructed server-side and delivered through the Bot Framework connector.
Card Structure
Every notification card follows this base structure:
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "Card Title",
"weight": "Bolder",
"size": "Large",
"wrap": true
}
// ... card-specific content
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Button Label",
"url": "https://compliancelayer.net/..."
}
]
}Key Card Elements
The integration uses several Adaptive Card element types to present scan data effectively:
| Element | Usage | Example |
|---|---|---|
ColumnSet | Side-by-side layout for grade, score, and issue counts | Grade column next to score column next to findings |
FactSet | Key-value pairs for structured data like old/new scores | Previous Score: 92, Current Score: 78 |
Container | Styled wrappers for severity-based coloring | Red container for critical alerts |
TextBlock | Headings, descriptions, timestamps | Card title, scan timestamp, descriptions |
Action.OpenUrl | Buttons linking to ComplianceLayer reports | View Full Report, Compare Scans |
Color Mapping
Adaptive Card colors are mapped to ComplianceLayer grades for consistent visual feedback:
| Grade | Score Range | Adaptive Card Color |
|---|---|---|
| A / A+ | 90 - 100 | Good (green) |
| B / B+ | 80 - 89 | Warning (yellow) |
| C / C+ | 70 - 79 | Warning (yellow) |
| D | 60 - 69 | Attention (red) |
| F | 0 - 59 | Attention (red) |
Channel Configuration
Configure which events are delivered to each Teams channel and set thresholds to control notification volume.
Event Selection
Choose which events trigger notifications for each connected channel. Available events:
| Event | Description | Default |
|---|---|---|
scan.completed | Fires when any domain scan finishes | Enabled |
score.changed | Fires when a score changes between consecutive scans | Enabled |
alert.triggered | Fires when an alert condition is met | Enabled |
weekly.digest | Scheduled weekly summary of all domains | Disabled |
Score Threshold
Set a minimum score threshold to reduce noise. When configured, scan.completed notifications are only sent when the domain scores below the threshold. This is useful for filtering out passing scans and only alerting on domains that need attention.
{
"channel_id": "19:abc123@thread.tacv2",
"team_id": "team-uuid-here",
"events": ["scan.completed", "alert.triggered"],
"score_threshold": 80,
"weekly_digest": {
"enabled": true,
"day": "monday",
"time": "09:00",
"timezone": "America/New_York"
}
}scan.completed cards when the score is below 80. Alerts and score change events are always delivered regardless of the threshold setting.Weekly Digest Schedule
When enabled, the weekly digest is sent at a fixed day and time each week. Configure the schedule from Settings:
- Day: Any day of the week (default: Monday)
- Time: Hour in 24-hour format (default: 09:00)
- Timezone: IANA timezone string (default: UTC)
Multiple Channels
You can route different event types to different channels. Common patterns include:
- #security-alerts —
alert.triggeredonly, for immediate response - #scan-results —
scan.completedandscore.changed, for the security team - #weekly-review —
weekly.digestonly, for management visibility
To add additional channels, return to Settings → Integrations → Teams and click Add Channel. Each channel has independent event and threshold settings.
Programmatic Configuration
You can also configure the Teams integration via the ComplianceLayer API. This is useful for MSPs managing multiple tenants or automating setup.
# List configured Teams channels
curl -X GET https://api.compliancelayer.net/v1/integrations/teams/channels \
-H "Authorization: Bearer cl_your_api_key"
# Update channel configuration
curl -X PUT https://api.compliancelayer.net/v1/integrations/teams/channels/{channel_id} \
-H "Authorization: Bearer cl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"events": ["scan.completed", "alert.triggered", "score.changed"],
"score_threshold": 75,
"weekly_digest": {
"enabled": true,
"day": "monday",
"time": "09:00",
"timezone": "America/New_York"
}
}'
# Send a test notification
curl -X POST https://api.compliancelayer.net/v1/integrations/teams/test \
-H "Authorization: Bearer cl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"channel_id": "19:abc123@thread.tacv2"
}'
# Disconnect Teams integration
curl -X DELETE https://api.compliancelayer.net/v1/integrations/teams \
-H "Authorization: Bearer cl_your_api_key"Security
The Teams integration follows security best practices for OAuth, API access, and data handling.
Azure AD OAuth Scopes
During authorization, ComplianceLayer requests the following Azure AD permissions:
| Permission | Type | Purpose |
|---|---|---|
ChannelMessage.Send | Application | Send messages to authorized Teams channels |
Team.ReadBasic.All | Delegated | List teams for channel selection during setup |
Channel.ReadBasic.All | Delegated | List channels within selected teams during setup |
Graph API Permissions
The bot uses Microsoft Graph API to deliver messages. All API calls are authenticated using the Bot Framework service-to-service token. ComplianceLayer does not store your Microsoft password or personal access tokens.
Bot Framework Authentication
Communication between ComplianceLayer and Microsoft Teams uses the Bot Framework Connector service with the following security measures:
- Service-to-service auth: Bot Framework tokens are issued by Azure AD and validated on every request
- Channel binding: Tokens are scoped to specific channels, preventing message delivery to unauthorized destinations
- TLS encryption: All traffic between ComplianceLayer and Microsoft services uses TLS 1.2+
- Token rotation: Bot Framework tokens are short-lived and automatically rotated
Data Shared with Teams
Notification cards include only summary-level data. The following information is included in cards:
- Domain name
- Overall security score and grade
- Issue counts by severity (critical, high, medium, low)
- Alert type and severity level
- Timestamps
- Links back to ComplianceLayer (full report data requires authentication)
Detailed scan findings, raw vulnerability data, and personally identifiable information are never included in notification cards. Users must authenticate with ComplianceLayer to view full report details.
Revoking Access
To disconnect the Teams integration and revoke all access:
- Navigate to Settings → Integrations → Teams
- Click Disconnect
- Confirm the disconnection in the dialog
This immediately revokes the bot's ability to send messages to your channels. You can also revoke access from the Azure AD admin center:
- Go to Azure Portal → Azure Active Directory → Enterprise Applications
- Search for "ComplianceLayer"
- Select the application and click Properties
- Click Delete to fully remove the application grant
Troubleshooting
Bot Not Appearing in Channel
- Verify that your Microsoft 365 admin has approved the ComplianceLayer bot application
- Check that the bot is not blocked by a Teams app permission policy in your organization
- Ensure the selected channel allows bot messages (some private channels restrict bot access)
- Try removing and re-adding the bot: go to the channel, click the + tab, search for ComplianceLayer, and add it
- Wait up to 5 minutes for bot registration to propagate across Teams
Cards Not Rendering
- Adaptive Cards v1.4 requires Teams desktop 1.5+ or web client. Update your Teams client to the latest version
- Some older mobile clients may show a simplified fallback. Update the Teams mobile app
- Check if your organization has policies restricting Adaptive Card rendering
- Verify the card appears correctly using the Adaptive Cards Designer by pasting the card JSON
- If action buttons are missing, check that external link policies allow
compliancelayer.net
Authentication Errors
- "Need admin approval" — Your Azure AD admin must grant consent for the app. Share the admin consent URL from the error screen with your IT department
- "Invalid grant" — The authorization code has expired. Start the connection flow again from Settings
- "AADSTS65001" — The user has not consented to the required permissions. Re-authorize from Settings → Integrations → Teams
- "Insufficient privileges" — Your account does not have permission to install bots in the selected team. Contact your Teams owner
Reconnecting After Token Expiry
Bot Framework tokens are automatically refreshed. However, if the refresh token itself expires (typically after 90 days of inactivity), you will need to reconnect:
- Go to Settings → Integrations → Teams
- You will see a Reconnect Required banner if the token has expired
- Click Reconnect to start the OAuth flow again
- Your channel configurations and event settings are preserved
Notifications Delayed
- Bot Framework message delivery typically takes 1-3 seconds but can be delayed during Microsoft service incidents
- Check the Microsoft 365 service health page for ongoing issues
- Verify the notification was sent by checking Settings → Integrations → Teams → Delivery History
- Rate limiting may apply if sending more than 4 messages per second to a single channel
Missing Events
- Confirm the event type is enabled for the target channel in your configuration
- Check if the score threshold is filtering out the notification (only applies to
scan.completed) - Verify the domain being scanned is in your monitored domains list
- Review delivery history for any failed delivery attempts and error messages
Next Steps
Support
Need help with the Teams integration? Contact our support team:
- Email: support@compliancelayer.net
- Dashboard: View delivery logs in Settings → Integrations → Teams → Delivery History
- Status: Check system status page for any ongoing issues