DEV Community

Eagle Peak
Eagle Peak

Posted on

Microsoft Teams API Integration

This is a practical guide for Microsoft Teams Incoming Webhook integration, focusing on JSON string formatting issues that commonly break webhook calls. If you've ever gotten frustrated with escaped quotes and newlines in Teams messages, this guide will save you time.

What This Guide Covers:

  • Setting up Teams Incoming Webhooks
  • Handling special characters, quotes, and newlines in messages
  • Using StringFlux tools to solve JSON formatting headaches
  • Debugging common JSON payload errors

Setting Up Your Teams Webhook

  1. In your Teams channel, click the three dots (⋯) → Manage channel
  2. Go to Apps tab → CreateIncoming Webhook
  3. Give it a name (e.g., "deployment-alerts") → Create
  4. Copy the webhook URL - save this immediately as Teams hides it after a few days

Test with a basic message:

curl -H "Content-Type: application/json" \
     -X POST \
     -d '{"text": "Hello from webhook!"}' \
     https://outlook.office.com/webhook/YOUR_WEBHOOK_ID_HERE
Enter fullscreen mode Exit fullscreen mode

The JSON Formatting Challenge

Here's where most developers run into trouble. Let's say you want to send this message:

Deployment Status: "FAILED"
Error: File not found: "C:\config\app.json"
Next steps:
1. Check file permissions
2. Verify path exists
Enter fullscreen mode Exit fullscreen mode

The Wrong Way (This Will Break):

curl -H "Content-Type: application/json" \
     -X POST \
     -d '{"text": "Deployment Status: "FAILED"
Error: File not found: "C:\config\app.json""}' \
     https://outlook.office.com/webhook/YOUR_WEBHOOK_ID
Enter fullscreen mode Exit fullscreen mode

Result: 400 Bad Request - Invalid JSON

The Right Way (Using StringFlux):

Use the JSON Escape tool to properly format your message:

curl -H "Content-Type: application/json" \
     -X POST \
     -d '{"text": "Deployment Status: \"FAILED\"\nError: File not found: \"C:\\config\\app.json\"\nNext steps:\n1. Check file permissions\n2. Verify path exists"}' \
     https://outlook.office.com/webhook/YOUR_WEBHOOK_ID
Enter fullscreen mode Exit fullscreen mode

Result: Message posts successfully with proper formatting!

Real-World Examples

Server Alert Messages

Raw alert content:

ALERT: Database Connection Failed
Server: "prod-db-01"
Error: "Connection timeout after 30s"
Time: 2025-09-04 14:30:15 UTC
Log path: /var/log/myapp/errors.log
Enter fullscreen mode Exit fullscreen mode

After escaping with StringFlux, the JSON becomes:

{
  "text": "ALERT: Database Connection Failed\nServer: \"prod-db-01\"\nError: \"Connection timeout after 30s\"\nTime: 2025-09-04 14:30:15 UTC\nLog path: /var/log/myapp/errors.log"
}
Enter fullscreen mode Exit fullscreen mode

Code Deployment Notifications

Raw deployment message:

Deployment Complete
Branch: feature/user-auth
Commit: "Fix login validation bug"  
Files changed:
- src/auth/validator.js
- tests/auth.test.js
Enter fullscreen mode Exit fullscreen mode

After proper escaping, this becomes a clean webhook payload that Teams can process without errors.

Programming Language Integration

Python Example

import requests

# After using StringFlux to escape your message:
escaped_message = "Warning: High Memory Usage\nServer: \"prod-web-02\"\nMemory: 89% (7.1GB / 8GB)\nAction: Consider scaling up"

webhook_url = "https://outlook.office.com/webhook/YOUR_WEBHOOK_ID"
payload = {"text": escaped_message}

response = requests.post(webhook_url, json=payload)
print(f"Status: {response.status_code}")
Enter fullscreen mode Exit fullscreen mode

Node.js Example

const axios = require('axios');

// After using StringFlux to escape your message:
const message = "Release v2.1.0 deployed!\nFeatures:\n- New user dashboard\n- \"Dark mode\" toggle\nBreaking changes: None";

const webhookUrl = 'https://outlook.office.com/webhook/YOUR_WEBHOOK_ID';

axios.post(webhookUrl, { text: message })
  .then(response => console.log('Message sent successfully'))
  .catch(error => console.error('Failed to send message:', error.response?.data));
Enter fullscreen mode Exit fullscreen mode

Debugging Common Issues

Problem 1: "Bad Request" Errors

Symptoms: 400 status code, message doesn't appear
Solution: Your JSON is malformed. Use StringFlux JSON escape to properly escape special characters.

Problem 2: Message Appears But Formatting Is Wrong

Symptoms: Message posts but newlines don't work, quotes are missing
Solution: You need \n for newlines and \" for quotes. StringFlux handles this automatically.

Problem 3: Webhook URL Stopped Working

Symptoms: Worked yesterday, 404 error today

Solution: Teams webhook URLs expire. Create a new webhook in your channel settings.

Pro Tips from Production Use

Handle Dynamic Content Safely

# Don't do this - user input can break JSON:
user_feedback = 'App crashed with "null pointer" error'
message = f'{{"text": "User says: {user_feedback}"}}'

# Do this - escape user input with StringFlux first:
escaped_feedback = "App crashed with \"null pointer\" error"  
message = f'{{"text": "User says: {escaped_feedback}"}}'
Enter fullscreen mode Exit fullscreen mode

Debug Failed Webhooks Quickly

When a webhook call fails:

  1. Copy your JSON payload
  2. Use the JSON Unescape tool to see the human-readable version
  3. Check for obvious formatting issues
  4. Re-escape if needed

Preserve Markdown Formatting

Teams processes messages in this order:

  1. JSON parsing (where StringFlux helps)
  2. Markdown rendering (where Teams takes over)

So escape your JSON properly first, then Teams will render your Markdown correctly.

Common Gotchas and Solutions

Gotcha 1: Forgetting to double-escape backslashes

// Wrong:
{"text": "Path: C:\Users\dev"}

// Right (StringFlux handles this):  
{"text": "Path: C:\\Users\\dev"}
Enter fullscreen mode Exit fullscreen mode

Gotcha 2: Newline formatting in Teams

// This renders as one line:
{"text": "Line 1\nLine 2"}

// This creates separate paragraphs:
{"text": "Line 1\n\nLine 2"}  
Enter fullscreen mode Exit fullscreen mode

Troubleshooting Checklist

When your Teams webhook isn't working:

  1. Check webhook URL - Did it expire? Create a new one.
  2. Validate JSON - Use StringFlux JSON unescape to check readability
  3. Test simple message - Try {"text": "test"} first
  4. Check special characters - Use StringFlux JSON escape for complex content
  5. Verify Content-Type - Must be application/json

Top comments (0)