← Back to Blog

JSON Webhook Payload Debugging: Finding Broken Escaping, Types, and Missing Fields

Utility · June 1, 2026 · 5 min read

A webhook arrives but your handler rejects it. The JSON might have escaped quotes, wrong types, or missing keys. Debug it with jq, Python, and OpsCheck.

Technical cover image for JSON Webhook Payload Debugging: Finding Broken Escaping, Types, and Missing Fields

The Webhook Arrives but Your Handler Rejects It

A third-party service sends you a webhook. Your handler logs "invalid JSON" and returns 400. You copy the raw payload, paste it into a validator, and it parses fine. The problem is likely subtle: double-encoded quotes, a Unicode BOM character, a number where a string is expected, or a missing field your code assumes is always present.

Use OpsCheck JSON Formatter to pretty-print and validate the payload structure. If the payload includes Base64-encoded sections, decode them with OpsCheck Base64 Encoder/Decoder.

Common Webhook JSON Failures

# 1. Check for BOM (Byte Order Mark) — some systems prepend U+FEFF
hexdump -C webhook-payload.txt | head -1
# If you see "ef bb bf" at the start, the payload has a UTF-8 BOM

# 2. Validate with jq — catches syntax errors immediately
cat webhook-payload.txt | jq .
# "parse error: Invalid numeric literal" — means a number field is malformed

# 3. Check if the Content-Type is actually application/json
curl -D - -s -X POST https://example.com/webhook -d '{"test":1}'
# Some services send text/plain with JSON body

Type Mismatches and Missing Fields

# Use jq to inspect specific fields and their types
jq '.event.id, .event.timestamp, .event.data.status' payload.json

# Check if a field is sometimes a string and sometimes a number
# "amount": "100" vs "amount": 100 — both valid JSON, different types
jq 'type' <<< '"100"'
jq 'type' <<< '100'

# Find missing expected keys
jq 'if .event.user == null then "MISSING user" else "OK" end' payload.json

Real-World Scenario

A payment provider webhook started failing after a seemingly minor API version update. The new payload added a "metadata" object that was null when no metadata existed. The handler did: payload.event.metadata.internal_id — which crashed on null. The fix was optional chaining: payload.event?.metadata?.internal_id. The old payload never had the metadata key at all, so the handler had worked for years. The OpsCheck URL Encoder/Decoder helped verify that callback URLs within the payload were properly encoded and not breaking JSON string boundaries.

# Test your handler with edge case payloads
echo '{"event": {"metadata": null}}' | jq '.event.metadata.internal_id'
# jq: error: Cannot index null — same error your handler crashes on

# Safer approach with jq
echo '{"event": {"metadata": null}}' | jq '.event.metadata.internal_id // "N/A"'

Debugging Checklist

  • Capture the raw HTTP body before any parsing — not the parsed representation
  • Check for BOM, trailing commas, and unicode escape sequences
  • Validate with jq or OpsCheck JSON Formatter
  • Verify all field types match what your code expects
  • Handle null gracefully — any field can be null in a future API version
  • Log the raw body at the handler entry point for post-mortem debugging