I prefer simplicity and using the first example but I’d be happy to hear other options. Here’s a few examples:
HTTP/1.1 403 POST /endpoint
{ "message": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
Unauthorized access (no json)
HTTP/1.1 403 POST /endpoint
{ "error": "Unauthorized access" }
HTTP/1.1 403 POST /endpoint
{
"code": "UNAUTHORIZED",
"message": "Unauthorized access",
}
HTTP/1.1 200 (🤡) POST /endpoint
{
"error": true,
"message": "Unauthorized access",
}
HTTP/1.1 403 POST /endpoint
{
"status": 403,
"code": "UNAUTHORIZED",
"message": "Unauthorized access",
}
Or your own example.
If any client app is blindly converting body to JSON without checking (at the very least) content type and size, they deserve what they get.
If you want to make it part of your API spec to always return JSON that’s one thing, but don’t do it to make up for poorly written clients. There’s no end of ways in which clients can fail. Sticking to a clear spec is the only way to preserve your sanity.