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