REST Guidelines

2022-03-31

1. Introduction

This style guide for RESTful services is a collaborative effort by several Belgian government institutions, originally under the G-Cloud umbrella, before moving to Belgif, the Belgian Interoperability Framework. Its goal is to improve compatibility between RESTful services offered by government institutions or any other organization adopting these guidelines.

This guide is a living document, updated when new interoperability issues arise or when REST-related standards evolve. All changes are reviewed by the REST design workgroup, in which the Belgian government organizations are represented. If you find errors or omissions, or want to contribute, you can open an issue or pull request on GitHub.

The main benefit for choosing RESTful services is to increase flexibility and to offer web service support to client platforms not able to communicate using SOAP web services.

  • REST is the defacto standard to communicate with web services from JavaScript and native mobile applications.

  • While SOAP is strictly linked to XML and needs complex standards (MTOM, Soap-with-Attachments) to work with other media formats, RESTful services can support this natively.

  • WS-* specifications added to SOAP are often overly complex and redundant to the possibilities over the underlying communication protocol (e.g. HTTP).

  • REST has become the industry standard for developing APIs on the web (Google, Facebook, Amazon, Twitter, etc).

Topics not covered in this guide:

  • Securing REST APIs. Guidelines are under development by the REST Security Working Group, based on the OpenID Connect and OAuth 2.0 standards.

  • Semantics of common business data (e.g. social security number, address, …​). These are managed by the REST Functional Working Group in the belgif fedvoc repository.

  • OpenAPI specifications for common data types can be found in the openapi-* GitHub repositories.

For brevity most URIs in examples are shortened, but in practice URIs should be in absolute notation.

License

This work is licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

2. Changelog

3. REST API

3.1. Uniform interface

The REST uniform interface embraces all aspects of the HyperText Transfer Protocol, version 1.1 (HTTP/1.1) including its request methods, response codes, and message headers.​

The REST uniform interface is based on three fundamental elements:

  1. Resources – How can we express where the data is being transferred to or from?

  2. HTTP Methods – What are the protocol mechanisms used to transfer the data?

  3. Media Types – What type of data is being transferred?

3.2. API

Rule 1: API

An API bundles a set of resources which are functionally related and form an autonomous unit of business logic.

Compare an API to the concept of Services in service-orientation or the concept of Bounded Contexts in Domain-Driven Development.

Rule 2: API format

URI = https://host/pathPrefix/apiName/vmajorVersion/resources

example: https://services.socialsecurity.be/REST/employerProfile/v1/profiles

https://

Services are at least secured on transport level using a one-way TLS connection. The implicit port is 443.

host

Hostname is determined by the environment where the service is deployed

pathPrefix

Optional path prefix to discriminate between REST APIs and other resources on the same host. (example: /REST)

apiName

Name of the API that groups a functional consistent set of resources. The API name consists of one or multiple path segments written in lowerCamelCase (example: /referenceData/geography).

majorVersion

Major version numbers are integers and start at 1. See API versioning.

resources

All path segments identifying the actual resources.

3.3. Richardson Maturity Model

The Richardson Maturity Model (developed by Leonard Richardson) breaks down the principal elements of a RESTful interface into three steps. In order to be compliant to the uniform interface as described by Roy Fielding, all three levels must be fulfilled.

  1. Resources — Level 1 tackles the question of handling complexity by using divide and conquer, breaking a large service endpoint down into multiple resources, each represented by a unique URI.

  2. HTTP Methods — Level 2 introduces a standard set of verbs so that we handle similar situations in the same way, removing unnecessary variation.

  3. Hypermedia controls — Level 3 introduces discoverability, providing a way of making a protocol more self-documenting.

Rule 3: Maturity level

All REST services MUST at least respect level 2 and desirably achieve level 3.

References

4. Resources

A REST APIs exposes a set of resources. Resources model the data and functionality of a system. A resource can be data, processing logic, files, or anything else that a service may have access to.

4.1. Resource URI

Any resource is uniquely identified by a Uniform Resource Identifier or URI (RFC 3986​).

URI = scheme "://" authority "/" path [ "?" query ] [ "#" fragment ]​
​example: https://api.socialsecurity.be/employers/406798006​?lang=nl

URI must be intuitively structured.

Rule 4: URI notation

The URI notation MUST use lowerCamelCase to enhance readability and to separate compound names. As lowerCamelCase is used for JSON property names as well, the casing is consistent throughout the API. Trailing slashes MUST NOT be used.

Example 1. URI notation

GOOD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats/331
BAD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/Social_Secretariats/331
BAD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/social-secretariats/331
BAD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats/331/

Rule 5: URI extensions

The URI SHOULD NOT contain a file extension.

A notable exception to this rule is the swagger/OpenAPI file (see doc resource).

Instead, look at how to express Media Types using HTTP headers.

Example 2. URI extensions

GOOD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats​/331
​BAD: http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats​/331.json

Rule 6: Query parameters

The query part of a URI may be used to filter the resource output.

The query component of a URI contains a set of parameters to be interpreted as a variation or derivative of the resource. The query component can provide clients with additional interaction capabilities such as ad hoc searching and filtering.

Example 3. Query parameters

http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats​?q=sdworx
Filter the resource collection using a search string.

http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats/331​?select=(name,address)
Filter the resource properties to the ones specified in the select query parameter.

http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/socialSecretariats/331​?lang=nl
Only return translatable properties in dutch.

Rule 7: Multiple values for the same query parameter

When a single query parameter can have multiple values, the parameter SHOULD be repeated for each value.

Example 4. Multi-valued parameter
OpenAPI 2.0 definition
parameters:
- name: embed
  in: query
  collectionFormat: multi
  type: string
  enum:
  - employees
  - mainAddress
OpenAPI 3.0 definition
parameters:
- name: embed
  in: query
  style: form
  explode: true
  schema:
    type: array
    items:
      type: string
      enum:
      - employees
      - mainAddress

4.2. Resource Archetypes

There are three different resource archetypes:

4.3. Document

A document resource is a singular concept that is referring to a business entity or object instance. A document’s state representation typically includes both fields with values and links to other related resources.

Example 5. An employer document resource

Child resources

A document may have child resources that represent its specific subordinate concepts. Most document resources are contained in a parent collection resource and define a variable path segment with identity key.

collection

When defining child resources, stick to concepts within the same API. An employer resource could contain child concepts as employees, debts, taxes, mandates, declarations, risks, but putting all these different concepts below a single document resource becomes unmanageable. In that case prefer associations and create links to other APIs from the employer resource.

4.3.1. Identifier

Rule 8: Choice of identifier

The identity key of a document resource is preferably a natural business identifier uniquely identifying the business resource like an ISBN number or SSIN. If such key does not exist, a surrogate or technical key can be created.

New types of identifiers should be designed carefully. Once an identifier has been introduced, it may get widespread usage in various systems even beyond the scope for which it was initially designed, making it very hard to change its structure later on.

Designing identifiers in a URI structure, as specified in the ICEG URI standard, is useful as it makes the identifier context-independent and more self-descriptive. A REST API may choose to use a shorter API-local form of a URI identifier because of practical considerations.

When designing an identifier, various requirements may be of importance specific to the use case:

  • the governance and lifecycle of identifiers and the entities they represent

  • easy to memorize (e.g. textual identifier like problem types)

  • input by user (e.g. web form, over phone/mail)

    • easy to type (ignore special separator chars, difference between lower/capital case), limited length

    • validation of typing errors, e.g. by checksum, fixed length, …​

    • hint on format to recognize purpose of identifier based on its value

  • printable (restricted length)

  • open to evolve structure for new use cases

  • ability to generate identifiers collision-free at multiple independent sources, e.g. by adding a source-specific prefix, using UUIDs, …​

  • stable across different deployment environments (e.g. problem type codes)

  • hide any business information (e.g. no sequential number that indicates number of resources created)

  • not easy to guess a valid identifier, especially for unsecured resources (e.g. no sequentially generated identifier)

  • easy to represent in URL parameter without escaping

  • sortable (for technical reasons e.g. pagination)

Rule 9: Designing new identifiers

For new identifiers, a string based format SHOULD be used: textual lowerCamelCase string codes, UUID, URI or other custom formats. Take into account the requirements that follow from the ways the identifier will be used.

Each identifier MUST be represented by only one single string value, so that a string equality check can be used to test if two identifiers are identitical. This means that capitalization, whitespace or leading zeroes are significant.

In the OpenAPI data type for the identifier, a regular expression may be specified if helpful for input validation or as hint of the structure (e.g. to avoid whitespace or wrong capitalization), but shouldn’t be too restrictive in order to be able to evolve the format.

No business meaning SHOULD be attributed to parts of the identifier. This should be captured in separate data fields. Parts with technical meaning like a checksum are allowed.

Parts of an identifier may carry some business meaning for easier readability, like the problem type identifiers in this guide, but no application logic should parse and interpret these parts.
Don’t use database-generated keys as identity keys in your public API to avoid tight coupling between the database schema and API. Having the key independent of all other columns insulates the database relationships from changes in data values or database design (agility) and guarantees uniqueness.
Example 6. Identifiers

The table below lists some examples of identifiers, though it does not list all possibilities or considerations when designing a new identifier.

identifier structure example OpenAPI type considerations

UUID

"d9e35127-e9b1-4201-a211-2b52e52508df"

Type defined in common-v1.yaml

Uuid:
  description: Universally Unique Identifier, as standardized in RFC 4122 and ISO/IEC 9834-8
  type: string
  pattern: '^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$'

long identifier, not easy to memorize or input by user, easy to generate, resistant to brute-force guessing

URI (URN)

"urn:problem-type:belgif:resourceNotFound"

type: string
format: uri
pattern: "^urn:problem-type:.+$" # further restrictions may be possible

can be human readable, long, not easy to input by user

URI (http)

"https://www.waterwegen.be/id/rivier/schelde"

type: string
format: uri

can be human readable, long, not easy to input by user, requires character escaping when used as URL parameter can be generated collision-free by multiple sources (different domain name)

custom format

"ab12347895"

type: string
pattern: "^[a-z0-9]{1-20}$"

short, easy to encode

A code is a special type of identifier:

  • it has an exhaustive list of possible values that doesn’t change frequently over time

  • each value identifies a concept (examples: a country, a gender, …​).

Rule 10: Designing new codes

New code types SHOULD be represented as string values in lowerCamelCase.

Depending on context, the OpenAPI data type may enumerate the list of allowed values (see Enum values rule).

Example 7. Code

GET /refData/pensionTypes/{id} with id of type PensionType

As string with enumeration:

PensionType:
  type: string
  enum:
  - retirementPension
  - survivalPension
  - guaranteedIncomeElderly

As string with regular expression:

PensionType:
  type: string
  pattern: "^[A-Za-z0-9]+$"
  example: "retirementPension"
Rule 11: Representating existing numerical identifiers

When defining the type for a property representing an existing numerical code or identifier:

  • Identifiers that are commonly represented (e.g. when displayed or inputted by a user) with leading zeros present SHOULD be represented using a string type. A regular expression SHOULD be specified in the OpenAPI data type to avoid erronous values (i.e. without leading zeros).

  • Otherwise, use an integer based type. It is RECOMMENDED to further restrict the format of the type (e.g. format: int32 and using minimum/maximum).

For new identifiers, it is not recommended to use a number type however as stated in Designing new identifiers

Example 8. Representing existing numerical identifiers

An employer ID may be of variable length. Leading zeroes are ignored and most of the time not displayed.

EmployerId:
  description: Definitive or provisional NSSO number, assigned to each registered employer or local or provincial administration.
  type: integer
  minimum: 0
  maximum: 5999999999
  example: 21197

If SSIN has a zero as first digit, it is always displayed.

Ssin:
  description: Social Security Identification Number issued by the National Register or CBSS
  type: string
  pattern: '^\d{11}$'

Country NIS code is a three digit code, the first digit cannot be a zero.

CountryNisCode:
  description: NIS code representing a country as defined by statbel.fgov.be
  type: integer
  minimum: 100
  maximum: 999
  example: 150 # represents Belgium
Rule 12: Identifier name

The identifier should be named id unless there already exists another standardized name for the business identifier.

In a JSON representation, it SHOULD NOT be prefixed unless the identifier is not of the resource on which the operation applies and there’s ambiguity.

If the identifier is used as path parameter, it should be prefixed by the resource type.

Example 9. Identifier name
GET /stores/{storeId}/orders/{orderId} (1)

{
  "id": 123,  (2)
  "clientId": 456, (3)
  "store": {
     "id": 789, (4)
     "href": "/stores/789"
  }
}
1 path parameter: prefixed with resource type
2 use id as JSON property name for the resource being consulted
3 identifier doesn’t refer to the consulted resource. It would be ambiguous with the order’s id.
4 Clearly refers to the store’s id, so no prefix required.

