Microsoft Teams Error: 'Bad payload received by generic incoming webhook' — Cause, Fix, and Troubleshooting Guide
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
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
400with the plain bodyBad payload received by generic incoming webhook. - The same URL worked previously with a different (simpler) payload.
- A hand-crafted
curlwith-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-urlencodedfails 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 atextorsummary. Omittingsummary/textyields an empty, invalid card. - Adaptive Card sent without the attachments wrapper. Posting a raw Adaptive Card
contentat the top level fails; it must be wrapped as amessagewith anattachmentsarray. - 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+attachmentsAdaptive 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 emptyin 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.
Related tenant errors
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?
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.