The JSON looks right. The endpoint exists. Authentication works. And the server still sends back 415. Before rewriting the payload, inspect the headers describing it.
Two headers, two directions
Content-Type describes the body being sent. In a request, it tells the server how to interpret your payload. Accept describes the response formats the client is willing to receive. Setting Accept: application/json does not turn a request body into JSON.
A 415 points to an unsupported request format; a 406 can indicate that the server cannot provide an acceptable response representation. These are different debugging paths. Servers can also choose a default representation instead of returning 406, so check the actual behavior of your API.
The cURL command that looks more correct than it is
Suppose a local test endpoint accepts JSON. This command sends JSON-looking bytes, but its request media type is wrong for that endpoint:
curl -i http://localhost:3000/widgets \
-H 'Accept: application/json' \
--data '{"name":"demo"}'
With --data, cURL uses application/x-www-form-urlencoded unless you override it. The Accept header above only expresses a response preference. Make both directions explicit:
curl -i http://localhost:3000/widgets \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data '{"name":"demo"}'
On cURL 7.82.0 or later, --json is a convenient alternative. It sets both JSON headers and sends the data, but it does not validate whether the supplied text is valid JSON.
Change one variable at a time
Save the failing request before editing it. First correct only Content-Type. Then inspect the status, response Content-Type, and body together. If the result changes, you have a useful clue about where the request was rejected.
Next, keep the request unchanged and try a different Accept value. A strict JSON-only endpoint might reject Accept: application/xml with 406; another endpoint might return its default JSON representation. Record which behavior your clients actually depend on.
Finally, send malformed JSON while keeping Content-Type: application/json. This separates a supported media type from a payload the parser cannot read. The exact error response depends on your implementation. Do not document a status code just because a framework commonly uses it.
Put the format in the contract
A practical regression checklist has four rows: supported request format, unsupported request format, malformed payload, and unacceptable response preference. For each row, keep the exact headers, body, status, and returned media type. Run this against a disposable local or staging resource, since POST can create data.
In your OpenAPI document, check the requestBody content entries and the content entries for each response separately. A correct schema under the wrong media type still leaves clients guessing.
We build Powerduck for working with OpenAPI, imported cURL requests, and API debugging in one workspace. Importing the failing request gives you a starting point; comparing the actual headers with the documented formats is still the work that resolves this class of bug.
The next time JSON gets rejected, inspect the label on the body before changing the body itself.
References: MDN: Content-Type · MDN: 415 · MDN: 406 · cURL option reference · Powerduck
Top comments (0)