enterpriseNumber is a standard business name for the CBE enterprise identifier, so it should be used instead of id:

GET /enterprises/{enterpriseNumber}
{
   "enterpriseNumber":  "0202239951"
}

4.3.2. Consult

Example 10. Consulting a document

GET

/employers/{employerId}

Consult a single employer

Parameters

employerId

path-param

NSSO number uniquely identifying the employer.

Response

body

The response properties and links to other resources.

{
  "self": "/employers/93017373",
  "name": "Belgacom",
  "employerId": 93017373,
  "company": {
    "enterpriseNumber": "0202239951",
    "href": "/companies/202239951"
  }
}

Most used response codes ​​

200

OK

Default success code if the document exists.

400

Bad request

The dynamic path segment containing the identity key has a wrong data format:

{
  "type": "urn:problem-type:belgif:badRequest",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/badRequest.html",
  "status": 400,
  "title": "Bad Request",
  "instance": "urn:uuid:d9e35127-e9b1-4201-a211-2b52e52508df",
  "detail": "The input message is incorrect",
  "issues": [
    {
      "type": "urn:problem-type:belgif:input-validation:schemaViolation",
      "in": "path",
      "name": "employerId",
      "value": "abc",
      "detail": "This value should be numeric"
    }
  ]
}

404

Not Found

The document resource does not exist.

204 No content should not be used with GET.
Rule 13: Retrieve partial resource representation

The select query parameter is reserved to return a resource representation with only the specified properties.

The value of this parameter SHOULD follow this BNF grammar:

<selects>            ::= [ <negation> ] <selects_struct>
<selects_struct>     ::= "(" <select_items> ")"
<select_items>       ::= <select> [ "," <select_items> ]
<select>             ::= <select_name> | <selects_substruct>
<selects_substruct>  ::= <select_name> <selects_struct>
<select_name>        ::= <dash_letter_digit> [ <select_name> ]
<dash_letter_digit> ::= <dash> | <letter> | <digit>
<dash>              ::= "-" | "_"
<letter>            ::= "A" | ... | "Z" | "a" | ... | "z"
<digit>             ::= "0" | ... | "9"
<negation>          ::= "!"
Example 11. Select query parameter
GET /employers/93017373?select=(name)
{
  "self": "/employers/93017373?select=(name)",
  "name": "Proximus"
}

Note that parentheses around the value of the select parameter are required, even when selecting a single property.

This notation can also be used for nested properties:

GET /employers/93017373?select=(name,address(street(name,code)))
{
  "self": "/employers/93017373",
  "name": "Proximus",
  "address": {
    "street": {
      "name": "Koning Albert II laan",
      "code": 2177
    }
  }
}

4.3.3. Update

Updating a resource may be done in one of several ways. One and only one of following patterns should be chosen per resource, unless forced by a backwards compatible change.

In order of preference:

  1. use PUT with complete objects to update a resource as long as feasible (i.e. do not use PATCH at all).

    This option is preferred when clients are likely to always take into account the entire resource representation. If a client ignores some of a resource’s properties returned by a consultation, they are likely to be omitted from the PUT request and thus lost. This scenario may occur when new properties were added during the API lifecycle. In this case, use of PUT isn’t advised.

  2. Use PATCH with partial objects to only update parts of a resource, whenever possible, using the JSON Merge Patch standard.

    JSON Merge Patch is limited however, e.g. it doesn’t allow for an update of a single element of an array. If this proves to be an issue, this might however indicate that the array elements might be beter modeled as seperate subresources.

  3. use POST on a child resource instead of PATCH if the request does not modify the resource in a way defined by the semantics of the media type. See Controller for more information.

Use of the JSON Patch standard, an alternative to JSON Merge Patch, is not recommended, as it proves to be difficult to implement.

Full update

Use PUT when you like to do a complete update of a document resource. All values are replaced by the values submitted by the client. Absent optional values in the request are set to their default value if one is specified in the OpenAPI specification.

Example 12. PUT on a document resource
PUT http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/employers/93017373 HTTP/1.1

{
    "employerId": 93017373,
    "name": "Belgacom"
}

​​​​​​​​​PUT

/employers/{employerId}

Replace the entire employer resource with the client data. This implies a full update of the resource. Via PUT the client submits new values for all the data.

Request

body

Full representation of the resource to persist.

​​​Parameters

employerId

path-param

employer ID of NSSO uniquely identifying the employer.

Response

body

either empty or resource after update

{
    "employerId": 93017373,
    "name": "Belgacom"
}

Most used response codes ​​

200

OK

The update is successful and updated resource is returned.

​​

204

No Content

The update is successful but updated resource is not returned.

400

Bad request

The input data is not valid according the data schema.

404

Not Found

The resource does not exist and thus cannot be updated. ​

409

Conflict

The client data is in conflict with the data on the server e.g. optimistic locking issues. ​

Partial update

Use PATCH when you like to do a partial update of a document resource.

The PATCH message MUST be conform to the JSON Merge Patch (RFC 7386) specification:

  • JSON properties in the request overwrite the ones in the previous resource state

  • properties with value null in the request are removed from the resource

  • properties not present in the request are preserved

APIs should support both the MIME type of JSON merge patch application/merge-patch+json as the generic application/json JSON mime type. As JSON Merge Patch requests can not be fully specified as an OpenAPI data type, a MergePatch marker type should be used, defined in common-v1.yaml.

Example 13. JSON merge patch

PATCH

/employers/{employerId}

Performs a partial update of an existing employer.

Request

body

JSON Merge Patch

{
  "bankrupt": false,
  "bankruptDate": null
}

Parameters

employerId

path-param

employer ID of NSSO uniquely identifying the employer.

Response

body

empty or the complete resource after applying PATCH

{
    "employerId": 93017373,
    "name": "Belgacom",
    "bankrupt": false
}

Most used response codes

200

OK

Success code with resource after applying PATCH returned.

204

No Content

Success code without returning the resource.

400

Bad request

The input data is not valid according the data schema.

404

Not Found

The resource does not exist and thus cannot be updated.

409

Conflict

The client data is in conflict with the data on the server e.g. optimistic locking issues.

4.3.4. Remove

Use DELETE when you like to delete a document resource.

Example 14. Deleting a document resource

DELETE

/employers/{employerId}

Deletes an employer.

Parameters

employerId

path-param

employer ID of NSSO uniquely identifying the employer.

Response

body

empty or a message indicating success

204 No Content

or

200 OK
{
 "message": "Employer is deleted."
}

Most used response codes

200

OK

Success code with the deleted resource returned.

204

No Content

Success code with the deleted resource not returned.

400

Bad request

Generic error on client side. For example, the syntax of the request is invalid.

404

Not Found

The resource does not exist and thus cannot be deleted.

409

Conflict

A constraint on the resource is violated. The resource could not be deleted because the request is in conflict with the current state of the resource.

For example, a REST API may return this response code when a client tries to DELETE a non-empty store resource.

4.3.5. Long running tasks

Some operations need to be performed asynchronously, as they take too long to complete.

Rule 14: Long running tasks

Long running tasks MUST be represented as a resource. The task resource is created using a POST action returning a 202 Accepted response containing the URL of the task in the Location HTTP header. It can then be fetched by the client to get the processing status of the task.

When GETting the task resource, the response can be:

  • Still processing: status 200 OK and a representation of the task’s current status

  • Success: status 303 See Other with the Location header containing the URL of the task’s output.

  • Failure: status 200 OK with a representation of the task’s status, including the reason of the failure

Variations on the above may be required, e.g. if the task has no output, the response on success may be 200 OK without a Location header. The schema common-v1.yaml defines the representation of a task’s status.

Example 15. Long running task

Submitting the task

POST /images/tasks

HTTP/1.1 202 Accepted
Content-Type: application/json;charset=UTF-8
Location: http://www.example.org/images/tasks/1
Date: Sun, 13 Sep 2018 01:49:27 GMT
{
  "self": "/images/tasks",
  "status": {
    "state": "processing",
    "pollAfter": "2018-09-13T01:59:27Z"
  }
}

The response 202 Accepted status indicates that the server accepted the request for processing. pollAfter hints when to check for an updated status at a later time.

Getting the processing status

GET /images/tasks/1

When the server is still processing the task

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
  "self": "/images/tasks/1",
  "status": {
    "state": "processing",
    "pollAfter": "2018-09-13T02:09:27Z"
  }
}

When processing has completed

HTTP/1.1 303 See Other
Location: http://www.example.org/images/1
Content-Type: application/json;charset=UTF-8
{
  "self": "/images/tasks/1",
  "status": {
    "state": "done",
    "completed":"2018-09-13T02:10:00Z",
  }
}

The Location header refers to the result of the task.

In case of failure during processing

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
  "self": "/images/tasks/1",
  "status": {
    "state": "failed",
    "completed":"2018-09-13T02:10:00Z",
    "problem": {
      "instance": "urn:uuid:d9e35127-e9b1-4201-a211-2b52e52508df",
      "title": "Bad Request",
      "status": 400,
      "type": "urn:problem-type:example:invalidImageFormat",
      "href": "https://example.org/example/v1/refData/problemTypes/urn:problem-type:example:invalidImageFormat",
      "detail": "Invalid image format"
    }
  }
}

Note that the status code is 200 OK as the retrieval of the task’s status succeeded. The cause of failure is represented using an embedded Problem object, as defined in Error handling.

4.4. Collection

A collection resource is a server-managed list of document resources.

Rule 15: Collection name

A plural noun SHOULD be used for collection names, for example 'employers' or 'people'.

4.4.1. Consult

Rule 16: Representation of a collection

The representation of a collection MUST contain a list of links to child resources:

  • A query on a collection MUST contain an items property with an array of objects, each one representing an item in the collection.

  • In these objects, a href property MUST be present with a link to the resource.

  • The unique business identifier SHOULD be present for each item.

  • Each item object MAY be extended with some key business properties, needed for display in a master view.

  • In case the collection is empty, the items property MUST have an empty array as value.

  • The title attribute MAY be used to provide a human-readable description for an item, usable as display text for the link.

  • total is the reserved word for all the items in the collection result.

A collection resource SHOULD always return a JSON object as top-level data structure to support extensibility. Do not return a JSON array, because the moment you like to add paging, hypermedia links, etc, your API will break.
Table 1. Most used response codes

200

OK

Default response code, also when the collection is empty.

400

Bad Request

Generic error on client side. For example, the syntax of the request is invalid.

404

Not found

The URI provided cannot be mapped to a resource.

204 No content should not be used with GET.
Table 2. Query parameters

sort

Multi-value query param with list of properties to sort on. Direction is ascending by default. To indicate descending, prefix property with -.

?sort=age&sort=-name

Example 17. Consulting a collection
Response
{
 "self": "http://rest-reference.test.paas.socialsecurity.be/REST/demo/v1/employers",
 "items":[
   {
     "href":"/employers/93017373",
     "title":"Belgacom",
     "employerId": 93017373,
     "enterpriseNumber": "0202239951"
   },
   {
     "href":"/employers/20620259",
     "title":"Partena VZW",
     "employerId": 20620259,
     "enterpriseNumber": "0409536968"
   }
 ],
 "total":2
}
JSON data types
EmployersResponse:
  allOf:
  - $ref: common/v1/common-v1.yaml#/definitions/SelfLink
  - type: object
    properties:
      items:
        type: array
        items:
          $ref: "#/definitions/EmployerLink"
      total:
        type: integer
EmployerLink:
  allOf:
  - $ref: common/v1/common-v1.yaml#/definitions/HttpLink
  - type: object
    properties:
      employerId:
        $ref: "./employer/identifier/v1/employer-identifier-v1beta.yaml#/definitions/EmployerId"
      enterpriseNumber:
        $ref: "./organization/identifier/v1/organization-identifier-v1.yaml#/definitions/EnterpriseNumber"
Rule 17: Getting full representations of collection items

When the collection items contain few data, you may want to retrieve them in full when getting the collection. In this case, the full representations MUST be included in an 'embedded' property as described in Embedding resources.

