Why curl -d Doesn't Send application/json
You wrote curl -d '{"name":"Ada"}' https://api.example.com/users, the body is obviously JSON, and the API still rejects it. Nothing about the body is wrong — -d silently attaches Content-Type: application/x-www-form-urlencoded unless you tell it otherwise, and that header, not the body, is what your server actually trusts.
// what -d actually sends
curl -d '{"name":"Ada"}' https://api.example.com/usersPOST /users HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 16
{"name":"Ada"}curl -H 'Content-Type: application/json' -d '{"name":"Ada"}' https://api.example.com/users// what your API actually does with it
What happens next depends entirely on the framework, and it's rarely an error message that points anywhere near the real cause:
[ApiController]an unbindable [FromBody] parameter is treated as a validation failure — you get an automatic 400 Bad Request / ProblemDetails response before your action method ever runs, with no mention of Content-Type anywhere in it[ApiController]no automatic validation exists, so the parameter can bind to null silently — your method runs, and the first thing that touches the parameter throws a NullReferenceException that looks unrelated to the actual requestexpress.json()the middleware only parses a body when Content-Type matches application/json; anything else leaves req.body undefined, with no error raised at all// why Postman and fetch don't have this problem
Postman's body editor sets Content-Type for you based on which mode you picked (raw + JSON sets application/json automatically), and fetch/axios do the same when you pass an object instead of a raw string. If you tested an endpoint in Postman first and then hand-wrote the equivalent curl command, this is exactly the gap — Postman was quietly doing something curl never does on its own.
// the cURL Converter reproduces this faithfully, not silently
The cURL Converter defaults generated request code to application/x-www-form-urlencoded under exactly the same condition curl does — no explicit Content-Type header in the command you pasted. It doesn't try to detect that your body "looks like JSON" and quietly fix it, because guessing wrong in the other direction (assuming JSON when a real form submission was intended) would be its own silent bug. If the generated code's Content-Type looks wrong, the fix is the same one-line addition to the original curl command: add -H 'Content-Type: application/json', then reconvert.
// frequently asked questions
Why doesn't curl -d send Content-Type: application/json automatically?
Because -d predates JSON as a web convention — it was built for HTML form submission, and curl still defaults its Content-Type to application/x-www-form-urlencoded regardless of what the body actually contains. curl never inspects the body to guess its format; it sends exactly the header you gave it, or that one default if you gave it none.
What does my API actually receive when the Content-Type is wrong?
The real JSON text, byte for byte, labeled as a different format. Nothing about the body itself is corrupted — the server just never hands it to a JSON parser, because it trusts the Content-Type header to decide which parser to use, and that header says form data.
Why does ASP.NET Core return 400 instead of a JSON parse error?
With [ApiController], a [FromBody] parameter that can't be bound is treated as a validation failure, not a parse failure — the framework never gets far enough to attempt parsing, so it returns its automatic 400 Bad Request / ProblemDetails response before your action method ever runs.
Why did my Express req.body come back undefined instead of an error?
express.json() only parses the body when Content-Type matches application/json (or a type you explicitly configured it to accept). Any other Content-Type means the middleware skips the body entirely and leaves req.body undefined — silently, with no error — rather than rejecting the request.
Does the cURL Converter fix this automatically?
No, and deliberately not — it translates your curl command faithfully, defaulting to application/x-www-form-urlencoded exactly when curl itself would, unless your command already includes an explicit Content-Type header. If the header's missing, the generated code reproduces the same bug rather than silently guessing what you meant.
-d/--datais older than JSON being a common API format — it was built for submitting HTML forms, and curl has never changed its default to match how the flag gets used today. curl doesn't look at your body at all to decide the Content-Type; it just applies the same default every time you use-dwithout an explicit header.