Skip to main content

Microsoft Teams

Send notifications to Microsoft Teams channels via incoming webhooks. Like Discord, Teams uses a webhook-based connection model — you create an incoming webhook in your Teams channel and register it with Zyphr.

Features​

  • Webhook-Based Setup - No OAuth or app registration required
  • Adaptive Cards - Rich, interactive message formatting
  • Theme Colors - Color-coded messages for visual categorization
  • URL Validation - Verify webhook URLs before saving
  • Test Messages - Send test messages to verify connectivity
  • Delivery Tracking - Full message lifecycle with status updates

Quick Start​

1. Create a Teams Incoming Webhook​

In your Microsoft Teams channel:

  1. Click the ... menu on the target channel
  2. Select Connectors (or Manage channel > Connectors)
  3. Find Incoming Webhook and click Configure
  4. Name the webhook (e.g., "Zyphr Notifications")
  5. Optionally upload a custom icon
  6. Click Create and copy the webhook URL

2. Register the Connection​

Via Dashboard​

  1. Navigate to Integrations in the sidebar
  2. Click Connect Microsoft Teams
  3. Paste your Teams webhook URL
  4. Give the connection a name
  5. Click Create

Via API​

curl -X POST https://api.zyphr.dev/v1/teams/connections \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Engineering Alerts",
"webhook_url": "https://outlook.office.com/webhook/..."
}'

3. Send a Message​

curl -X POST https://api.zyphr.dev/v1/teams/send \
-H "X-API-Key: zy_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"connection_id": "conn_abc123",
"text": "Hello from Zyphr!"
}'
await zyphr.teams.send({
connectionId: 'conn_abc123',
text: 'Hello from Zyphr!',
});

Sending Messages​

Basic Text Message​

curl -X POST https://api.zyphr.dev/v1/teams/send \
-H "X-API-Key: zy_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"connection_id": "conn_abc123",
"text": "New deployment completed for production."
}'

Message Parameters​

ParameterTypeRequiredDescription
connection_idstringYesThe Teams connection to send through
textstringConditionalMessage text (required if no adaptive_card)
adaptive_cardobjectNoAdaptive Card JSON for rich formatting
theme_colorstringNoHex color for the message accent stripe
sectionsarrayNoMessage Card sections (legacy format)

Themed Messages​

Use theme_color to add a colored accent stripe:

curl -X POST https://api.zyphr.dev/v1/teams/send \
-H "X-API-Key: zy_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"connection_id": "conn_abc123",
"text": "Build failed on main branch",
"theme_color": "FF0000"
}'
Theme Color Conventions
  • 2ecc71 — Green (success, deployments)
  • e74c3c — Red (errors, failures)
  • 3498db — Blue (info, updates)
  • f39c12 — Orange (warnings)

Adaptive Cards​

Adaptive Cards provide rich, interactive message layouts:

curl -X POST https://api.zyphr.dev/v1/teams/send \
-H "X-API-Key: zy_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"connection_id": "conn_abc123",
"adaptive_card": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "Deployment Complete",
"weight": "Bolder",
"size": "Large"
},
{
"type": "FactSet",
"facts": [
{ "title": "Environment", "value": "Production" },
{ "title": "Version", "value": "v2.4.1" },
{ "title": "Status", "value": "Success" },
{ "title": "Duration", "value": "3m 42s" }
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View Logs",
"url": "https://yourapp.com/deployments/123"
}
]
}
}'
await zyphr.teams.send({
connectionId: 'conn_abc123',
adaptiveCard: {
type: 'AdaptiveCard',
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
version: '1.4',
body: [
{
type: 'TextBlock',
text: 'Deployment Complete',
weight: 'Bolder',
size: 'Large',
},
{
type: 'FactSet',
facts: [
{ title: 'Environment', value: 'Production' },
{ title: 'Version', value: 'v2.4.1' },
],
},
],
},
});