Example 18. Consulting a collection with embedded items
Response
{
 "self": "/appendices/employerclasses?embed=items",
 "items": [
  {
   "value": "0",
   "href": "/appendices/employerclasses/0"
  }, {
   "value": "2",
   "href": "/appendices/employerclasses/2"
  }
 ],
 "total":2,
 "embedded": {
   "/appendices/employerclasses/2": {
     "self": "/appendices/employerclasses/2",
     "value": "2",
     "description": {
       "nl": "Bijzondere categorie voor werkgevers die voor hun arbeiders een speciale bijdrage verschuldigd zijn.",
       "fr": "Catégorie particulière pour les employeurs redevables pour les ouvriers d’une cotisation spéciale."
      }
   },
   "/appendices/employerclasses/0": {
     "self": "/appendices/employerclasses/0",
     "value": "0",
     "description": {
      "nl": "Algemene categorie voor werkgevers van commerciële of niet-commerciële aard.",
      "fr": "Catégorie générale pour les employeurs, de type commercial ou non-commercial."
     }
   }
 }
}
JSON data types
AppendixCodesResponse:
  description: A collection of appendix codes
  type: object
  properties:
    items:
      type: array
      items:
        $ref: '#/definitions/AppendixCodeLink'
    total:
      type: integer
    embedded:
      type: object
      additionalProperties:
        $ref: 'appendixCode.yaml#/definitions/AppendixCode'
AppendixCodeLink:
  allOf:
  - $ref: 'common/v1/common-v1.yaml#/definitions/HttpLink'
  - type: object
    properties:
      value:
        $ref: 'appendixCode.yaml#/definitions/AppendixCodeValue'

4.4.2. Filtering

Rule 18: Filtering items in a collection

A collection may support query parameters to filter its items:

  • a query parameter with the name of a document property, filters items on the specified value

  • the query parameter q is reserved for a full text search on all the document’s content

  • other filter parameters may be supported, e.g. to look up items within a search period

Unless the API documentation explicitly states otherwise, returned collection items:

  • should satisfy ALL filter query parameters (AND-logic)

  • have to match ANY of the values of a multi-valued filter query parameter (OR-logic).

For example, the query GET /cars?doors=5&color=black&color=blue should be interpreted by default as: "search for all cars that have 5 doors AND are of color blue OR black".

GET

/employers

get all the employers documents in the collection

​​​Parameters

name

query-param

Filter only employers that have a specific name.

Response

body

{
  "self": "/companies?name=belg",
        "items": [{
                "href": "/companies/202239951",
                "title": "Belgacom"
        }, {
                "href": "/companies/448826918",
                "title": "Carrefour Belgium SA"
        }],
        "total": 2
}

Most used response codes ​​

200

OK

Default response code, also when the filtered collection is empty.

400

Bad Request

Generic error on client side. For example, the syntax of the request is invalid.

404

Not found

The URI provided cannot be mapped to a resource. ​

204 No content should not be used with GET.

4.4.3. Pagination

Rule 19: Paging over a large collection​

Collection with too many items MUST support pagination. There are two pagination techniques:

  • offset-based pagination: numeric offset identifies a page

  • cursor-based (aka key-based or luke index): a unique key element identifies a page

Cursor-based pagination has some advantages, especially for high volumes. Take into account the considerations listed in the Zalando API guidelines before choosing a pagination technique.

Table 3. Reserved JSON properties:

next

MANDATORY (except for the last page)

hyperlink to the next page

prev

OPTIONAL

hyperlink to the previous page

pageSize

RECOMMENDED

Maximum number of items per page. For the last page, its value should be independent of the number of actually returned items.

page

MANDATORY (offset-based); N/A (cursor-based)

index of the current page of items, should be 1-based (the default and first page is 1)

first

OPTIONAL

hyperlink to the first page

last

OPTIONAL

hyperlink to the last page

Note that the total collection property, if used, MUST always present the total number of items across all pages. The names of the properties with hyperlink values and the items property are derived from the IANA registered link relations.

Table 4. Reserved query parameters:

pageSize

OPTIONAL

maximum number of items per page desired by client; must have a default value if absent.

page

MANDATORY with default value 1 (offset-based); N/A (cursor-based)

the index of page to be retrieved

Example 19. Offset-based pagination
{
  "self": "/companies?page=2&pageSize=2",
  "items": [
    {
      "href": "/companies/202239951",
      "title": "Belgacom"
    },
    {
      "href": "/companies/212165526",
      "title": "CPAS de Silly"
    }
  ],
  "pageSize": 2,
  "total": 7,
  "first": "/companies?pageSize=2",
  "last": "/companies?page=4&pageSize=2",
  "prev": "/companies?page=1&pageSize=2",
  "next": "/companies?page=3&pageSize=2"
}
Example 20. Cursor-based pagination
{
  "self": "/companies?afterCompany=0244640631&pageSize=2",
  "items": [
    {
      "href": "/companies/202239951",
      "title": "Belgacom"
    },
    {
      "href": "/companies/212165526",
      "title": "CPAS de Silly"
    }
  ],
  "pageSize": 2,
  "total": 7,
  "first": "/companies?pageSize=2",
  "next": "/companies?afterCompany=0212165526&pageSize=2"
}

4.4.4. Create a new resource​

The collection resource can be used to create new document resources from the POST request body. Absent optional values are set to their default value if one is specified in the OpenAPI specification.

​​​​​​​​​POST

/employers

create a new employer in the collection

​​​Request

body

​The data of the resource to create.

{
  "name": "Belgacom",
  "employerId": 93017373,
  "company": {
    "enterpriseNumber": 202239951
  }
}

Response headers

Location

http-header

The URI of the newly created resource e.g. /employers/93017373

Response

body

The response contains an empty body.

Most used response codes ​​

201

Created

Default response code if the resource is created.

409

Conflict

The resource could not be created because the request is in conflict with the current state of the resource. E.g. the resource already exists (duplicate key violation).

303

See Other

The resource already exists. May be returned instead of 409 Conflict if it is considered a normal use case to perform the operation for an already existing resource. The Location header refers to the resource.

200 OK should not be used with POST for creating resources.
POST /employers HTTP/1.1

HTTP/1.1 201 Created
Location: /employers/93017373
Content-Length: 0
Date: Wed, 06 Jan 2016 15:37:16 GMT

4.4.5. Remove

A selection of items can be removed from a collection using the DELETE method. In fact, the collection itself cannot be removed, but it can be emptied if all its items are removed. By using query parameters, the items to be removed can be filtered. In order to remove a single specific item from a collection, use DELETE on the document resource.

DELETE

/employers

Delete all the employers documents in the collection.

Parameters

name

query-param

Remove only employers that have a specific name.

Response

body

Empty or a message incidating success.

Most used response codes

200

OK

The items are successfully removed from the collection and returned.

204

No content

The items are successfully removed from the collection but no additional content is included in the response body.

400

Bad Request

Generic error on client side. For example, the syntax of the request is invalid.

404

Not found

The collection resource does not exist and thus cannot be deleted.

4.5. Controller

A controller resource models a procedural concept. Controller resources are like executable functions, with parameters and return values; inputs and outputs.

A REST API can rely on controller resources to perform application-specific actions that cannot be logically mapped to one of the standard methods (standard CRUD-operations).

Rule 20: Controller resource

A controller resource name is a verb instead of a noun.

The HTTP method on a controller SHOULD be:

  • POST for actions with side effects (state change) or actions without side effects but requiring a request body

  • GET for idempotent actions without side effects

Example 21. Controller examples

Withdrawal from an account:

POST /accounts/123/withdraw

Sending a notification to an employer:

POST /employers/93017373/sendNotification

Converting money from one currency to another (using GET because of no side effects):

GET /convertMoney?from=EUR&amount=45&to=USD

4.5.1. Controller vs document

Rule 21: Reification of controller resource

Before using a controller resource to represent an action, consider reifying the action as a collection or document resource (noun) describing the intent of the action.

Long running tasks are an example of actions modelled as a collection/document instead of a controller.

Example 22. Reification examples

Withdrawal from an account as a controller resource:

POST /account/123/withdraw

or as a collection resource:

POST /account/123/withdrawals

Using a noun improves extensibility in this case. For instance, a GET operation could be added to consult a history of all withdrawals executed on the account.

5. HTTP Methods

5.1. GET

GET must be used to retrieve a representation of a resource. It is a strict read-only method, which should never modify the state of the resource.

HEAD should be used to only retrieve the HTTP response headers​. HEAD returns the same response as GET, except that the API returns an empty body. It is strictly read-only.

5.3. POST

POST must be used to create a new resource in a collection or to execute a controller resource.

The POST request’s body contains the suggested state representation of the new resource to be added to the server-owned collection. The response should contain a Location HTTP header containing the newly created URI.

POST operations may have side effects (i.e. modify state), and are not required to be idempotent.

5.4. PUT

PUT must be used to update a stored resource under a consumer supplied URI. It could be used to insert a new resource in case the client application decides on the resource identifier.

If the URI refers to an already existing resource, the enclosed entity SHOULD be considered as a new version to replace the one residing on the server. If the target resource is successfully modified in accordance with the state of the enclosed representation, then a 200 (OK)​ response SHOULD be sent to indicate successful completion of the request.

If the URI does not point to an existing resource, and that URI is capable of being defined as a new resource, the server can create the resource with that URI. The server MUST inform the client by sending a 201 (Created)​ response to indicate succesful creation.

PUT operations may have side effects (i.e. modify state), but MUST be idempotent.

5.5. PATCH

PATCH must be used to update partially a representation of a resource.

The enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; i.e., new resources may be created, or existing ones modified, by the application of a PATCH.​ PATCH operations are not required to be idempotent, however they will often be in pratice.

See Partial update on how to specify the set of instructions describing how a resource on the server should be modified.

Compatibility

The PATCH http method may not be supported in all components participating in the communication (e.g. HTTP client, intermediary proxy). If these can’t be upgraded to add support, client and server may agree to work around this by implementing PATCH as POST operations with HTTP header 'X-HTTP-Method-Override: PATCH'.

5.6. DELETE

DELETE must be used to remove a resource from its parent. Once a DELETE request has been processed for a given resource, the resource can no longer be found by clients. Therefore, any future attempt to retrieve the resource’s state representation, using either GET or HEAD, must result in a 404 (“Not Found”)​ status returned by the API.

5.7. OPTIONS

OPTIONS should be used to retrieve metadata that describes a resource’s available interactions.

5.8. How to use each method

Method Collection Document Controller Request body Response body

GET

Yes

Yes

Yes

Empty

Resource(s)

HEAD

Yes

Yes

Yes

Empty

Empty

POST

Yes

No

Yes

Representation of the created resource or controller info

Optional

PUT

No

Yes

No

Representation of the updated resource

Optional

PATCH

No

Yes

No

Fields of the resource to update

Optional

DELETE

Yes

Yes

No

Empty

Optional

OPTIONS

Yes

Yes

No

Empty

Optional

6. Status codes

The full list of HTTP status codes is documented here.

In order to conform to the REST uniform service contract, REST services should stick to this reduced list of status codes.

This HTTP status codes chart (takes a while to load) shows a decision tree to determine the usage of the correct HTTP status code.

6.1. 1xx Informational

Request received, continuing process.

6.2. 2xx Success

The action was successfully received, understood, and accepted.

Code Description Methods

200 OK

200 (“OK”) should be used to indicate nonspecific success

In most cases, 200 is the code the client hopes to see. It indicates that the REST API successfully carried out whatever action the client requested, and that no more specific code in the 2xx series is appropriate. Unlike the 204 status code, a 200 response should include a response body.

GET, HEAD, PUT, PATCH, DELETE, OPTIONS

201 Created

201 (“Created”) must be used to indicate successful resource creation

A REST API responds with the 201 status code whenever a collection creates, or a store adds, a new resource at the client’s request. There may also be times when a new resource is created as a result of some custom POST action, in which case 201 would also be an appropriate response.

POST

PUT only in case it is used to create a new document resource.

202 Accepted

202 (“Accepted”) must be used to indicate successful start of an asynchronous action

A 202 response indicates that the client’s request will be handled asynchronously. This response status code tells the client that the request appears valid, but it still may have problems once it’s finally processed. A 202 response is typically used for actions that take a long while to process (see Long running tasks). A POST method may send 202 responses, but other methods should not.

POST

204 No Content

204 (“No Content”) should be used when the response body is intentionally empty

The 204 status code is usually sent out in response to a PUT, POST, PATCH or DELETE request, when the REST API declines to send back any status message or representation in the response message’s body.

POST, HEAD, PUT, PATCH, DELETE, OPTIONS

6.3. 3xx Redirection

Further action must be taken in order to complete the request.

Code Description Methods

301 Moved Permanently

301 (“Moved Permanently”) should be used to relocate resources

The 301 status code indicates that the REST API’s resource model has been significantly redesigned and a new permanent URI has been assigned to the client’s requested resource. The REST API should specify the new URI in the response’s Location header.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

303 See Other

303 (“See Other”) should be used to refer the client to a different URI

