You look confused?

Yes, I have problems with this HTTP API endpoint. It fails my requests.

Have you looked at the status code?

Yes, It returns a 400 Bad Request but I don’t know what’s wrong.

Ah, yes. That’s one problem with status codes. They can be ambiguous.

Ambiguous, how?

Well, take the 404 Not Found status code; this is quite explicit in its semantics; you tried a request, but the server couldn’t find the resource.

Now, look at the 400 Bad Request response. This can be caused by a variety of errors, such as malformed request syntax or the request being too large.

Another problem is that it can hide other details, such as the developers try to redefine the semantic meaning within the API domain.

Also, some middleware could have tampered with your response. The API might have sent another status code, but due to security settings in some proxy, that status code could have been replaced.

Without further details, neither you or your API client know what’s wrong.

I understand, I am also the developer of the API endpoint. How can I make sure that we provide enough details for both the API Client as well as the investigator?

Take a look at the The Problem Details for HTTP APIs RFC. It is a specification which tries to provide a standardized and common structure for providing error details. Both via high-level error codes, i.e. the status codes, as well as finer-grained machine-readable detail types for the API Client.

It also contains specifications for human-readable values and implies consistency between middleware.

This will relax the need of specific domain knowledge for your consumers, as well as letting you think of other details instead of trying to redefine existing semantic meanings in status codes.

Cool, how does the model look like?

It is a really simple data model and if you need to provide more details, you simply extend it.

Take a look at this table:

Property name Data type Description
type string URI reference for identifying the problem type. Should provide human-readable documentation when dereferenced. If not present; the default value is assumed to be about:blank.
title string A short summary of the problem.
status number The HTTP Status code generated by the generator. Should not be changed via intermediaries.
detail string Explanation of the specifics of the problem.
instance string URI reference for identification of the specific occurrence.

What formats does it support?

The specification provides two well-known data formats; JSON and XML.

To be easily identifiable you also should alter the Content-Type-header, depending on which payload you are returning.

For JSON it should look like this:

Content-Type: application/problem+json

And for XML?

Like this:

Content-Type: application/problem+xml

Can you provide an example?

Gladly!

Imagine you are trying to transfer funds from one account to another, but the transferring account doesn’t have sufficient funds. This could result in a 403 Forbidden status code since it seems more appropriate.

If we just return the status code the consumer must start a guessing game. Maybe the API client might not have sufficient permissions?

If we provide with additional details such as this, it might clear things up:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "http://domain.tld/problems/out-of-funds",
  "title": "The user dont have enough funds.",
  "status": 403,
  "detail": "The current account balance is 40 but you tried to transfer 100."
}

Great! but I know we need to investigate an error like this because our angry clients will call our customer center…

Not a problem. That’s what the instance-property is for.

Let us say the transferring account number is 12345, we could simply add this into the instance-value so that we can correlate this later.

Now we could reference it like “that time that Jane had insufficient funds”.

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "http://domain.tld/problems/out-of-funds",
  "title": "The user dont have enough funds",
  "status": 403,
  "detail": "The current account balance is 40 but you tried to transfer 100."
  "instance": "accounts/12345/out-of-funds"
}

And if we need to replay the action? We need to know the receiving account as well.

We should then extend the model! Let us assume that the receiving account has an ID of 67890, and the transferring account still has 12345 as an ID.

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "http://domain.tld/problems/out-of-funds",
  "title": "The user dont have enough funds",
  "status": 403,
  "detail": "The current account balance is 40 but you tried to transfer 100."
  "instance": "accounts/12345/out-of-funds"
  "accounts": ["/account/12345", "/account/67890"]
}

We could store this in an array, or a complex object, that is entirely up to you.


Wow, I am starting to get this now! But what about details with multiple errors, like validation?

Simply extend the model. We could store this in an array with each error as specific as you would like.

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
  "type": "http://domain.tld/problems/validation-errors",
  "title": "The transfer validation did not succeed",
  "validation-errors": [
	{
	  "name": "account_from",
	  "reason": "You need to provide a transferring account number"
	},
	{
	  "name": "account_to",
	  "reason": "You need to provide a receiving account number"
  	}
  ]
}

Given that the validation errors are similar, otherwise, you could use the 207 (Multi status)-status code.


Do I need to model this myself?

It depends on your platform of choice.

If you are using ASP.NET then you can use and derive the class Microsoft.AspNetCore.Mvc.ProblemDetails. For validation specific, the framework already includes the Microsoft.AspNetCore.Mvc.ValidationProblemDetails class.

It is a really lightweight model, with just a couple of properties, so on other platforms and with other frameworks, it shouldn’t be a problem to make your own models.


Awesome, where can I find out more?

Read the RFC and look at examples. The spec is really not that long and you can find it here: https://tools.ietf.org/html/rfc7807

As mentioned, Microsoft ships ASP.NET Core with the ProblemDetails-class.

If you want to look into examples within the financial industry; I found this repository (Java) by Danish Bankdata.

Notes about the examples

The examples in this post are light alternations of the RFC.