Message Card Sections (Legacy)​

For simpler formatting, use the legacy sections format:

curl -X POST https://api.zyphr.dev/v1/teams/send \
-H "X-API-Key: zy_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"connection_id": "conn_abc123",
"text": "Daily Report",
"theme_color": "3498db",
"sections": [
{
"activityTitle": "Email Statistics",
"facts": [
{ "name": "Sent", "value": "1,234" },
{ "name": "Delivered", "value": "1,215 (98.5%)" },
{ "name": "Bounced", "value": "19 (1.5%)" }
]
}
]
}'

Managing Connections​

Validate a Webhook URL​

curl -X POST https://api.zyphr.dev/v1/teams/validate-webhook \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhook_url": "https://outlook.office.com/webhook/..."
}'

List Connections​

curl https://api.zyphr.dev/v1/teams/connections \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

Update a Connection​

curl -X PATCH https://api.zyphr.dev/v1/teams/connections/CONN_ID \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Connection Name"
}'

Send a Test Message​

curl -X POST https://api.zyphr.dev/v1/teams/connections/CONN_ID/test \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

Delete a Connection​

curl -X DELETE https://api.zyphr.dev/v1/teams/connections/CONN_ID \
-H "Authorization: Bearer YOUR_JWT_TOKEN"

Message History​

List Messages​

curl "https://api.zyphr.dev/v1/teams/messages?connection_id=conn_abc123&status=sent&limit=25" \
-H "X-API-Key: zy_live_your_api_key"

Query Parameters​

ParameterTypeDescription
connection_idstringFilter by connection
statusstringFilter by status: queued, sending, sent, failed
limitnumberResults per page (default: 25, max: 100)
offsetnumberPagination offset

Get Message Details​

curl https://api.zyphr.dev/v1/teams/messages/MSG_ID \
-H "X-API-Key: zy_live_your_api_key"

Message Lifecycle​

StatusDescription
queuedMessage accepted and queued for delivery
sendingMessage is being sent to Teams
sentMessage successfully delivered to Teams
failedDelivery failed (check error details)

Error Handling​

ErrorCauseResolution
invalid_webhook_urlWebhook URL format is incorrectVerify the URL is a valid Teams incoming webhook URL
webhook_not_foundWebhook was removed from TeamsCreate a new incoming webhook in Teams
rate_limitedTeams connector rate limit hitZyphr handles retries automatically
invalid_adaptive_cardMalformed Adaptive Card JSONValidate with the Adaptive Cards Designer

API Reference​

MethodEndpointAuthDescription
GET/v1/teams/connectionsJWTList connections
POST/v1/teams/connectionsJWTCreate connection
GET/v1/teams/connections/:idJWTGet connection
PATCH/v1/teams/connections/:idJWTUpdate connection
DELETE/v1/teams/connections/:idJWTDelete connection
POST/v1/teams/connections/:id/testJWTSend test message
POST/v1/teams/validate-webhookJWTValidate webhook URL
POST/v1/teams/sendAPI KeySend message
GET/v1/teams/messagesAPI KeyList messages
GET/v1/teams/messages/:idAPI KeyGet message

Rate Limits​

Microsoft Teams enforces rate limits on incoming webhook connectors.

Limit TypeRateScope
Per connector4 requests/secondPer webhook URL
BurstShort bursts toleratedTeams may queue excess messages
Message size28 KB maxPer message payload

How Zyphr Handles Rate Limits​

  • Automatic retry: When Teams returns 429, Zyphr waits for the Retry-After duration and retries
  • Queue-based delivery: Messages are naturally spaced via the delivery queue
  • No message loss: Rate-limited messages are retried, not dropped

Best Practices​

  • Use Adaptive Cards to consolidate information — one card with a FactSet is better than multiple text messages
  • Keep payloads under 28 KB — Teams rejects larger messages
  • Avoid rapid-fire sends to the same connector — batch updates when possible

Next Steps​