The 303 status code allows a REST API to send a reference to a resource without forcing the client to download its state. Instead, the client may send a GET request to the value of the Location header.

It can be used when a long-running action has finished its work, but instead of sending a potentially unwanted response body, it sends the client the URI of a response resource. This can be the URI of a temporary status message, or the URI to some already existing, more permanent, resource.

All methods are acceptable.

Mostly used with POST.

304 Not Modified

304 (“Not Modified”) should be used to preserve bandwidth

This status code is similar to 204 (“No Content”) in that the response body must be empty. The key distinction is that 204 is used when there is nothing to send in the body, whereas 304 is used when there is state information associated with a resource but the client already has the most recent version of the representation. This status code is used in conjunction with conditional HTTP requests.

GET, HEAD

307 Temporary Redirect

307 (“Temporary Redirect”) should be used to tell clients to resubmit the request to another URI

A 307 response indicates that the REST API is not going to process the client’s request. Instead, the client should resubmit the request to the URI specified by the response message’s Location header.

A REST API can use this status code to assign a temporary URI to the client’s requested resource. For example, a 307 response can be used to shift a client request over to another host.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

6.4. 4xx Client Error

The request contains bad syntax or cannot be fulfilled.

Code Description Method

400 Bad Request

400 (“Bad Request”) may be used to indicate nonspecific failure 400 is the generic client-side error status, used when no other 4xx error code is appropriate.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

401 Unauthorized

401 (“Unauthorized”) must be used when there is a problem with the client’s credentials.

A 401 error response indicates that the client tried to operate on a protected resource without providing the proper authorization. It may have provided the wrong credentials or none at all.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

403 Forbidden

403 (“Forbidden”) should be used to forbid access regardless of authorization state.

A 403 error response indicates that the client’s request is formed correctly, but the REST API refuses to honor it. A 403 response is not a case of insufficient client credentials; that would be 401 (“Unauthorized”).

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

404 Not Found

404 (“Not Found”) must be used when a client’s URI cannot be mapped to a resource.

The 404 error status code indicates that the REST API can’t map the client’s URI to a resource.

All: GET, HEAD, PUT, PATCH, DELETE, OPTIONS, POST (if parent resource not found)

405 Method Not Allowed

405 (“Method Not Allowed”) must be used when the HTTP method is not supported.

The API responds with a 405 error to indicate that the client tried to use an HTTP method that the resource does not allow. For example, when a PUT or POST action is performed on a read-only resource supporting only GET and HEAD.

A 405 response must include the Allow header, which lists the HTTP methods that the resource supports. For example: Allow: GET, POST

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

406 Not Acceptable

406 (“Not Acceptable”) must be used when the requested media type cannot be served

The 406 error response indicates that the API is not able to generate any of the client’s preferred media types, as indicated by the Accept request header. For example, a client request for data formatted as application/xml will receive a 406 response if the API is only willing to format data as application/json.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

409 Conflict

409 (“Conflict”) should be used when a request conflicts with the current state of the target resource

The 409 error response tells the client that they tried to put the REST API’s resources into an impossible or inconsistent state. For example, a REST API may return this response code when a client tries to DELETE a non-empty store resource.

POST, PUT, PATCH, DELETE

412 Precondition Failed

412 (“Precondition Failed”) should be used to support conditional operations

The 412 error response indicates that the client specified one or more preconditions in its request headers, effectively telling the REST API to carry out its request only if certain conditions were met. A 412 response indicates that those conditions were not met, so instead of carrying out the request, the API sends this status code.

Only use for conditional HTTP requests, not constraints expressed in the HTTP payload. Use 409 Conflict instead.

POST, PUT, PATCH, DELETE

413 Payload Too Large

413 (“Payload Too Large”) should be used when a request is refused because its payload is too large

The 413 error response indicates that the request is larger than the server is willing or able to process.

POST, PUT, PATCH

415 Unsupported Media Type

415 (“Unsupported Media Type”) must be used when the media type of a request’s payload cannot be processed

The 415 error response indicates that the API is not able to process the client’s supplied media type, as indicated by the Content-Type request header. For example, a client request including data formatted as application/xml will receive a 415 response if the API is only willing to process data formatted as application/json.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

429 Too Many Requests

429 (“Too Many Requests”) should be used to indicate that the user has sent too many requests in a given amount of time.

The response representations SHOULD include details explaining the condition, and MAY include a Retry-After header indicating how long to wait before making a new request. Note that this specification does not define how the origin server identifies the user, nor how it counts requests.

Responses with the 429 status code MUST NOT be stored by a cache.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

6.5. 5xx Server Error

The server failed to fulfill an apparently valid request.

Code Description Methods

500 Internal Server Error

500 (“Internal Server Error”) should be used to indicate API malfunction

500 is the generic REST API error response. Most web frameworks automatically respond with this response status code whenever they execute some request handler code that raises an exception.

A 500 error is never the client’s fault and therefore it is reasonable for the client to retry the exact same request that triggered this response, and hope to GET a different response.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

502 Bad Gateway

The 502 ("Bad Gateway") status code indicates that the server, while acting as a gateway or proxy, received an invalid response from an inbound server it accessed while attempting to fulfill the request.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

503 Service Unavailable

503 (“Service Unavailable”) indicates that the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.

The server MAY send a Retry-After header field to suggest an appropriate amount of time for the client to wait before retrying the request.

All: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS

6.6. Status codes for each method

Code GET HEAD PUT POST PATCH DELETE OPTIONS

200 OK

X

X

X

X (controller only)

X

X

X

201 Created

-

-

X (creation only)

X

-

-

-

202 Accepted

-

-

-

X

-

-

X

204 No Content

-

X

X

X

X

X

-

301 Moved Permanently

X

X

X

X

X

X

X

303 See Other

-

-

-

X

-

-

-

304 Not Modified

X

X

-

-

-

-

-

307 Temporary Redirect

X

X

X

X

X

X

X

400 Bad Request

X

X

X

X

X

X

X

401 Unauthorized

X

X

X

X

X

X

X

403 Forbidden

X

X

X

X

X

X

X

404 Not Found

X

X

X

X

X

X

X

405 Method Not Allowed

X

X

X

X

X

X

-

406 Not Acceptable

X

X

X

X

X

X

X

409 Conflict

-

-

X

X

X

X

-

412 Precondition Failed

-

-

X

X

X

X

-

413 Payload Too Large

-

-

X

X

X

-

-

415 Unsupported Media Type

X

X

X

X

X

X

X

429 Too Many Requests

X

X

X

X

X

X

X

500 Internal Server Error

X

X

X

X

X

X

X

502 Bad Gateway

X

X

X

X

X

X

X

503 Service Unavailable

X

X

X

X

X

X

X

7. Media Types

The HTTP protocol supports content negotiation for media types of the response.

Rule 22: Content negotation

REST clients SHOULD specify the media type(s) that is(/are) acceptable for the response in the Accept HTTP header.

A REST response or request with payload MUST include the Content-Type HTTP header to indicate the media type of the HTTP payload.

Status code 406 MUST be used to indicate that the requested media type could not be provided.

Status code 415 MUST be used to indicate an unsupported media type in the request payload.

If a resource supports multiple content types through negotation, the response header Vary: Accept MUST be added to avoid undesired cache hits (also see Caching).

Example 23. Content negotiation

Request HTTP headers:

Accept: image/jpeg, image/png

Response HTTP headers:

Content-Type: image/png
Vary: Accept

JSON

The primary media type to support is JSON (Javascript Object Notation). The choice for JSON is particularly made for supporting JavaScript clients.

Rule 23: Representation of structured data

Structured data MUST be made available in JSON format (RFC 8259) with media type application/json. JSON text MUST be encoded using UTF-8.

Some legacy clients erroneously decode JSON payloads using a non-UTF-8 encoding by default. To work around this, a charset parameter may be added to the Content-Type HTTP header, even though it has no meaning according to the JSON RFC.

Content-Type: application/json;charset=UTF-8

Media types of format application/<subtype>+json may be used for standardized JSON formats, though REST APIs should also accept requests using the generic application/json media type:

  • application/merge-patch+json for JSON Merge Patch payloads (see Partial update)

  • application/problem+json for Problem Detail for HTTP APIs (RFC 7807) payloads (see Error handling)

XML

On specific client request, a REST service can expose XML messages defined by XML Schema.

Content-Type: application/xml;charset=UTF-8

Always stick to UTF-8 encoding and specify the charset in the Content-Type HTTP header.

It is not recommended to implement both JSON / XML in each REST service. It would require to define and implement two representations of the same data. Automatic conversion between the two standards will almost never give a satisfying result.

Media types in OpenAPI

Payload media types can be specified in an OpenAPI specification.

For JSON, the media type specified in OpenAPI should not contain a charset parameter, because this is known to lead to problems in some server software when treating requests without the parameter in the Accept header.

OpenAPI 2.0

The request and response body’s media type are specified in an OpenAPI 2.0 specification respectively by the consumes and produces keywords:

  • at operation level

  • at root level, as default media types for the entire specification

Example 24. Specifying media types at operation level
  /persons/{ssin}/photo:
    get:
      summary: Get the photo of the person
      operationId: getPhoto
      produces:
      - image/jpeg  # for photo
      - application/problem+json # for problems
      parameters:
      - $ref: "person/identifier/v1/person-identifier-v1.yaml#/parameters/SsinQueryParameter"
      responses:
        "200":
          description: successful operation
          schema:
            type: file
        "400":
          $ref: "#/responses/InvalidSsinResponse"
        "404":
          $ref: "#/responses/UnknownSsinResponse"
OpenAPI 2.0 doesn’t support specifying a different media type per status code.
Rule 24: Default media types

OpenAPI 2.0 specifications SHOULD specify following default media types:

consumes:
- application/json

produces:
- application/json
- application/problem+json

OpenAPI 3.0

OpenAPI 3.0 allows to specify a different media type for each request or response format. Default media types cannot be specified.

Example 25. Specifying OpenAPI 3.0 media types
  /persons/{ssin}/photo:
    get:
      summary: Get the photo of the person
      operationId: getPhoto
      parameters:
      - $ref: "person/identifier/v1/person-identifier-v1.yaml#/components/parameters/SsinQueryParameter"
      responses:
        "200":
          description: successful operation
          content:
            image/jpeg:
              schema:
                type: string
                format: binary
        "400":
          description: SSIN not found
          content:
            application/problem+json:
              schema:
                $ref: "#/components/responses/InvalidSsinResponse"
        "404":
          description: SSIN not found
          content:
            application/problem+json:
              schema:
                $ref: "#/components/schemas/InvalidSsinProblem"

8. Performance

8.1. Embedding resources

In order to reduce the number of requests, optional embedding of subresources may be used.

Rule 25: Embedding subresources

Embedded subresources MUST be put in a JSON object named embedded at root level of the JSON document. In this object, each property key is the URI and the value the full JSON representation of the subresource. The URI MUST be the same as the href value by which the subresource is referenced in the representation of the main resource.

Embedded resources are returned at the client’s request if the embed query parameter is specified. The name of the JSON property referencing the subresource can be used as value of the embed parameter.

This way of embedding resources limits the changes required when a clients starts to use it, and only impacts the JSON Schema type of the root document. It also works well when there are multiple occurrences of the same subresource, and when embedding resources more than one level deep.

In JSON Schema, additionalProperties can be used to specify the data types allowed in embedded. Note that it not possible to specify more than one data type this way in OpenAPI 2.0, so the only restriction that can be specified is type: object when there are multiple embedded data types. In OpenAPI 3.0, oneOf can be used to list all possible data types.

Example 26. Embedding subresources
{
  "self": "http://myrestapi/family/32456",
  "family": {
    "parents": [{
      "ssin": "12345678901",
      "href": "http://myrestapi/people/12345678901"
    }],
    "children": [{
      "ssin": "98765432101",
      "href": "http://myrestapi/people/98765432101"
    }]
  },
  "embedded": {
    "http://myrestapi/people/12345678901": {
      "self": "http://myrestapi/people/12345678901",
      "ssin": "12345678901",
      "givenName": "Jane",
      "lastName": "Doe"
    },
    "http://myrestapi/people/98765432101": {
      "self": "http://myrestapi/people/98765432101",
      "ssin": "98765432101",
      "givenName": "Bobby",
      "lastName": "Doe"
    }
  }
}
JSON data type
Family:
  description: A family
  type: object
  properties:
    parents:
      type: array
      items:
        $ref: '#/definitions/PersonRef'
    children:
      type: array
      items:
        $ref: '#/definitions/PersonRef'
    embedded:
      type: object
      additionalProperties:
        $ref: 'person.yaml#/definitions/Person'
Example 27. Query parameters for subresources more than one level deep

