Skip to content
🎉 Launch sale:50% off everything over $22 — automatically applied at checkout· ends Aug 2Shop the sale →
DevOps AI ToolKit
Newsletter
All guides
AI for Microsoft Teams By James Joyner IV · · 8 min read Last reviewed Jul 2026

Microsoft Teams Error: 'Bad payload received by generic incoming webhook' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix 'Bad payload received by generic incoming webhook' HTTP 400 from Teams incoming webhooks: send valid JSON, correct Content-Type, and proper card wrappers.

  • #microsoft-teams
  • #troubleshooting
  • #errors
  • #incoming-webhook
Free toolkit

Stuck on this Microsoft Teams error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

What this error means

You POST a message to a Microsoft Teams Incoming Webhook (or the newer Workflows “Post to a channel when a webhook request is received” URL) and instead of the message appearing in the channel, the HTTP call comes back with a 400 status and this body:

Bad payload received by generic incoming webhook.

This is a request-validation failure. The Teams webhook endpoint accepted the connection and read the body, but the body did not parse as a message the endpoint knows how to render. The status code is 400 Bad Request — the URL and token are fine, so this is not a 401, 403, or 410. The fix is almost always in the shape or encoding of what you sent, not in the webhook configuration.

What users report

  • POST to the webhook returns HTTP 400 with the plain body Bad payload received by generic incoming webhook.
  • The same URL worked previously with a different (simpler) payload.
  • A hand-crafted curl with -d '{...}' fails, but the Teams “test” from the connector UI succeeds.
  • Adaptive Cards that render fine in the Designer are rejected when posted to the webhook.
  • Sending with a default client that sets Content-Type: application/x-www-form-urlencoded fails every time.
  • An empty request or a trailing/leading byte-order-mark (BOM) triggers the same 400.

Tenant and app configuration causes

  • Invalid JSON. A trailing comma, unescaped quote, or truncated body means the endpoint cannot parse the payload at all.
  • Wrong Content-Type. The endpoint expects application/json. Clients that default to form-encoding or omit the header get rejected.
  • Missing required card fields. A legacy MessageCard needs at least "@type": "MessageCard", "@context", and a text or summary. Omitting summary/text yields an empty, invalid card.
  • Adaptive Card sent without the attachments wrapper. Posting a raw Adaptive Card content at the top level fails; it must be wrapped as a message with an attachments array.
  • Empty body or non-JSON body. A zero-length POST, or a string that is not a JSON object, is rejected outright.
  • Encoding artifacts. A UTF-8 BOM, CRLF noise, or double-encoded JSON (a JSON string containing JSON) breaks parsing before validation.

Confirming tenant configuration

First prove your payload is valid JSON locally. jq empty exits non-zero and prints the parse error if it is not:

jq empty payload.json && echo "valid JSON" || echo "INVALID JSON"

Then POST with curl -i so you can read the exact status line and body the endpoint returns:

curl -i -H "Content-Type: application/json" \
  -d @payload.json \
  "$TEAMS_WEBHOOK_URL"

Look at the first line. HTTP/2 400 plus Bad payload received by generic incoming webhook. confirms this class of error. If you instead see 200/202 with 1 in the body, the post succeeded.

Check for a BOM or hidden bytes if the JSON validates but the post still fails:

head -c 3 payload.json | xxd

A leading ef bb bf is a UTF-8 BOM — strip it before sending.

Resolution

Send a well-formed body with the correct header. For a minimal legacy MessageCard:

cat > payload.json <<'JSON'
{
  "@type": "MessageCard",
  "@context": "http://schema.org/extensions",
  "summary": "Deploy finished",
  "text": "Deploy to prod completed in 4m12s."
}
JSON

curl -i -H "Content-Type: application/json" -d @payload.json "$TEAMS_WEBHOOK_URL"

For an Adaptive Card, wrap the card in a message with an attachments array and the correct contentType. Posting the card content on its own is the single most common cause of this 400:

{
  "type": "message",
  "attachments": [
    {
      "contentType": "application/vnd.microsoft.card.adaptive",
      "content": {
        "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
        "type": "AdaptiveCard",
        "version": "1.4",
        "body": [
          { "type": "TextBlock", "text": "Deploy finished", "weight": "Bolder" }
        ]
      }
    }
  ]
}

Always set the header explicitly rather than relying on client defaults. In a script:

curl -sS -o /dev/null -w "%{http_code}\n" \
  -H "Content-Type: application/json" \
  --data-binary @payload.json \
  "$TEAMS_WEBHOOK_URL"

Use --data-binary (not -d) when reading from a file so curl does not strip newlines and reshape the body.

Avoiding tenant drift

  • Keep the webhook URL out of logs and source. Anyone with the full URL can post to the channel; treat it like a secret and read it from $TEAMS_WEBHOOK_URL.
  • Match the card type to the endpoint. Classic connectors expect MessageCard; Workflows-based webhooks expect the message + attachments Adaptive Card shape. Sending the wrong one 400s.
  • Set Content-Type on every call. Libraries and shells that silently default to form encoding will fail even with perfect JSON.
  • Validate before you send. Run jq empty in CI so a malformed template is caught before it hits the channel.
  • Watch the body size. Very large cards can be rejected for a different reason; keep Adaptive Cards within documented size limits.
  • Do not double-encode. Serializing an already-JSON string produces a quoted string, not an object, and the endpoint rejects it.
Free download · 368-page PDF

Fixed it? Get 500 Microsoft Teams & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.