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.
GET {API}/employers/93017373[^]
{
"name": "Belgacom",
"employerId": 93017373,
"company": {
"enterpriseNumber": "0202239951",
"href": "{API}/companies/0202239951[^]"
},
"self": "{API}/employers/93017373[^]"
}
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.

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.
Identifier
The identity key of a document resource is preferably a natural business identifier uniquely identifying the business resource like an ISBN 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)
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 identical. 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.
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 | 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" | | can be human-readable, long, not easy to input by user |
URI (http) | "https://www.waterwegen.be/id/rivier/schelde" | | 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" | | 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, …).
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 ???).
GET /refData/paymentMethods/{code} with code of type PaymentMethodCode
As string with enumeration:
PaymentMethodCode:
type: string
enum:
- cash
- wireTransfer
- creditCard
- debitCard
As string with regular expression:
PaymentMethodCode:
type: string
pattern: "^[A-Za-z0-9]+$"
example: "debitCard"
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 erroneous 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: int32and usingminimum/maximum).
For new identifiers, it is not recommended to use a number type however as stated in simpara_title
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
format: int64
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
Following naming guidelines should be applied when using an identifier or code in a REST API:
JSON property:
within an object that represents the entire or part of a resource: use
idorcodeto reference a resource within another one’s representation: property name should designate the relation between the resources (see ???); no need to suffix with
idorcode
path parameter: use
idorcode, OPTIONALLY prefixed with the resource type to disambiguate when there are multiple identifiers as path parametersquery search parameter: use same name as the property in the JSON resource representation of the response (see ???)
OpenAPI type: add suffix
IdorCodeto distinguish it from the type of the full resource representation
As an exception, use the standardized name for the business identifier if one exists, rather than id or code.
If multiple identifiers or coding schemes may be used within the same context, a suffix can be added to the name to disambiguate.
Request:
GET /stores/{storeId}/orders/{orderId}
Response:
{
"id": 123,
"customer": 456,
"store": {
"id": 789,
"href": "/stores/789"
},
"paymentMethod": "creditCard",
"deliveryMethod": {
"code": "deliveredAtHome",
"href": "/refData/deliveryMethods/deliveredAtHome"
}
}
OpenAPI types (not all are listed for brevity):
Order:
type: object
properties:
id:
$ref: "#/components/schemas/OrderId"
customer:
$ref: "#/components/schemas/CustomerId"
store:
$ref: "#/components/schemas/StoreReference"
paymentMethod:
$ref: "#/components/schemas/PaymentMethodCode"
deliveryMethod:
$ref: "#/components/schemas/DeliveryMethodReference"
OrderId:
type: integer
format: int32
minimum: 1
StoreReference:
allOf:
- $ref: "./common/v1/common-v1.yaml#/definitions/HttpLink"
type: object
properties:
id:
$ref: "#/components/schemas/StoreId"
StoreId:
type: integer
format: int32
minimum: 1
path parameter:
idprefixed with resource type to be able to distinguish both parametersidas property of the consulted resourceidentifier used to reference another resource. JSON property name designates relation to the other resource.
idas property in a partial representation of astoreresourcecodeas property in a partial representation of adeliveryMethodresourceThe type of the full resource representation doesn’t have a suffix
Types of the identifiers have the suffix
IdorCodePartial resource representations, which may link to the full resource
GET /refData/deliveryMethods/{code}
{
"code": "deliveredAtHome",
"description": {
"nl": "Geleverd aan huis",
"en": "Delivered at home",
"fr": "Livré à domicile"
}
}
- Since there are no child resources with other path parameters, simply
codesuffices
GET /persons/{ssin}
{
"ssin": "12345678901",
"partner": "2345678902",
"civilStatus": 1
}
Standardized business identifier name
Ssinis preferred overid.JSON property name designates relation to the other resource. The OpenAPI specification declares the expected value to be of type
Ssin.
GET /addresses/{addressId}
{
"municipality": {
"code": 10000,
"name": "Brussels"
},
"country": {
"nisCode": 150,
"isoCode": "BE",
"name": {
"nl": "België",
"fr": "Belgique"
}
}
}
- Prefixes
nisandisodisambiguate between two types of country identifiers used in a single context
Consult
GET {API}/employers/93017373[^] HTTP/1.1
/employers/{employerId} | Consult a single employer | |
Parameters | ||
| path-param | NSSO number uniquely identifying the employer. |
Response | ||
body | The response properties and links to other resources. | |
Most used response codes | ||
OK | Default success code if the document exists. | |
Bad request | The dynamic path segment containing the identity key has a wrong data format: | |
Not Found | The document resource does not exist. | |
204 No content should not be used with GET.
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> ::= "!"
GET /employers/93017373?select=(name)
{
"self": "{API}/employers/93017373?select=(name)[/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": "{API}/employers/93017373[/employers/93017373^]",
"name": "Proximus",
"address": {
"street": {
"name": "Koning Albert II laan",
"code": 2177
}
}
}
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:
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.
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.
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 ??? 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.
PUT {API}/employers/93017373[^] HTTP/1.1
{
"employerId": 93017373,
"name": "Belgacom"
}
??? | /employers/{employerId} | Replace the entire employer resource with the client data. This implies a full update of the resource. Via |
Request | ||
body | Full representation of the resource to persist. | |
Parameters | ||
| path-param | employer ID of NSSO uniquely identifying the employer. |
Response | ||
body | either empty or resource after update | |
Most used response codes | ||
OK | The update is successful and updated resource is returned. | |
No Content | The update is successful but updated resource is not returned. | |
Bad request | The input data is not valid according the data schema. | |
Not Found | The resource does not exist and thus cannot be updated. | |
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 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
nullin the request are removed from the resourceproperties 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.
PATCH {API}/employers/93017373[^] HTTP/1.1
/employers/{employerId} | Performs a partial update of an existing employer. | |
Request | ||
body | JSON Merge Patch | |
Parameters | ||
| path-param | employer ID of NSSO uniquely identifying the employer. |
Response | ||
body | empty or the complete resource after applying PATCH | |
Most used response codes | ||
OK | Success code with resource after applying PATCH returned. | |
No Content | Success code without returning the resource. | |
Bad request | The input data is not valid according the data schema. | |
Not Found | The resource does not exist and thus cannot be updated. | |
Conflict | The client data is in conflict with the data on the server e.g. optimistic locking issues. | |
Remove
Use DELETE when you like to delete a document resource.
DELETE {API}/employers/93017373[^] HTTP/1.1
/employers/{employerId} | Deletes an employer. | |
Parameters | ||
| path-param | employer ID of NSSO uniquely identifying the employer. |
Response | ||
body | empty or a message indicating success | |
Most used response codes | ||
OK | Success code with the deleted resource returned. | |
No Content | Success code with the deleted resource not returned. | |
Bad request | Generic error on client side. For example, the syntax of the request is invalid. | |
Not Found | The resource does not exist and thus cannot be deleted. | |
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. | |
Long-running tasks
Some operations need to be performed asynchronously, as they take too long to complete.
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 OKand a representation of the task’s current statusSuccess: status
303 See Otherwith theLocationheader containing the URL of the task’s output.Failure: status
200 OKwith 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.
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).