8.2. Caching

Rule 26: HTTP Caching

HTTP caching SHOULD be considered for resource types for which the same resources are repeatedly retrieved by clients.

The following schema shows a decision tree for the caching setup for a given resource:

http cache decision tree

8.2.1. Conditional requests

Conditional requests are those where the client can ask the server if it has an updated copy of the resource. The client will send some information about the cached resource it holds and the server will determine whether updated content should be returned or the client’s copy is already up to date. In the latter case, an HTTP status of 304 Not Modified is returned.

conditional

Though conditional requests do invoke a call across the network, unmodified resources result in an empty response body – saving the cost of transferring the resource back to the end client. The backend service is also often able to very quickly determine a resource’s last version or modification date without accessing the resource which itself saves non-trivial processing time.

Time-based

A time-based conditional request is based on the the last modified time of the resource. If the cached copy’s last modification time is still the most recent one, the server returns the 304 response code. To enable time-based conditional requests, the application specifies the last modified time of a resource via the Last-Modified response header.

Last-Modified: Mon, 03 Jan 2011 17:45:57 GMT

The next time the browser requests this resource it will only ask for the contents of the resource if they’re unchanged since this date using the If-Modified-Since request header

If-Modified-Since: Mon, 03 Jan 2011 17:45:57 GMT

If the resource hasn’t changed since Mon, 03 Jan 2011 17:45:57 GMT the server will return with an empty body with the 304 response code.

Rule 27: Precision of Last-Modified header

The Last-Modified header doesn’t allow subsecond precision, which may lead to incorrect cache hits if there are two updates within a second.

Because of this, the ETag SHOULD be preferred over the Last-Modified header.

Content-based

The ETag (or Entity Tag) works in a similar way as the Last-Modified header except its value is a free-form tag identifying a resource version. Valid ETags can be a MD5 hash, a version number or a modification timestamp, allowing more precision than when using a Last-Modified-header.

This allows the server to identify if the cached contents of the resource are different to the most recent version.

ETag: "15f0fff99ed5aae4edffdd6496d7131f"

On subsequent browser requests, the If-None-Match request header is sent with the ETag value of the last requested version of the resource.

If-None-Match: "15f0fff99ed5aae4edffdd6496d7131f"

As with the If-Modified-Since header, if the current version has the same ETag value as the browser’s cached copy, then an HTTP status of 304 is returned.

8.2.2. Client caching directives

Cache-Control header

An HTTP client cache may cache server responses and decide to not even contact the server when the resource is requested again, saving the round trip to the server. The Cache-Control response header specifies directives for the client under which conditions and how long it should cache the response contents. This is useful for resources which don’t change frequently, and a client doesn’t need to be always synchronized with the latest version of the resource.

Example 28. Cache-Control response header
Cache-Control:public, max-age=86400

The response data may be cached by clients and intermediary servers as it is public, and should expire from the cache after 1 day (86400 seconds).

Vary header

The Vary response header describes which request headers, aside from the method, Host header field, and request target, influence the origin server’s process for selecting and representing this response. It is used to prevent unwanted cache hits.

See Best Practices for Using the Vary Header for more guidelines on the usage of the Vary header.

Example 29. Vary response header
Vary: Accept

This avoids using a cached XML response when a second request asks for JSON.

Never use Vary: * as it will result in a cache hit of 0.

9. JSON

All RESTful services must support JSON (Javascript Object Notation RFC 7159).

9.1. JSON object

For extensibility, the payload of a RESTful request or response should always be a JSON object (instead of an array, string , number, etc). This way the request or response can always be extended with new properties.

Service consumers should be aware that JSON objects can always be extended with additional JSON properties. This shouldn’t break the client code. The unknown properties must be ignored.

9.2. JSON properties

Rule 28: Naming of JSON properties

All JSON property names are in American English and are written in lowerCamelCase notation. This also applies to abbreviations part of a property name : all letters after the first one should always be lowercase.

Do not use underscores (_), hyphens (-) or dots (.) in a property name, nor use a digit as first letter.

Overly generic terms like info(rmation) and data SHOULD NOT be used as property name or as part of it.

A JSON property SHOULD refer to the business meaning of its value rather than how it is defined.

Properties used from other standards, like OpenID Connect and OAuth2, are allowed to deviate from this rule.

KO OK

SSIN

ssin

partnerSSIN

partnerSsin

customerInformation

customer

descriptionString

description

{
  "name": "Belgacom",
  "nssoNbr": 93017373,
  "enterprise": {
    "enterpriseNumber": "0202239951"
  }
}
Rule 29: Null values

Properties with a null value SHOULD be stripped from the JSON message.

A notable exception to this rule are JSON Merge Patch requests, in which a null value indicates the removal of a JSON property.

Note that this rule doesn’t apply to empty values (e.g. empty strings "" or empty arrays []) as they are considered different from a null value.

NOK

OK

{
  "name": "Belgacom",
  "nssoNbr": 93017373,
  "enterprise": null
}
{
  "name": "Belgacom",
  "nssoNbr": 93017373
}

The JSON properties have no specific order inside a JSON object.

{
  "name": "Belgacom",
  "nssoNbr": 93017373
}
{
  "nssoNbr": 93017373,
  "name": "Belgacom"
}

Dates are written in ISO 8601 full-date format: yyyy-MM-dd

{
  "birthday": "1930-07-19"
}

Date/time are written in ISO 8601 date-time format: yyyy-MM-dd’T’HH:mm:ss.SSSXXX

See JSON Schema Validation 7.3.1. date-time and RFC 3339 section 5.6. The fraction part for sub second precision is optional and may be of any length.

Example UTC
{
  "lastModification": "2016-04-24T09:26:01.5214Z"
}
Example with timezone
{
  "lastModification": "2016-04-24T11:26:00+02:00"
}

10. API specifications

Rule 30: API contract-first approach

The contract-first principle SHOULD be followed when developing an API. In this approach, the specifications of the REST API are created first and not generated from the code.

A code-first approach may sometimes be necessary however, as current state of technology does not always fully support contract-first development. In this case, special attention should be given that the API specifications remain stable and loosely coupled to the API implementation technology.

10.1. OpenAPI (Swagger)

OpenAPI is the dominating standard for API documentation, having gained wide industry support. It is based on Swagger, which has been standardized as OpenAPI 2.0.

Rule 31: OpenAPI

Specifications of the API SHOULD be provided using OpenAPI 2.0 (aka Swagger) or OpenAPI 3.0. OpenAPI uses the OpenAPI Schema Object to describe the JSON representation of resources, which is a variant of JSON Schema, with some significant incompatibilities.

OpenAPI 3.1 improves upon OpenAPI 3.0, but to avoid interoperability problems it SHOULD NOT be used yet because it is not yet widely supported by most tooling.

The OpenAPI standard also includes a list of supported primitive data types.

You can visualize OpenAPI files using some of the tools listed in Tools.

Rule 32: Additional documentation

Any additional documentation SHOULD be linked from the API’s OpenAPI definition using the externalDocs property.

Rule 33: Use YAML format for OpenAPI

OpenAPI files SHOULD be made available in YAML format, and OPTIONALLY in JSON format as well.

YAML is a superset of JSON which allows a simpler notation, and also allows comments. According to usage, a conversion step to JSON might be necessary considering limitations of tools.

Rule 34: Use tags to group operations

When an API has many operations, use tags to group them together. This will make the visual representation (SwaggerUI) more readable.

Rule 35: doc and refData resources

The OpenAPI specification file and other API documentation SHOULD be placed under a special doc resource under the API root location.

The swagger specification file is, by convention, named swagger.json or swagger.yaml. Root OpenAPI 3.0 documents SHOULD be named openapi.json or openapi.yaml, as specified in the OpenAPI 3.0 specification.

Resources representing reference data (code lists) specific to an API SHOULD be placed under a refData resource. As reference data is typically static, consider supporting Caching headers for these resources.

Note that these are exceptions to the rule that API resource URLs shouldn’t have a file extension.

Example 30. doc and refData resources
 /doc
     /swagger.json
     /swagger.yaml
     /<optional other documentation>
 /refData
     /<list1OfCodes>
        /<code1>
        /<code2>
        /...
     /<list2OfCodes>
        /...
 /<resource1>
     /...
 /<resource2>
     /...
 ...
Rule 36: default values

Absent optional properties in a request are set by the API provider to their default value if one is specified in the OpenAPI specification.

Rule 37: Add examples in OpenAPI

Add example response values to the OpenAPI specification under the examples property.

Example 31. Examples in OpenAPI

OpenAPI 2.0

  /enterprises/{enterpriseNumber}:
    get:
      operationId: getEnterprise
      parameters:
      - in: path
        name: enterpriseNumber
        required: true
        type: string
      responses:
        "200":
          description: successful operation
          schema:
            $ref: '#/definitions/Enterprise'
          examples:
            application/json:
              {
                "name": "Belgacom",
                "enterpriseNumber": "0202239951"
              }

OpenAPI 3.0

  /enterprises/{enterpriseNumber}:
    get:
      operationId: getEnterprise
      parameters:
      - in: path
        name: enterpriseNumber
        required: true
        schema:
          type: string
      responses:
        "200":
          description: successful operation
          content:
            application/json:
              schema:
                $ref: '#/definitions/Enterprise'
              examples:
                success:
                  {
                    "name": "Belgacom",
                    "enterpriseNumber": "0202239951"
                  }
OpenAPI 2.0 only allows a single example per media type under examples. Any additional examples should be put in external documentation or specified using a x-examples custom extension following the OpenAPI 3.0 format.
Rule 38: Reusable OpenAPI definitions

Instead of specifying everything directly in the swagger.yaml file of an API, OpenAPI allows to reference data types and other definitions from other reusable files. These files SHOULD follow the Swagger/OpenAPI file format as well and may include data type definitions, but also parameter, path items and response objects.

To work around limitations of certain tools, a conversion step to inline the definitions into the swagger.yaml file may be necessary.

Duplication of types in multiple APIs SHOULD be avoided. Rather, put the type in a reusable OpenAPI file. Files reusable from multiple APIs SHOULD be organized in this structure:

<domain>/<version>/<domain-version>.yaml
<domain>/<subdomain>/<version>/<domain-subdomain-version>.yaml

Definitions SHOULD be grouped per (sub)domain in a file. Each file has its own lifecycle, with a major version number in it’s directory and file name, that is increased when backwards compatibility is broken. This version, with optionally a minor and patch version added to it, MUST be specified in the info section in the swagger file as well.

While it is not strictly necessary for external definitions to be put in a valid OpenAPI file, doing so makes it possible to use standard OpenAPI/Swagger tooling on them.

Example 32. Reusable OpenAPI file
/person/identifier/v1/person-identifier-v1.yaml
openapi: "3.0.3"
info:
  title: person-identifier
  description: data types for person identifiers
  version: "1.1.2"
paths: {} # empty paths property required to be a valid OpenAPI file
components:
  schemas:
    Ssin:
      description: "Social Security Identification Number issued by the National Register or CBSS"
      type: string
      pattern: \d{11}

A type can be referenced from another OpenAPI file:

"$ref": "./person/identifier/v1/person-identifier-v1.yaml#/definitions/Ssin"
Rule 39: Common definitions for Belgian government institutions

Common definitions for use by Belgian government institutions are available in the openapi-* GitHub repositories, organized per domain. Types in these schemas SHOULD be used instead of defining your own variants.

The technical types referenced in this style guide are available in the openapi-common and openapi-problem repositories. Other types for business concepts commonly used by Belgian government institutions are available in other repositories.

The OpenAPI files will in the future (WIP) be released in zip archives and via a Maven repository. In addition, they will also be made available on https URLs both in YAML and JSON format through content negotiation (see Media Types), with YAML being the default format.

10.2. JSON data types

Rule 40: Naming of data types

Data type names SHOULD be defined in American English and use UpperCamelCase notation. For abbreviations as well, all letters except the first one should be lowercased.

Do not use underscores (_), hyphens (-) or dots (.) in a data type name, nor use a digit as first letter.

Overly generic terms like info(rmation) and data SHOULD NOT be used as data type name or part of it.

A data type name SHOULD refer to the business meaning rather than how it is defined.

KO OK

SSIN

Ssin

CustomerInformation

Customer

LanguageEnumeration

Language

Rule 41: Data type description

The description property MAY provide a textual description of a JSON data type. The title property MUST NOT be used because it is hides the actual data type name in visualization tools like Swagger UI.

KO OK
Pet:
  title: a pet in the pet store
  type: object
Pet:
  description: a pet in the pet store
  type: object

additionalProperties can be used to put restrictions on other properties of a JSON object than those specified in the schema.

Rule 42: additionalProperties

additionalProperties SHOULD be used exclusively to describe an object representing a map of key-value pairs. The keys of such maps don’t need to respect the naming rules for JSON properties (lowerCamelCase and English).

An example is the description a map of embedded resources, as described in Embedding resources. Other uses of additionalProperties than for maps are to be avoided, in order to support schema evolution.

Rule 43: readOnly properties

Properties SHOULD be declared readOnly when appropriate.

Properties can be declared readOnly: true. This means that it MAY be sent as part of a response but MUST NOT be sent as part of the request. Properties marked as readOnly being true SHOULD NOT be in the required list of the defined schema.

Examples are properties that are computed from other properties, or that represent a volatile state of a resource.

Rule 44: Enum values

A fixed list of possible values of a property can be specified using enum. However, this may make it harder to change the list of possible values, as client applications will often depend on the specified list e.g. by using code generation.

enum SHOULD only be used when the list of values is unlikely to change or when changing it has a big impact on clients of the API.

Example 33. Enum declaration
State:
  type: string
  enum:
  - processing
  - failed
  - done

When defining a type for an identifier or code, like the above example, the guidelines under Identifier apply, even when not used as a URL path parameter of a document resource.

Rule 45: Decimals

Decimal numbers for which the fractional part’s precision is important, like monetary amounts, SHOULD be represented by a string-based type, with number as format. Depending on the context, a regular expression can enforce further restrictions like the number of digits allowed before/after comma or on the presence of a +/- sign.

When number would be used as type instead of string, some technologies will convert the values to floating point numbers, leading to a loss of precision and unintended calculation errors.

This problem may also be avoided by using an equivalent integer representation, for example by expressing a monetary amount in Euro cent rather than Euro.

Some more background on why floating point numbers can lead to loss of precision, can be found in this blog post.

Example 34. Number types preserving precision

belgif openapi-money defines a string-based type for monetary values:

MonetaryValue:
  type: string
  format: number # number is a custom string format that is supported by some, but not all tooling
  pattern: '^(\-|\+)?((\d+(\.\d*)?)|(\.\d+))$'  # Variable number of digits, with at least one digit required, before or after the decimal point. Allows both positive and negative values.
  x-examples:
  - "100.234567"
  - "010"
  - "-.05"
  - "+1"
  - "10"
  - "100."
MonetaryAmount:
  description: A monetary amount
  type: object
  properties:
    value:
      "$ref": "#/components/schemas/MonetaryValue"
    currency:
      "$ref": "#/components/schemas/Currency"
  required: [value, currency]
  example:
    value: "0.01"
    currency: "EUR"

It also defines integer-based types specific for monetary amounts expressed in Euro cent:

EuroCentPositiveAmount:
  description: Money amount in Euro cents >= 0
  type: integer # representation as Euro cent instead of Euro to avoid floating point rounding problems and need for custom 'number' format
  minimum: 0

EuroCentAmount:
  description: 'Money amount in Euro cents, also allows negative amounts.'
  type: integer # representation as Euro cent instead of Euro to avoid floating point rounding problems and need for custom 'number' format

10.3. Tools

Following tools can be used to edit OpenAPI files

Name Link Description

KaiZen OpenAPI editor

https://github.com/RepreZen/KaiZen-OpenAPI-Editor

Open Source Eclipse plugin. Text only editor.

Swagger UI

https://swagger.io/swagger-ui/

Browser application. Graphical and text view of Swagger files. Does not support references to external files.

Swagger UI watcher

https://github.com/moon0326/swagger-ui-watcher

Swagger UI with multi-file support. Only supports viewing. View is refreshed on each file change.

Zalando’s Swagger plugin

https://github.com/zalando/intellij-swagger

Plugin for IntelliJ. Text-only Swagger editor

RepreZen

https://www.reprezen.com/

Commercial editor based on KaiZen. Graphical and text view.

Senya Editor

https://senya.io

Commercial IntelliJ IDEA plugin. Graphical and text view.

Stoplight Studio

https://stoplight.io/studio/

Commercial editor with a free version. Graphical and text view, both web based or as desktop application. Supports validation of API style guides (Spectral).

42Crunch OpenAPI (Swagger) Editor

https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi

Open Source plugin for Visual Studio Code. Text editor with SwaggerUI preview and multi-file support.

Following tools can be used to generate server stubs and API client libraries from OpenAPI specification files.

Name Link Comments

openapi-generator

https://openapi-generator.tech/

Started as fork of swagger-codegen.

swagger-codegen

https://github.com/swagger-api/swagger-codegen

11. Hypermedia controls

Thanks to hypermedia controls a client can navigate through the REST API without hardcoding URIs to specific resources. It suffices to know a limited set of entry points and navigate to the desired information. This adds flexibility to the evolution of the API and increases loose coupling between client and server.

Rule 46: Links to self and other resources

When referencing another resource, a link SHOULD be added using the href attribute. Each resource SHOULD also contain its own location in a self attribute at root level of the JSON document. URIs SHOULD always be absolute.

For brevity, most URIs in the style guide examples are shortened, but in reality URIs should always be absolute.

All href hyperlinks are derived from this minimal link type:

HttpLink JSON data type (from common-v1.yaml)
HttpLink:
  description: A base type of objects representing links to resources.
  type: object
  properties:
    href:
      description: Any absolute URI that is using http or https protocol
      type: string
      format: uri
      readOnly: true

self hyperlinks are derived from the following type:

SelfLink JSON data type (from common-v1.yaml)
SelfLink:
  description: A base type representing a link to the resource's own location within its representation
  type: object
  properties:
    self:
      description: Absolute URI (http or https) to the the resource's own location.
      type: string
      format: uri
      readOnly: true

Links should not be used in request bodies in a PUT, POST or PATCH context, as indicated by the readOnly: true property. In a JSON response, they can be added anywhere.

Example 35. Resource links
{
   "self": "/companies/1"
   "owner": {
      "ssin": "12345678901",
      "href": "http://example.org/v1/people/12345678901"
   },
   "website": "https://wwww.mycompany.com"
}
{
   "owner": {
      "ssin": "12345678902"
   },
   "website": "https://wwww.mynewwebsite.com"
}

The corresponding JSON data type includes the link types using allOf. As website isn’t a reference to another API resource, it is not defined using the HttpLink type.

definitions:
  Company:
    allOf:
    - "$ref": "./common/v1/common-v1.yaml#/definitions/SelfLink"
    - type: object
      properties:
        owner:
          "$ref": "#/definitions/PersonReference"
        website:
          type: string
          format: uri
  PersonReference:
    allOf:
      - "$ref": "./common/v1/common-v1.yaml#/definitions/HttpLink"
      - type: object
        properties:
          ssin:
            "$ref": "#/definitions/Ssin"

Hyperlinks for Pagination inside collections and self-references should use a simple URI value in combination with their corresponding link relations (next, prev, first, last, self) instead of the extensible link type.

The use of Web Linking and Hypertext Application Language (HAL) is not recommended.

12. Reserved words

A list of reserved words exists for common used practices

12.1. Query parameters

Term Description Example Ref

page

When a collection resources is paged, use this parameter to request a specific page. Page numbers are 1-based.

?page=3&pageSize=20

Pagination

pageSize

When a collection resources is paged, use this parameter to specify the page size.

?page=3&pageSize=20

Pagination

q

The standard search parameter to do a full-text search.

?q=Belgacom

Filtering

select

Filter the resource properties to the ones specified.

?select=(name,address)

Consult (Document)

sort

Multi-value query param with list of properties to sort on. Default sorting direction is ascending. To indicate descending direction, the property may be prefixed with -.

?sort=age&sort=-name

Collection

embed

Request to embed subresource

?embed=mainAddress

Embedding resources

lang

language to filter multi-language descriptions

?lang=fr

Multi-language descriptions

12.2. JSON properties

Term Description Example Ref

next

The next-reference contains the absolute URL of the next page in a paged collection.

Links

prev

The previous-reference contains the absolute URL of the previous page in a paged collection.

Links

self

The self-reference contains the absolute URL of the resource itself.

Links

href

A reference (absolute URL) to another resource.

Links

first

A reference (absolute URL) to the first page in a paged collection.

Pagination

last

A reference (absolute URL) to the last page in a paged collection.

Pagination

items

an array with the items of a collection resource

Collection

total

the total number of items in a collection resource

Collection

page

the index of a page in a paged collection

Pagination

pageSize

the maximum number of items in a page of a paged collection

Pagination

embedded

a map of embedded subresources, with URIs as property key and the resource as value

Embedding resources

12.3. HTTP headers

This list includes all standardized and common non-standard HTTP headers.

Rule 47: Custom HTTP headers

Custom HTTP headers SHOULD be prefixed with the organization’s name.

Custom headers that are standardized across Belgian Government organizations use the BelGov- prefix.

X- headers were initially reserved for unstandardized parameters, but the usage of X- headers is deprecated (RFC-6648). Instead, it is recommended that company specific header' names should incorporate the organization’s name. However, for backwards compatibility reasons, headers with the X- prefix may still be used.

Table 5. Standard HTTP Headers referenced in the style guide
HTTP Header Type Description Reference

Location

Response

Used in redirection, or when a new resource has been created.

Create a new resource​, POST, status codes 301, 303 and 307

Accept

Request

Media type(s) that is(/are) acceptable for the response.

Media Types

Content-Type

Request/Response

The Media type of the body of the request.

Media Types

X-HTTP-Method-Override

Request

Override the method specified in the request.

PATCH

Retry-After

Response

Suggest amount of time for the client to wait before retrying the request when temporarily unavailable or quota reached

Service Unavailable, Too Many Requests, Too Many Failed Requests

Allow

Response

Valid methods for a specified resource.

[http-405]

ETag

Request

Identifier for returned response content

Conditional requests

If-None-Match

Response

Return resource if ETag changed

Conditional requests

Last-Modified

Request

Time on which resource was last modified

Conditional requests

If-Modified-Since

Response

Return resource only if changed since specified timestamp

Conditional requests

Vary

Response

Specifies which request headers change response content

Client caching directives

Cache-Control

Response

Indicates HTTP client how to cache responses

Client caching directives

Table 6. BelGov HTTP headers
HTTP Header Type Description Reference

BelGov-Trace-Id

Request/Response

Unique ID for tracing purposes, identifying the request or response

Tracing

BelGov-Related-Trace-Id

Response

BelGov-Trace-Id value used on related request

Tracing

12.4. Resource names

path Description Reference

/doc, /doc/swagger.yaml, /doc/swagger.json

API documentation (swagger file and other)

doc resource

/refData

resources representing reference data used in the API (i.e. code lists)

doc resource

/health

API health status

Health

13. Error handling

Problem Details for HTTP APIs (RFC 7807) defines a simple JSON format to convey additional information about an error to an API consumer. This information complements the HTTP status codes [RFC7231].

For any problem occurred during processing, whether it be caused by the client or the server, the API provider supplies

  1. a suitable status code: 4xx for consumer or 5xx for server errors

  2. a RFC 7807 problem detail message

Problem Details JSON schema (from problem-v1.yaml)
    Problem:
      description: A Problem Details object (RFC 7807)
      type: object
      properties:
        type:
          type: string
          format: uri
          description: An absolute URI that identifies the problem type
          default: about:blank  # kept for backwards-compatibility, type will be mandatory in problem-v2
        href:
          type: string
          format: uri
          description: An absolute URI that, when dereferenced, provides human-readable documentation for the problem type (e.g. using HTML).
        title:
          type: string
          description: A short, summary of the problem type. Written in English and readable for engineers (usually not suited for non technical stakeholders and not localized).
          example: Service Unavailable
        status:
          type: integer
          format: int32
          description: The HTTP status code generated by the origin server for this occurrence of the problem.
          minimum: 400
          maximum: 600
          exclusiveMaximum: true
          example: 503
        detail:
          type: string
          description: A human-readable explanation specific to this occurrence of the problem
        instance:
          type: string
          format: uri
          description: An absolute URI that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced.
Rule 48: Use of RFC 7807 Problem Details

Any information on a problem SHOULD be provided in the Problem Detail format, as specified in Problem Details for HTTP APIs (RFC 7807):

  • the media type for problems SHOULD be application/problem+json

  • status: the HTTP status code

  • type: a unique identifier for the type of the problem that MUST remain stable over time. It can be used by API clients during problem handling.

  • title: a short summary of the problem type in English

  • instance: a unique identifier of the problem instance. It could be used to track the message for support purposes.

  • detail: consumer-friendly information about the specific failing request. It MAY be localized using language negotiation.

  • additional properties may be added to provide more information on the problem

The Problem Details response body MAY be absent when the HTTP status code itself provides sufficient information (e.g. for 405 Method Not Allowed, 406 Not Acceptable or 415 Unsupported Media Type).

This guide applies following additional restrictions on the Problem Details structure:

  • type and instance SHOULD be specified as absolute URIs in order to avoid issues when resolving relative URIs (see Links)

  • The type property, a stable identifier for the type of problem, is MANDATORY instead of optional

  • a new optional href property SHOULD be used to provide a URI to human-readable documentation on the problem type instead of dereferencing the type value

Note that using href instead of type for documentation intentionally deviates from the recommendation in the RFC. href allows use of a URL for documentation purposes that may change over time, while type can be specified as a URN that must remain stable. This is especially useful for API-specific problem types for which the documentation URL may depend on technical aspects, like deployment environment.

Example 37. Example Problem response
POST /enterprises/0206731645/employers
{
    "name": "John"
}
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
BelGov-Trace-Id: d9e35127-e9b1-4201-a211-2b52e52508df
{
  "type": "urn:problem-type:belgif:resourceNotFound",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/resourceNotFound.html",
  "instance": "urn:uuid:d9e35127-e9b1-4201-a211-2b52e52508df",
  "status": 404,
  "title": "Resource is not found",
  "detail": "There is no enterprise in CBE with enterprise number 0206731645",
  "issues": [
      {
        "in": "path",
        "name": "enterpriseNumber",
        "detail": "the enterprise number is not assigned",
        "value": "0206731645"
      }
  ]
}
Rule 49: Problem type

Problem type SHOULD be specified as a URN in one of following formats:

  • urn:problem-type:<org>:<api>:<type>

  • urn:problem-type:<org>:<type>

with:

  • <org>: a short identifier for the organization

  • <api>: name of the API. It MAY be used for API-specific problem types

  • <type>: type of the problem in lowerCamelCase

A description of an API-specific problem type MAY be provided as a reference data resource : /refData/problemTypes/{problemType}. If provided, at least the HTML media type SHOULD be supported.

Reverse FQDN notation for <org> is not recommended in order to keep the identifiers short and readable.

Example 38. API-specific problem type
{
  "type": "urn:problem-type:cbss:socialStatus:searchCriteriaTooWide",
  "href": "https://api.ksz-bcss.fgov.be/socialStatus/v2/refData/problemTypes/urn:problem-type:cbss:socialStatus:searchCriteriaTooWide",
  "status": 400,
  "title": "Search criteria should be more specific",
  "detail": "Only one search parameter specified. Please specify at least two.",
  "instance": "urn:uuid:123e4567-e89b-12d3-a456-426614174000"
}
Rule 50: Error sanitization

A problem MUST NOT leak sensitive implementation and infrastructure details. Providers must strip this information (e.g. DB error codes, internal hostnames, stacktraces) before returning the problem detail to the consumer.

E.g. this should not be returned to a client:

HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: application/problem+json
{
  "type": "urn:problem-type:belgif:internalServerError",
  "instance": "urn:uuid:ac19acc6-5e11-4b2a-8c10-f9680998d07a",
  "title": "Internal Server Error",
  "detail": "Unexpected error code 24879 in server product XYZ v1.0.1",
  "stackTrace": [  [
      "EJBException: java.lang.RuntimeException: Something horrible happened on the server",
      "org.jboss.as.ejb3.CMTTxInterceptor.handleExceptionInOurTx(CMTTxInterceptor.java:191)",
      "org.jboss.as.ejb3.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:282)",
      "org.jboss.as.ejb3.CMTTxInterceptor.required(CMTTxInterceptor.java:345)",
      "org.jboss.as.ejb3.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:243)"
    ],
    [
      "Caused by: java.lang.RuntimeException: Something horrible has happened on the server",
      "sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
      "sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)",
      "sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)",
      "java.lang.reflect.Method.invoke(Method.java:606)"
    ]
  ]
}

13.1. Standardized problem types

A list of problem types is standardized in this guide for reuse across all REST APIs. They are defined as URNs of format urn:problem-type:belgif:<type>.

href links to descriptions of standardized problem types will change in the future to recommended format containing full problem type identifier without HTML extension.

13.1.1. Bad Request

Status code 400 Bad Request

Description The input message is incorrect. Look for more details in the issues property. The issue type urn:problem-type:belgif:input-validation:schemaViolation indicates violations of the OpenAPI schema.

POST /enterprises/abc

{
  "mandator": {
    "enterpriseNumber": "123456"
  }
}

returns

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

{
  "type": "urn:problem-type:belgif:badRequest",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/badRequest.html",
  "status": 400,
  "title": "Bad Request",
  "detail": "The input message is incorrect",
  "issues": [
    {
      "type": "urn:problem-type:belgif:input-validation:schemaViolation",
      "in": "path",
      "name": "enterpriseNumber",
      "value": "abc",
      "detail": "value should be numeric"
    },
    {
      "type": "urn:problem-type:belgif:input-validation:schemaViolation",
      "in": "body",
      "name": "mandator.enterpriseNumber",
      "value": "123456",
      "detail": "An enterprise number should be 10 digits long"
    }
 ]
}
InputValidationProblem schema definition (from problem-v1.yaml)
InputValidationProblem:
  type: object
  allOf:
  - $ref: "#/components/schemas/Problem"
  properties:
    issues:
      type: array
      items:
        $ref: "#/components/schemas/InputValidationIssue"
InputValidationIssue:
  type: object
  allOf:
    - $ref: "#/components/schemas/Problem"
    # status property of Problem is usually not used for input validation issues
  properties:
    in:
      type: string
      enum:
        - body
        - header
        - path
        - query
    name:
      type: string
    value: {} # any type allowed

The possible type values used within issues should be documented for each API. They follow the same URN structure as problem types. It is RECOMMENDED to use input-validation as infix to distinguish them.

InputValidationProblem replaces InvalidParamProblem previously used in this guide, which is now deprecated.

13.1.2. No Access Token

Status code 401 Unauthorized

Description The consumer must pass a valid access token in the Authorization HTTP header for each request to a secure resource.

POST /enterprises
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245

If no access token was found, the service returns:

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
   "type": "urn:problem-type:belgif:noAccessToken",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/noAccessToken.html",
   "status": 401,
   "title": "No Access Token",
   "detail": "No Bearer access token found in Authorization HTTP header"
}

13.1.3. Invalid Access Token

Status code 401 Unauthorized

Description The consumer must pass a valid access token in the Authorization HTTP header for each request to a secure resource.

POST /enterprises
Authorization: Bearer foo
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
   "type": "urn:problem-type:belgif:invalidAccessToken",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/invalidAccessToken.html",
   "status": 401,
   "title": "Invalid Access Token",
   "detail": "The Bearer access token found in the Authorization HTTP header is invalid"
}

13.1.4. Expired Access Token

Status code 401 Unauthorized

Description The access token passed in the Authorization HTTP header has expired and cannot be used anymore. Renew the access token and resubmit the request.

POST /enterprises
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
   "type": "urn:problem-type:belgif:expiredAccessToken",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/expiredAccessToken.html",
   "status": 401,
   "title": "Expired Access Token",
   "detail": "The Bearer access token found in the Authorization HTTP header has expired"
}

13.1.5. Missing Scope

Status code 403 Forbidden

Description The consumer access token doesn’t have the required scope to invoke the operation. The requiredScopes property lists the required scopes.

GET /enterprises/202239951
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
   "type": "urn:problem-type:belgif:missingScope",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/missingScope.html",
   "status": 403,
   "title": "Missing Scope",
   "detail": "Forbidden to consult the enterprise resource",
   "requiredScopes": ["enterprise-read"]
}

13.1.6. Missing Permission

Status code 403 Forbidden

Description The consumer doesn’t have the right to invoke an operation on the given resource. The refusal is not related to the consumer scope but to access to the specific resource itself (data access).

PUT /enterprises/202239951
Authorization: Bearer ds4d2f13sdds2q13qxcgbdf245
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
   "type": "urn:problem-type:belgif:missingPermission",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/missingPermission.html",
   "status": 403,
   "title": "Missing Permission",
   "detail": "Not permitted to update the details of this enterprise"
}

13.1.7. Resource Not Found

Status code 404 Not Found

Description The requested resource cannot be found. The detail property reveals additional information about why the resource was not found.

Either the resource path isn’t specified in the API’s OpenAPI specification.

GET /enterprises

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "urn:problem-type:belgif:resourceNotFound",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/resourceNotFound.html",
  "status": 404,
  "title": "Resource Not Found",
  "detail": "No resource /enterprises found"
}

Either the path parameters don’t resolve to an existing resource. Look for the more details in the issues property.

GET /enterprises/0206731645

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "urn:problem-type:belgif:resourceNotFound",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/resourceNotFound.html",
  "status": 404,
  "title": "Resource not found",
  "detail": "No resource /enterprises/0206731645 found",
  "issues": [
      {
        "in": "path",
        "name": "enterpriseNumber",
        "detail": "the enterprise number 0206731645 is not assigned",
        "value": "0206731645"
      }
  ]
}

The InputValidationProblem Schema Object SHOULD be used to represent this type of problems.

13.1.8. Payload Too Large

Status code 413 Payload Too Large

Description The consumer request was refused because its payload is too large. The property limit contains the maximum payload size expressed in bytes.

POST /attachments

<very large binary attachment>

returns

HTTP/1.1 413 Payload Too Large
Content-Type: application/problem+json

{
   "type": "urn:problem-type:belgif:payloadTooLarge",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/payloadTooLarge.html",
   "status": 413,
   "title": "Payload Too Large",
   "detail": "Request message must not be larger than 10 MB",
   "limit": 10485760
}

13.1.9. Too Many Requests

Status code 429 Too Many Requests

Description The consumer quota was exceeded. The property limit contains the number of requests allowed. retryAfter or retryAfterSec indicate when the consumer may submit new requests again, respectively at a specific time or as a number of seconds after the reply. The Retry-After HTTP header conveys the same information.

Retry after a specific time

HTTP/1.1 429 Too many requests
Content-Type: application/problem+json
Retry-After: Thu, 05 Aug 2021 10:30:00 GMT

{
   "type": "urn:problem-type:belgif:tooManyRequests",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyRequests.html",
   "status": 429,
   "title": "Too Many Requests",
   "detail": "No more requests accepted before 2021-08-05T10:30:00Z",
   "limit": 200,
   "retryAfter": "2021-10-30T10:30:00Z",
   "retryAfterSec": 60
}

Retry after a number of seconds

HTTP/1.1 429 Too many requests
Retry-After: 60
Content-Type: application/problem+json
{
  "type": "urn:problem-type:belgif:tooManyRequests",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyRequests.html",
   "status": 429,
   "title": "Too Many Requests",
   "detail": "No more requests accepted during next 60 seconds",
   "limit": 200,
   "retryAfterSec": 60
}

13.1.10. Too Many Failed Requests

Status code 429 Too Many Requests

Description No more new requests are accepted because the consumer quota for failed requests was exceeded. The property limit contains the number of failed requests allowed. retryAfter or retryAfterSec indicate when the consumer may submit new requests again, respectively at a specific time or as a number of seconds after the reply. The Retry-After HTTP header conveys the same information.

GET /enterprises/123invalidEnterpriseId

Retry after a specific time

HTTP/1.1 429 Too many requests
Content-Type: application/problem+json
Retry-After: Thu, 05 Aug 2021 10:30:00 GMT

{
   "type": "urn:problem-type:belgif:tooManyFailedRequests",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyFailedRequests.html",
   "status": 429,
   "title": "Too Many Failed Requests",
   "detail": "No more requests are accepted because of previous failures until 2021-08-05T10:30:00Z",
   "limit": 50,
   "retryAfter": "2021-08-05T10:30:00Z"
}

Retry after a number of seconds

HTTP/1.1 429 Too many requests
Retry-After: 300
Content-Type: application/problem+json
{
   "type": "urn:problem-type:belgif:tooManyFailedRequests",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/tooManyFailedRequests.html",
   "status": 429,
   "title": "Too Many Failed Requests",
   "detail": "No more requests are accepted because of previous failures during next 300 seconds",
   "limit": 50,
   "retryAfterSec": 300
}

13.1.11. Internal Server Error

Status code 500 Internal Server Error

Description The server has encountered a situation it doesn’t know how to handle.

HTTP/1.1 500 Internal Server Error
Content-Type: application/problem+json

{
   "type": "urn:problem-type:belgif:internalServerError",
   "href": "https://www.belgif.be/specification/rest/api-guide/problems/internalServerError.html",
   "status": 500,
   "title": "Internal Server Error"
}

13.1.12. Bad Gateway

Status code 502 Bad Gateway

Description The API, acting as a gateway or proxy to an API provided by a third party, received an invalid response from the upstream server.

HTTP/1.1 502 Bad Gateway
Content-Type: application/problem+json

{
  "type": "urn:problem-type:belgif:badGateway",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/badGateway.html",
  "status": 502,
  "title": "Bad Gateway",
  "detail": "Error in communication with upstream service"
}

13.1.13. Service Unavailable

Status code 503 Service Unavailable

Description The service is unavailable, because of a planned or unplanned downtime. If present, retryAfter or retryAfterSec indicate when to retry, respectively at a specific time or as a number of seconds after the reply. The Retry-After HTTP header conveys the same information.

Retry after a specific time

HTTP/1.1 503 Service Unavailable
Retry-After: Thu, 05 Aug 2021 10:30:00 GMT
Content-Type: application/problem+json
{
  "type": "urn:problem-type:belgif:serviceUnavailable",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/serviceUnavailable.html",
  "instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
  "status": 503,
  "title": "Service is unavailable",
  "detail": "The service is unavailable due to a scheduled maintenance, and is expected to be available again at 10h30 on Aug 5th, 2021",
  "retryAfter": "2021-08-05T10:30:00Z"
}

Retry after a number of seconds

HTTP/1.1 503 Service Unavailable
Retry-After: 120
Content-Type: application/problem+json
{
  "type": "urn:problem-type:belgif:serviceUnavailable",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/serviceUnavailable.html",
  "instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
  "status": 503,
  "title": "Service is unavailable",
  "detail": "The service experiences an unplanned downtime. Please try again after 120 seconds.",
  "retryAfterSec": 120
}

Without indication when to retry

HTTP/1.1 503 Service Unavailable
Content-Type: application/problem+json
{
  "type": "urn:problem-type:belgif:serviceUnavailable",
  "href": "https://www.belgif.be/specification/rest/api-guide/problems/serviceUnavailable.html",
  "instance": "d9e35127-e9b1-4201-a211-2b52e52508df",
  "status": 503,
  "title": "Service is unavailable",
  "detail": "The service experiences an unplanned downtime."
}

14. Versioning

14.1. Preserving compatibility

Don’t Break Backward Compatibility

Change APIs, but keep all consumers running. Consumers usually have independent release lifecycles, focus on stability, and avoid changes that do not provide additional value. APIs are service contracts that cannot be broken via unilateral decisions.

There are two techniques to change APIs without breaking them:

  • evolve the API, allowing clients to gradually implement changes by following rules for compatible extensions and deprecation

  • introduce new major API version and still support older version(s) for a period in which clients can transition to the new version

Backwards-incompatible changes, requiring a big bang release, are only feasible if:

  • there is a very limited number of clients

  • the release is well coordinated

  • a temporary service interruption is allowed.

In this case, the major version number could be kept the same as only a single version of the API is active at a given time and it can be easier to keep the base URL of the API unchanged.

Rule 51: Prefer API evolution

We strongly encourage using API evolution when possible and discourage versioning:

  • versioning requires maintaining and supporting two service contracts

  • API evolution maintains stable URIs (e.g. links from other APIs) and allows for a more gradual migration of clients

Use of media type versioning (using Accept/Content-Type HTTP headers) isn’t included as an option in this guide as it has various practical problems

14.2. API evolution

Rule 52: JSON object as top level structure

In a response body, you MUST always return a JSON objects (and not e.g. an array) as a top level data structure to support future extensibility.

JSON objects support compatible extension by additional attributes. This allows you to easily extend your response and e.g. add pagination later, without breaking backwards compatibility.

With Postel’s Law in mind, here are some rules for providers and consumers that allow us to make compatible changes without versioning:

Rule 53: Compatible extensions

Apply the following rules to evolve RESTful APIs in a backward-compatible way:

  • New fields may be added to response. Clients MUST ignore unknown fields in response payloads.

  • Add only optional, never mandatory fields to request payloads

  • Never change the meaning of a field.

  • Never change input validation logic to be more restrictive

  • Enum ranges can be reduced when used as input parameters, only if the server is ready to accept and handle old range values too.

  • Enum values can be reduced when used as output parameters, but not extended as clients might not be ready to handle them.

  • Support redirection in case an URL has to change (301 Moved Permanently)

  • a new resource variant (with another name) can be created in addition to the old resource variant

Rule 54: Deprecation

Deprecation MAY be used to indicate parts of an API are not recommended to be used anymore. Their deprecation SHOULD be documented in OpenAPI . Deprecated elements MAY be completely removed them after a period in which clients can adapt. Always communicate clearly about the deprecated fields and transition period to clients.

Newer versions of the OpenAPI standard have improved support to indicate deprecation using deprecated: true:

  • OpenAPI 2.0 only allows specifying deprecation on operations

  • OpenAPI 3.0 adds support for deprecating parameters and Schema Objects (data types), though not yet everywhere like on object properties

deprecated: true SHOULD be used if the OpenAPI version allows it, otherwise the custom OpenAPI extension x-deprecated: true SHOULD be used.

Use of deprecated elements in requests may be monitored by the server in order to evaluate the impact of their removal. However, this isn’t possible for response elements, so there is always remains some risk for breaking clients.

14.3. API versioning

Rule 55: Version numbering

The version string of an API SHOULD contain a major, minor and optionally a patch number.

Only the major version of the API is part of the base URL (basePath in Swagger) of the API. Thereby, for a major version only a single minor and patch version can be available in production at a time. The version of the API MUST be included in the info section of the OpenAPI definition.

Note that reusable OpenAPI files are to be versioned as well, as specified in OpenAPI (Swagger).

Example 39. API version in OpenAPI 2.0
swagger: "2.0"
info:
  title: petShop
  description: API exposing my pet shop’s functionality
  version: "2.1.2"
host: example.org
basePath: /petShop/v2
Example 40. API version in OpenAPI 3.0
openapi: "3.0.3"
info:
  title: petShop
  description: API exposing my pet shop’s functionality
  version: "2.1.2"
servers:
  url: https://example.org/petShop/v2

Similar guidelines apply to sunset an old major version of an API apply as for removal of deprecated elements.

Rule 56: Removing an old version

Before removing an old API version:

  • provide a transition period supporting old and new versions at the same time before removing the old version

  • always communicate clearly about the transition period to clients

  • use of the old version may be monitored by the server in order to evaluate the impact of their removal.

15. Internationalization (I18N)

Textual descriptions part of a resource may need to be offered in the language of a user.

One of following strategies may be chosen for internationalization:

  • Do not offer textual descriptions in the API response. Offer them only separate from the data, for example as a dedicated collection resource in the API returning a code list with descriptions. This list can be cached by a client.

  • Only include descriptions in a single language, specified by the caller in the Accept-Language header.

  • Offer descriptions in all applicable languages, i.e. for APIs offered by the Belgian Government in French, Dutch, German and sometimes English as well. This approach is not scalable to high number of languages. The descriptions may be filtered by a lang query parameter.

15.1. Accept-Language

As for Media Types, HTTP supports content negotiation for the language used in response content.

Rule 57: HTTP language headers

Users can inform the server that a specific language is requested by adding the Accept-Language HTTP header to each request (RFC 7231).

Its value is a language tag (RFC 5646), consisting of a ISO 639-1 lowercase language code, optionally followed by an uppercase country code indicating a country-specific variant. Internationalized APIs of Belgian Federal Government organizations MUST also support the variants for the official Belgian languages ending on -BE. Multiple languages may be specified in order of preference. A wildcard * matches any language.

The Content-Language response header MUST be used to indicate the language used in the response for internationalized content.

In case the server could not honor any of the requested languages, it SHOULD return a 406 Not Acceptable error. If the resource supports caching, the Vary: Accept-Language MUST be included in the response.

Example 41. HTTP language headers
Request headers
Accept-Language: nl-BE, nl, fr-BE, fr, en, *
Response headers
Content-Language: nl

15.2. Multi-language descriptions

It may be decided to include all supported translations for descriptions in a resource’s representation.

Rule 58: Multi-language descriptions

A multi-language description SHOULD be represented by a JSON object with ISO 639-1 language codes as property names and the corresponding textual description as values.

An API MAY offer to filter the representation to a single language by using the reserved query parameter lang.

A LocalizedString type is defined supporting the three official Belgian languages.

LocalizedString JSON Schema (from common-v1.yaml)
LocalizedString:
  description: A description specified in multiple languages
  type: object
  properties:
  fr:
    type: string
  nl:
    type: string
  de:
    type: string
Example 42. Multi-language descriptions
Request

GET /countries/BE

Response
"name": {
 "fr": "Belgique",
 "nl": "België",
 "de": "Belgien"
}
Filtering a single language

GET /countries/BE?lang=fr

"name": {
 "fr": "Belgique"
}

16. Tracing

In order to uniquely identify requests or response messages, a tracing ID may be added as HTTP header. This tracing id can be used to be able to trace the flow of calls across APIs and organizations.

The trace ID could be used for example:

  • for operational purposes, to enable lookup related technical logs

  • for audit logging, in order to be able to trace the origin of each request. It may be a legal requirement to have such audit logging when sensitive data is exchanged.

Rule 59: HTTP headers for tracing

APIs SHOULD support the tracing HTTP header BelGov-Trace-Id on both requests and responses. Generated trace IDs MUST NOT be longer than 36 characters and unique. The trace id on a response MUST be generated by the API provider and is not copied from the request. Using UUIDs as tracing headers is RECOMMENDED.

Additionally, the BelGov-Related-Trace-Id HTTP header MAY be present on a response. Its value MUST be the same as the BelGov-Trace-Id on the request.

Within an organization, other headers MAY be used for tracing calls, for instance the headers supported by tracing tools like Zipkin or Jaeger.

In communication external to an organization, each BelGov-Trace-Id value should be unique. For instance, when a RESTful service makes multiple outgoing calls within the context of the same incoming request, every outgoing request should be uniquely identifiable with its own ID. For auditing purposes, it should be possible to correlate the various tracing IDs used in the context of a request.

An API provider should not be dependent on the client generating correct unique trace IDs.

17. Events

When systems are in need of subscribe-notify capabilities, they tend to define their own custom event format.

To improve interoperability, this styleguide promotes and adopts the CloudEvents specification prepared by the Cloud Native Computing Foundation (CNCF): https://cloudevents.io/.

On October 24, 2019 they released a v1.0 version of this specification which thoroughly separates the core conceptual event model from any format and protocol details. Moreover, some SDKs are in development to support integration of CloudEvents in some popular programming languages.

Table 7. CloudEvents documentation
Description Link

CloudEvents overview

https://github.com/cloudevents/spec/blob/v1.0/README.md

Conceptual model

https://github.com/cloudevents/spec/blob/v1.0/spec.md

JSON Event Format

https://github.com/cloudevents/spec/blob/v1.0/json-format.md

HTTP Protocol Binding

https://github.com/cloudevents/spec/blob/v1.0/http-protocol-binding.md

Web Hooks for Event Delivery

https://github.com/cloudevents/spec/blob/v1.0/http-webhook.md

Extensions (Sequence, Claim Check, Partitioning, …​)

https://github.com/cloudevents/spec/blob/v1.0/documented-extensions.md

18. Health

Each REST API SHOULD expose a health resource which returns the current availability status of the service.

When invoked without any access token, the resource simply returns its status. The status code is either 200 OK when the service is up or partially available, or 503 Service Unavailable when the service is down or out of service.

Service is up
{
  "status": "UP"
}

When invoked by a client with additional health-check permissions, the resource MAY return additional details on the status of its subsystems or components. This internal information should be hidden from external clients for security reasons.

Service is down, with additional details
{
  "status": "DOWN",
  "details": {
    "datastore": {
      "status": "DOWN",
      "errorMessage": "connection timeout"
    }
  }
}

The health resource is specified in common-v1.yaml. Note that uppercase is used for the status values, which differs from the Enum values rule, in order to align with existing health checks provided by frameworks like Spring Boot and MicroProfile Health. The format of additional component-level details is not specified.

GET

/health

Check the health status of the API.

Response

body

The status of the service. Component-level details may be shown when the client has additional permissions.

{
   "status": "UP"
}
{
   "status": "DOWN"
}

Response codes

200

OK

When service is UP or DEGRADED

503

Service Unavailable

When service is DOWN

18.1. Status levels

The health resource returns one of the following status levels indicating the component or system:

Status Status Code Description

UP

200

is functioning as expected.

DEGRADED

200

is partly unavailable but service can be continued with reduced functionality.

DOWN

503

is suffering unexpected failures

The status property also allows custom strings for other use cases.