API guidelines — Part C: API development 2022
Learn about the technical details in developing an API (Application Programming Interface) and the standards that need to be met.
1. API design and development
This section covers key considerations such as good API design, correct use of the Hyper Text Transfer Protocol (HTTP), version control and various technical standards.
1.1. API artefacts
Some fundamental artefacts that comprise an API deliverable are:
- interface specification, accessible via some form of catalogue
- API code and policies
- tests
- Service Level Agreement (SLA) for the API, identifying performance, availability.
These artefacts are the responsibility of the API developer and should be mandated as a minimum set of deliverables when developing an API. All of these artefacts must be managed in a source code management system so that changes can be tracked and collaborative development can be performed.
1.2. API design
Learn about good API designs and how it will attract other developers.
Application developers will invest heavily in using your API. They will invest in learning the design and behaviour of your API, in developing and testing around your API and may even invest in developing an entire business model on your API.
Good API designs will attract more developers. Conversely, bad API designs will drive away developers and drive up costs — support costs, maintenance costs and operating costs.
1.2.1. When an API is appropriate
In the past, the default reaction to a requirement for capability has been to develop a web application. This is now gravitating towards APIs as the default. So, when is it better to build an API than a web application?
Situations where an API may be more appropriate are when:
- commercial organisations or non-governmental organisations (NGOs) are screen scraping data from the agency’s website
- the agency holds a single authoritative source of the truth
- there is a need for (near real time) information exchange with a partner agency
- parts of a business process are (or may be) outsourced
- the agency’s service can be included as part of a larger business process
- the agency needs internal systems to interact with cloud-based commercial off-the-shelf (COTS) solutions (software as a service (SaaS))
- businesses require easy access to (public) information
- commercial organisations want to build capability into their own applications that will benefit the public (mobile / web applications)
- the agency expects other parties to act as agents or intermediaries for their services
- the agency expects to support service delivery across multiple channels.
1.2.2. Types of API
There are several different types of API and the type you choose may depend on the technical use cases that you apply to both consumption and provision of your API.
API type | Description | Usage |
---|---|---|
Representational State Transfer (REST) | REST is the most common and well understood API type. REST should be considered an architectural style for developing distributed hypermedia systems. There is a wealth of information and tooling to support the definition and creation of REST APIs. Typically, a REST API will have a well-defined and strongly-typed schema definition (OpenAPI) where strict compliance can be achieved. |
Creating a distributed system where a set of API resources are well defined. If medium latency resource creation or modification (POST, PUT, DELETE) is required then typically a REST API is a better fit. Typically used for synchronous interactions. |
GraphQL |
GraphQL is an open source query and manipulation language developed by Facebook primarily designed to empower API consumers to consume only the data that they require. A common criticism of REST is that only the entire resource is available for consumption, sometimes referred to as ‘over fetching’, however with GraphQL the client decides the data that it requires. GraphQL also has a strongly-typed schema (GraphQL Schema Definition Language (SDL)). |
An API that has a widely distributed client set with a variety of data requirements. Particularly well suited to high read (GET) clients. |
Asynchronous APIs |
AsyncAPI is an open source initiative to create an event-driven and asynchronous API standardisation and development tooling. The AsyncAPI specification, inspired by the OpenAPI specification, describes and documents event-driven APIs in a machine-readable format. |
Creating distributed systems where a set of API resources are well defined. Typically used for asynchronous interactions and event-driven architectures. Also useful when developing APIs that front workflows or long running orchestrations. |
gRPC |
gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. In gRPC, a client application can directly call a method on a server application on a different machine as if it were a local object, making it easier for you to create distributed applications and services. This is enabled by a formal Interface Definition Language (IDL). gRPC utilises Protocol Buffers by default, you can make it work with other data formats, such as JavaScript Object Notation (JSON). |
Creating distributed systems that require highly performing and scalable APIs. gRPC makes use of binary data rather than just text which makes the communication more compact and more efficient. |
Figure 1 demonstrates a combination of a synchronous API and an asynchronous API.
Figure 1 has 2 associated API specifications. The first is an OpenAPI specification that describes the ‘create claim’ and ‘get claim’ RESTful interactions and the second is an AsyncAPI specification that describes the ‘receive updates’ interaction. These example specifications have been provided in Appendix D.
1.2.3. API design principles
This section assumes that API principles defined in Part A: API concepts and managment 2022 of these guidelines have already been read.
1.2.3.1 Future-focused design
APIs should not be tightly coupled to legacy applications, exposing whatever capabilities the legacy system offered. Nor should they be designed to work in the way the legacy system currently works.
Instead APIs should be consumer driven — built to expose the resources that consumers need, whether those resources are in legacy systems or new. There should not be a drive to expose an entire product via the API or to wait for the perfect backend system to be available. The recommendation is to just offer as much as is practically useful, then evolve it to meet needs. The API interface should be abstracted from the backend, so that backend systems can be changed or replaced over time without needing to change the interface.
The aim is to be future focused (while still pragmatic) and develop APIs to meet future needs. A good example of being future focused is building APIs to support Hypermedia as the Engine of Application State (HATEOAS). This is where an API, in response to a consuming application’s request, is programmed to return hyperlinks (URLs) that indicate the options for subsequent actions or information.
1.2.3.2. Layering
When designing and developing an API it is important to consider that an API is made up of the following distinct functional layers:
1.2.3.3. Standards based
Web standards have rapidly become powerful agreements, which span not just local regions but are internationally accepted and enable commonality and consistency. Using standard HTTP and URLs, the core technologies of the web, along with standards such as JSON and OAuth (Open Authorization) ensures that agencies are not creating bespoke or proprietary technologies.
Hence the principle is to build to the latest versions of existing open and accepted standards such as:
- HTTP
- OpenAPI
- AsyncAPI
- REST
- JSON
- OAuth
- OpenID Connect (OIDC).
Refer to Appendix E for more details.
1.2.4. Designing an API
When designing an API, it is important to perform business process analysis to ensure that API development is business driven rather than technology driven. Technology-driven projects rarely meet customers’ needs in the long run, so it is important to gain background in who could be using the API, and for what. As mentioned previously, co-design is fundamental to driving the right API development. To help identify potential partners to involve in the co-design, consider processes that:
- currently depend on information the API could expose
- require a capability an API could expose.
Some actors will be human (for example, application developers) while some actors will be the systems that will interact with, or depend on, the API. There may be different types of actors, some public sector, some commercial entities, some public.
When representatives for the potential actors are identified, start co-designing with these representatives. First and foremost consider the requirements for the API. Application developers often couch their requirements in terms of how the API should work, rather than what the API needs to do. Do not get bogged down in the variety of proposed solutions from each developer. Focus on extracting the true requirements by performing functional analysis (for example, use cases) and data flow analysis, and then identify resources and work out the granularity (see section 1.2.7. Granularity).
It is especially important that security and information privacy impacts are identified up front and addressed early on in the design process. Assess the information being passed and the types of access different customers and / or consuming applications should have to the API. This will help drive development of security policies alongside the design of the API.
One common pitfall in API design is to map all existing data tables onto resources and develop the associated CRUD (Create, Read, Update and Delete) capabilities into an API. Usually this results in a poor API design and tightly couples the API design to the underlying data structure.
Another common pitfall is to design APIs as an extension to, or way into, monolithic legacy systems. This should be avoided as it tightly couples the API to the legacy system. Both of these pitfalls will create issues for both provider and consumers in the long term.
At this point in the design process, agility is probably more important than completeness. Share early design thoughts and interface specifications with the developer community and quickly make changes in response to their feedback. Work through some sequence diagrams with them to help pin down API interactions and inform API design thinking.
The correct API design will likely not please every API developer, so do not try to be all things to all developers. A rule of thumb is that the API design is probably on the right track if most developers are a little unhappy, but all are able to achieve their aims with the proposed design.
It is important not to try to bypass evolution steps and try to build for all potential use cases right from the offset. Starting simple and focusing on a single channel or interaction initially is a more measured progression towards API delivery. By building onto these simple building blocks, API evolution over time naturally progresses towards omnichannel applications.
1.2.5. Design considerations
The following considerations emerge when looking to develop APIs in the public sector.
- Understand the data, understand the consumers. Before starting work on APIs, gain a good understanding of what data is held, the complexity of that data, its relationships to other data, the downstream dependents on that data, and potential consumers of the data. It is also a good idea to understand the consumers’ drivers and downstream usage or customers.
- Design for REST. If the interaction appears RESTful then develop a REST API, for example, one to many. In some cases, however, a gRPC or GraphQL API may be appropriate and are supported by these guidelines. REST APIs are widely understood, easier to govern and suitable for most interoperability use cases. It is also important to note here that if there is an appropriate supported standard such as Fast Healthcare Interoperability Resource (FHIR) then the resources defined in that standard should be used rather than a set of agency-defined resources.
- The information supplied via an API may still be dependent on overnight batch processes for updates. It is worth making the information’s timeliness and update period clear to potential consumers of the API.
- APIs should not be used to handle large batch transfers because if one single data write fails then the whole batch fails. APIs work better for multiple individual update transactions.
1.2.6. Design-driven development — Required
When building APIs, a design-driven approach must be taken. This includes:
1.2.7. Granularity
There is a question as to how fine-grained an API resource should be. APIs should be designed at the lowest practical level of granularity because this makes each service simpler and allows them to be combined in ways that suit the application developer. The key principle is to design services that can be re-used and combined in different ways, and not tie the application developer to a specific way of working or sequence of API consumption just because that is how it is configured or built in the backend.
If an API offers very fine-grained resources, then consuming applications end up having to make many more calls to the API in order to collect all the information they need, resulting in chattier communications. On the other hand, if the API is based on very coarse-grained resources (returning everything about the resource) the response payloads could end up being enormous, may not support all API application developers’ needs, and the API could become cumbersome to use and maintain.
There may also be a need for varying granularity within one API, depending on the purpose and use of that API. For example, if a blogging API was being created, it may make sense to offer a coarse-grained resource for posting a new blog entry as a bundle of content including pictures, sound, keywords as well as the textual blog entry. But when offering the ability to like or comment on a blog entry it would make sense, for clarity and ease of use, to offer these as separate sub-resources, one for indicating a ‘like’ and one for submitting a comment.
It is important to aim for a granularity that will prevent business logic from leaking into the API, for example, requiring API calls to be made in a certain sequence to meet with internal business processes. With the blogging example, consider the impact of a fine-grained API being offered for posting blog entries, with separate calls for adding images, sounds and metadata. If metadata is mandatory, then the API application developer needs to ensure that every blog post call is followed by a metadata call. This puts the onus on the application developer to get the sequence of calls correct and could lead to inconsistent data if they do not adhere to the rules. It also leads to close coupling between the consuming application and the API, and if the business logic changes in the future there is a downstream impact on all application developers, requiring them to modify their applications.
General guidelines are:
- Do not aim for the finest granularity — an API should be built around each discrete and updateable resource.
- There does not need to be a 1-to-1 mapping between a manual service and a corresponding API. APIs should support the process but not try to encapsulate the process.
- A rough guide is to have one API operation for each entity life cycle state transition.
- Resources can be identified by reviewing a business process and identifying the key entities that underpin the process. Any entities that the organisation manages or maintains throughout its full life cycle will be potential API resources. There is typically one API operation for each entity life cycle state transition.
- There should be only 1 API function for 1 business outcome, for example, change an address.
- Consider using query parameters in URLs for different granularity of resources. For example, ‘/passengers.json’ could return a list of names, while ‘/passengers.json?detail=full’ could return detailed information about each passenger in a list.
1.2.8. Interface specification
An API represents a contract between the provider and the consumer for access to provider resources. Because API consumption is a programmatic exercise, it is important to have a clear definition of what the API offers and how those API resources are to be accessed. This definition is the interface specification.
The interface specification should be designed in advance of developing the API, as the act of working out the specification often helps think through all the issues that could impact the design of the underlying resource-handling code. It also helps consuming application developers to review the capabilities being offered to see if the capabilities meet their needs before they start developing to the specification. When outsourcing API development, the interface specification can be written in abstract as a means of defining the API the vendor should build. The specification can be handled as a separate entity, is version controllable, and it should be possible to use it as the main API documentation or reference.
There are several API modelling languages available for defining the interface specification. Some are proprietary (for example, REST API Modelling Language (RAML), API Blueprint) while others are machine readable but not human readable (for example, Web API Description Language (WADL)).
The most commonly used API modelling language today is an open standard known as the OpenAPI Specification (formerly Swagger) maintained by the Open API Initiative. This initiative is supported by many of the main commercial entities in IT (Google, IBM, Microsoft, Atlassian, Paypal). It offers a standard, programming language-agnostic (JSON / YAML Ain’t Markup Language (YAML)) interface to REST APIs that allows both humans and computers to discover and understand the capabilities of the service.
It is important to use a modelling language to define the API interface specification as essentially it is text or code and can be maintained using source code management systems.
It is recommended to use OpenAPI / Swagger as the interface specification language for all APIs being developed.
1.2.9. Orchestration
As a general rule using APIs as an orchestration tool is not recommended. This is due to the complexity that this can introduce to an API architecture. That said, however, simple orchestration may in some cases be appropriate. An example of this may be a mashup API that creates a new logical API resource by combining related data held in 2 or more backend systems.
A mashup is not appropriate when you create an API that merges 2 distinct API products together. If a consumer requires this functionality the mashup should be performed by the consuming application.
Other examples of simple orchestration could include mediation such as message transformation (JSON to eXtensible Markup Language (XML) or canonical message model transformation).
1.2.10. Software development kits (SDK)
It is recommended that API providers offer an SDK to developers of consuming applications.
An SDK can be considered the implementation toolset for use of an agency’s APIs. By providing developers with an SDK they can build applications faster without having to understand all of the API’s complexities. An SDK should provide sample code that explains the functionality of your API to potential application developers.
An example of a public service API SDK can be found at the US Department of Labor GitHub repository Publication / Catalogue — Github.
Once an API is in sufficient state to be offered to API consumers, the API definition should be published to an API catalogue. The primary API discoverer is the developer, so an external API must be well documented, and provide accurate and up-to-date guidance via the catalogue.
Government are considering options for a central point of discovery for externally accessible public sector APIs.
Publishing to a catalogue is recommended for internal APIs.
Publishing to a catalogue is required for external APIs.
1.3. Architectural and deployment patterns
Table 2 identifies some architectural and deployment patterns that bear further investigation. There are a number of vendor specific references that are also available however these guidelines do not recommend any particular one.
Pattern | Reference |
---|---|
Microservices | Microservices Guide — MartinFowler.com |
API management | Understanding API management, API gateway, and API manager — Nordic APIs |
Observability | |
API publication / discovery | Gathering My Thoughts on API Discovery — API Evangelist |
Hybrid deployments | Making Sense of a Multi-Cloud API Approach — Nordic APIs |
Service mesh |
1.4. HTTP verbs
Access to REST APIs must be via the standard HTTP verbs: GET, PUT, POST, DELETE in line with the W3C Standard.
Verb | Common usage |
---|---|
GET |
|
POST |
|
PUT |
|
DELETE | To delete an existing resource item |
OPTIONS | To retrieve information about what the consumer is allowed to do with the resource |
PATCH | Not recommended due to complexity |
HEAD | Rarely used, but used to retrieve metadata about a resource |
1.5. URI construction
Uniform Resource Identifier (URI) construction is important in that it is the door through which consumers enter to obtain agency resources. It should be intuitive and easy to guess what an endpoint does just by looking at the URI and HTTP verb, without needing to see a query string. As basic guidance, endpoint URLs should advertise resources, and avoid verbs.
In some situations an implemented standard may define the URI structure for you. A good example of this is Resource — FHIR v4.0.1 — Fast Healthcare Interoperability Resources (FHIR).
1.5.1. API offering — Recommended
Is it recommended that agencies make it clear in the URL that their offering is an API:
Example
https://api.example.govt.nz
1.5.2. Version
APIs should have a clear indication of the version, so that application developers can ensure they are using the appropriate version for their consuming application.
Header-based versioning is recommended (see section 1.10. API version control) however, it is recognised that some API infrastructure does not readily support header-based versioning.
URL-based versioning is a viable alternative, as the version number in the URL should only change when major revisions have been made and the interface has changed substantially without backwards compatibility. For URL-based versioning the URI should include /vN with the major version (N) and v as a prefix. Agencies should not include minor version numbers when using version numbers in the API paths.
Template
/v{version}/
Example
#Get details for provider id 123435 – version 1 of the API
GET https://api.example.govt.nz/v1/providers/12345
Accept: application/json
Host: api.example.govt.nz
#Get details for provider id 123435 – version 2 of the API
GET https://api.example.govt.nz/v2/providers/12345
Accept: application/json
Host: api.example.govt.nz
1.5.3. Namespaces
For APIs where an agency holds multiple responsibilities that may result in overlapping resource naming (for example, MBIE could have the resource ‘accommodation’ in 2 contexts: tenancy/contracts, tourism/contracts) it is recommended that namespaces be used to avoid any ambiguity. The namespace would be the first noun in the URI and should reflect the function of government being offered by this API. Namespaces may be singular or plural, depending on the situation.
Template
/{version}/{namespace}/
Example
/v1/biosecurity/
1.5.4. Resources and sub-resources — Recommended
Resource names should be noun-based and collection resource names should be plural nouns, for example, ‘/passengers’ in lower case. Resource naming should be short, simple and clearly understandable. It should also be human-guessable, avoiding technical or specialist terms where possible.
Sub-resources must appear under the resource they relate to, but should go no more than 3 deep, for example, ‘/resource/id/sub-resource/id/sub-sub-resource’. If you reach a third level of granularity (sub-sub-resource), it may be worth reviewing your resource construction to see if it is actually a combination of multiple first or second level resources.
The URI references for resources should consistently use the same path structure to refer to resources. Sub-namespace or sub-folders should be avoided, to maintain path consistency. This allows application developers to have a predictable experience in case they are building URIs in code.
Template
/{version}/{namespace}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
/{version}/{resource}/{resource-id}/{sub-resource}/{sub-resource-id}
Example
https://api.example.govt.nz/v2/biosecurity/trusted-travellers/33245/arrivals/P100782
https://api.example.govt.nz/v2/learners/43265/achievements/7281
1.5.5. Word separation
Hyphens have traditionally been used as word separators in URLs, as search engines (particularly Google) prefer a hyphen to split words because a hyphen is not a word character (as defined in regular expression language). This has led to hyphens, or kebab case, being the de facto standard in the interests of readability and Search Engine Optimisation (SEO).
Therefore, in order to keep URLs consistently formatted, path and query string parameters should be lower case with hyphen separators for multiword names.
Example
https://api.example.govt.nz/v1/example-service/search?sort-order=asc
1.5.6. Query arguments
Query arguments should be used to filter a response, for example, modify a returning result set. The general rule is if it changes the behaviour of the:
- result set, then it should be a query argument
- API, then it should be in the path.
Query arguments are generally used for:
- sorting or ordering the result set — for example, sort-order=ascending
- pagination — pagination is a query argument because it effectively acts as a filter and limits the result set returned. This is particularly useful with large response data sets. When using pagination, it is a good idea to inform the consumer where they can find previous and subsequent result sets using hypermedia (see section 1.7.7. HATEOAS).
Example
"_links": [
{
"rel": "next",
"href": "https://api.example.govt.nz/v1/example-service/search?limit=5&start=5"
},
{
"rel": "prev",
""href": "https://api.example.govt.nz/v1/example-service/search?limit=5&start=0"
},
{
"rel": "self",
"href": "http://api.example.govt.nz/v1/example-service/search"
}
"href": "http://api.example.govt.nz/v1/example-service/search"
]
- limiting the result set, for example, by specifying which fields to return. This approach can be complicated and is often a decision based on functionality versus complexity. For example, it may be desirable to be able to filter a result set to a specific set of objects. While this is possible, it is not a recommended approach. If this kind of flexibility is required in an API it could be a good time to consider the use of Open Data Protocol (OData).
- in cases where response filtering is used, providers should ensure that they use a JSON schema by default so that consumers have the ability to understand the entire resource and do not need to query the resource for the message structure.
Example
#Get a filtered result set for a learner
GET
https://api.example.govt.nz/v2/learners/33245?fields=firstName,lastName,dateOfBirth
{
"firstName": "Mary",
"lastName": "Contrary",
"dateOfBirth": "12-01-1974",
"_links": [
{
"rel": "self",
"href": "https://api.example.govt.nz/v2/learners/33245?fields=firstName,lastName,dateOfBirth"
}
]
}
1.6. HTTP headers
1.6.1. Request headers
Header | Usage | GET | POST | PUT | DELETE |
---|---|---|---|---|---|
Accept | Indicates desired format of the response. If set to a value that cannot be supported by the server, API responds with 406 (Not Acceptable) | Required | Required | Required | N/A |
Content-Type | Indicates the format of the payload provided on the request. If not supported by the server, API responds with 415 (Unsupported Media Type) | N/A | Required | Required | N/A |
Authorisation | To provide authorisation information — type and token, depending on authorisation type. If token is not valid for the request, API responds with 401 (Unauthorized) | Required, unless a public API | Required, unless a public API | Required, unless a public API | Required, unless a public API |
Accept-Encoding | Advertises what compression algorithm the consuming application is able to understand. If encoding not supported by the server, API responds with uncompressed response | Should use | Should use, if response body expected | Should use, if response body expected | N/A |
{API Key Header} Note: there is no set naming standard for an API Key Header |
Send the API keys with every request. If keys are not valid, API response with 401 (Not authorised) | Required, if issued | Required, if issued | Required, if issued | Required, if issued |
If_Modified_Since/If-None-Match | Makes the request conditional —the server will respond with the resource only if the specified condition is met. If condition is not met, API responds with 304 (Not Modified) | Shoud use | N/A | N/A | N/A |
{Request tracking Headers} Note: there is no set naming standard for a transaction ID header |
Unique identifier that can be used to trace a request throughout its life cycle | Shoud use | Shoud use | Shoud use | Shoud use |
1.6.2. Response headers
Response Header | Usage | GET | POST | PUT | DELETE |
---|---|---|---|---|---|
Content-Type | Indicates the format type of the response | Required | Required, if response body returned | Required, if response body returned | N/A |
Location | Indicates the absolute URI of the newly created resource item | Required for 302 redirect | Should use, if resource created | N/A | N/A |
Content-Location | Indicates the absolute URI of the requested resource | Should use | Should use, if resource returned | Should use | N/A |
Cache-Control | Directives to control caching behaviour external to the API layer (for example, Content Delivery Network (CDN) caching) | Should use | Should use | Should use | N/A |
Expires | Used in conjunction with Cache-Control for backwards compatibility | Should use | Should use | Should use | Should use |
ETag | Concurrency control header | Should use | Should use | Should use | N/A |
1.6.3. Customer X-HTTP headers
X-notation headers have been deprecated as per Request for Comments (RFC) 6648 and should not be used. This standard appreciates that X-notation headers are widely used however this guideline recommends that where an agency is defining a custom header the X-notation should not be used and the agency defines a custom header notation that is relevant. For example, X-Request-Id should be redefined as Request-Id.
RFC 6648 — Internet Engineering Task Force (IETF)
1.7. Returned content
The returned content is the body of the message that the API returns in response to a request. This usually relates to the resource that was requested.
1.7.1. Resource scope
The amount of data to return about a resource depends on several factors:
- Business context — does all the resource information being returned have relevance to the business process or activity consumers will use it for?
- Payload size and network efficiency — how much information does the resource contain, and is it so bulky it could impact network performance?
- Usefulness — is the information in the returned content always useful to the consumer, or is it something they may need once but never again?
Analysis of the full resource data against these factors should help determine how much resource data is returned in the response.
1.7.2. Formats
REST responses should be of a format that is both human and machine readable. Even if your initial consumer is via business to business (B2B), the API should be designed, where possible, for all potential consumers. REST APIs should, by default, return content in JSON format. But it is good practice to support multiple message formats (for example, XML, Multi-part Multipurpose Internet Mail Extensions [MIME]) and allow consuming applications to request the format they wish to consume (in line with the design for the consumer principle in section 2.1. Design for the consumer of Part A: API concepts and management 2022).
The response format required from a GET request must be indicated by the consumer in their request using the Accept header.
Example
#GET a resource and specify the response type
https://api.example.govt.nz/crown-property/0219c539-5885-4ab6-a55b-b0de7537c426
Accept: application/xml
Example
#Response
0219c539-5885-4ab6-a55b-b0de7537c426
commercial
Northbound Boulevard
Wellington
6011
NZ
annual
NZD
1000
squareMetre
rent
office
183
squareMetre
188
squareMetre
Free text describing the property in some way.
tennanted
2016-06-01
The request format for a request containing a request body (POST, PUT, PATCH) must be supplied in the Content-Type request header.
Example
#PUT (update) a resource
https://api.example.govt.nz/crown-property
Content-Type: application/json
Accept: application/json
#Request BODY
{
"id": "0219c539-5885-4ab6-a55b-b0de7537c426",
"category": "commercial",
"location": {
"streetName": "Northbound Boulevard",
"town": "Wellington",
"postalCode": "6011",
"countryCode": "NZ"
},
"pricing": {
"rentFrequency": "annual",
"currencyCode": "NZD",
"pricePerUnitArea": {
"price": 1000,
"units": "squareMetre"
},
"transactionType": "rent"
},
"property_type": "office",
"areas": {
"internal": {
"minimum": {
"value": 183,
"units": "squareMetre"
},
"maximum": {
"value": 188,
"units": "squareMetre"
}
}
},
"detailedDescription": [
{
"text": "Free text describing the property in some way."
}
],
"tennancy": {
"life_cycle_status": "vacant",
"nextReviewDate": "2016-07-01"
}
}
For a good example of this in action, see the tabbed format options for presenting patient information on the FHIR site.
Where JSON format is used in responses, it must conform to the JSON Standard (RFC 7159 — IETF). In line with this standard, textual content should be Unicode Transformation Format-8 (UTF-8) encoded by default, and any deviations from this must be limited.
It is inadvisable to use APIs for returning binary data such as images in the response content — hyperlinks to images are the preferred returned response. But where images are returned, ensure that the image encoding is MIME, HTTP Multipart.
Hypertext Markup Language (HTML) is allowed but is less flexible for consumer usage (for example, how can it be consumed by a native mobile app?), ties content to presentation and loses its benefits when building a single page application. It also reduces the ability to filter the content in transit for consumers who have fine-grained control on what they can access. However, it may be necessary for some forms of API like APIs with geospatial content.
Avoid using bespoke formats in returned content.
Recommended
Returned content format should be JSON by default.
Required
Requests for a specific return format must be defined in the Accept header.
1.7.3. Layout
Responses should be a JSON object (not an array) by default. Using an array to return results limits the ability to include metadata about results and limits the API’s ability to add additional top-level properties in the future. Do not use unpredictable properties. Parsing a JSON response where properties are unpredictable (for example, derived from data) is difficult, and adds friction for clients.
Example of good layout
{
"response_meatadata": {
"item1": "value1",
"item2": "value2"
},
"responseArray": [
{
"name": "entity1",
"type": "good"
},
{
"name": "entity2",
"type": "good"
}
]
}
Example of bad layout
[
{
"name": "entity1",
"type": "bad"
},
{
"name": "entity2",
"type": "bad"
}
]
1.7.4. JSON property names
Within content, property names should conform to the following guidelines:
- Property names should be meaningful names with defined semantics.
- Property names must be camel-case American Standard Code for Information Interchange (ASCII) strings.
- The first character must be a letter or underscore (_).
- Subsequent characters can be a letter, a digit or underscore.
- Reserved JavaScript keywords should be avoided.
1.7.5. Consistency
Preserve backwards compatibility with existing consumers of the API by returning expected fields and employing sensible default values for missing fields. Keep consistency of terminology throughout, so that the consumer is not misled. Avoid modifying the semantics of content to new meanings, for example, do not change a ‘title’ field from being used for the title of a page to meaning a person’s job title.
1.7.6. Singletons versus collections
Some API GET calls will return a single resource item (for example, GET https://api.example.govt.nz/passengers/12345) whereas some calls will return a collection of resource items (for example, GET https://api.example.govt.nz/passengers), and the handling and error treatment can be different.
A single item will generally consist of all the pertinent fundamental details about the resource, and the GET will return these fundamental details.
Example
#GET a single passenger resource by ID
GET https://api.example.govt.nz/passengers/12345
Accept: application/json,version=1.*
{
“id”: 12345,
“names”: {
“firstName”: “John”,
“middleName”: “Howard”,
“lastName”: “Doe”,
“preferredName”: “Howie”,
“title”: “Mr”
},
“addresses”: [
{
“type”: “home”,
“address1”: “1 Some Street”,
“address2”: “Some Suburb”,
“address3”: “Some City”,
“address4”: null,
“country”: “New Zealand”,
“postcode”: 1111
},
{
“type”: “business”,
“address1”: “2 Work Street”,
“address2”: “Work Suburb”,
“address3”: “Work City”,
“address4”: null,
“country”: “New Zealand”,
“postcode”: 2222
}
]
}
If passenger 12345 did not exist, the HTTP return code would be 404.
If a requested sub resource did not exist — for example, addresses for passenger 54321: GET https://api.example.govt.nz/passengers/54321/addresses an HTTP 404 response would not be valid. The correct response would contain an empty array:
Example
#GET a single passenger address resource by ID
GET https://api.example.govt.nz/passengers/54321/addresses
Accept: application/json,version=1.*
{
“id”: 54321,
“addresses”: [
{
“type”: null,
“address1”: null,
“address2”: null,
“address3”: null,
“address4”: null,
“country”: null,
“postcode”: null
}
]
}
A collection will take the form of an array of the given resources, including any related metadata. The collection returned should be the complete set, leaving it to the consumer to reduce the quantity of information returned by sending filters in the resource request (see section 1.5.6. Query arguments).
The only reason the collection should be filtered by the API provider is based on the security context — the API consumer should only gain access to what that consumer is allowed to see. It is advisable to aim for consistent naming of collection resource fields, as this enables application developers to create generic handling of the data from across various resource collections offered by the API. Collection resources should not contain binary attachments or other content that will lead to large response payloads.
Note: The example below is contrived and it would be rare to expose a resource collection as large as passengers.
Example
#GET a collection of passenger resources
GET https://api.example.govt.nz/passengers
Accept: application/json,version=1.*
{
“passengers”: [
{
“id”: 12345,
“names”: {
“firstName”: “John”,
“middleName”: “Howard”,
“lastName”: “Doe”,
“salutation”: “Mr John Doe”,
“title”: “Mr”
}
},
{
“id”: 98765,
“names”: {
“firstName”: “Mary”,
“middleName”: “Pauline”,
“lastName”: “Smith”,
“salutation”: “Ms Mary Smith”,
“title”: “Ms”
}
},
{
“…..lots more passengers here…..”: “…”
}
],
“_links”: [
{
“rel”: “12345”,
“href”: “https://api.example.govt.nz/passengers/12345”
},
{
“rel”: “98765”,
“href”: “https://api.example.govt.nz/passengers/98765”
},
{
“…..lots more links here…..”: “…”
}
]
}
If there were no results from a query against a filter— for example, passengers booked on a specific flight (GET https://api.example.govt.nz/passengers/flights/1234), the return value would be a 200 response code, because the query is not invalid and there may be such data in the future.
1.7.7. HATEOAS
Hypermedia as the Engine of Application State (HATEOAS) is the principle of not just returning a response to a request, but also returning links to other useful capabilities. So, if a POST has just been used to create an item, the response could return links to options to update the item, delete the item and view other items of the same type. This makes it possible for user interfaces (UIs) that utilise an API to be automatically generated (in terms of hyperlinks) and adaptable to change (response content links).
If a web page effectively captures a single state in time, then hyperlinks are transitions to other states. The HATEOAS principle suggests that REST should return not only response content (current state) but also links to transitions to other states. This could result in thinner clients and fatter APIs, and means UIs need to be built to adapt to potential new transitions, but makes it possible to update all UIs no matter what platform they sit on (mobile app, web application).
For example, consider the following representation of a person and sequence of requests.
Note: This example assumes that an address is a unique resource in its own right and that people are associated with addresses. (This may not always be a practical application and the example is used just to illustrate the hypermedia concept.)
-
User submits a request for a person record:
# Request person details:
Example
GET https://api.example.govt.nz/people/c70d37cf-3314-4a24-a2c3-0fca7ec0943f
Host: www.example.com
Accept: application/json;q=1.0# Response with person details:
Example
HTTP/1.1 200 OK
Content-Location: https://api.example.govt.nz/people/c70d37cf-3314-4a24-a2c3-0fca7ec0943f
Content-Type: application/json;charset=UTF-8
Cache-control: max-age=3600,must-revalidate
{
"personId": "d9e1a2f6-6bc9-46af-9dbb-50de7f6eacd5",
"name": "Joe Bloggs",
"email": "joe.bloggs@example.com",
"addresses": [
{
"id": "5046b8f2-e8aa-4ec9-a597-34d01af0fd32",
"streetNumber": "12",
"streetName": "Somewhere Street",
"City": "Springfield",
"type": "home"
},
{
"id": "18a1a85c-3b1e-41b9-909c-668f2a803b69",
"streetNumber": "1",
"streetName": "Some Other Street",
"City": "Springfield",
"type": "business"
}
],
"_links": [
{
"rel": "self",
"href": "https://api.example.govt.nz/people/d9e1a2f6-6bc9-46af-9dbb-50de7f6eacd5"
},
{
"rel": "address1",
"href": "https://api.example.govt.nz/addresses/046b8f2-e8aa-4ec9-a597-34d01af0fd32"
},
{
"rel": "address2",
"href": "https://api.example.govt.nz/addresses/18a1a85c-3b1e-41b9-909c-668f2a803b69"
}
]
} -
The user then updates the address details changing the street number:
# Request to update the address details:
Example
PUT https://api.example.govt.nz/addresses/d9e1a2f6-6bc9-46af-9dbb-50de7f6eacd5 HTTP/1.1
Host: api.example.govt.nz
Content-Type: application/json
{
"streetNumber": "12a",
"streetName": "Somewhere Street",
"City": "Springfield"
}# Response that the update was successful:
Example
HTTP/1.1 204 No Content
Location: https://api.example.govt.nz/addresses/046b8f2-e8aa-4ec9-a597-34d01af0fd32If the user were now to re-fetch the person details again as per step 1, the representation would contain a stale version of the address (street number 12 instead of 12a) because the address representation had been cached. This caching could occur anywhere along the network path (including public internet) between the server and client.
Using hypermedia links for related resources improves the visibility of the API and avoids these types of problems. Consider the revised person representation below:
# Response that the update was successful:
Example
{
"personId": "d9e1a2f6-6bc9-46af-9dbb-50de7f6eacd5",
"name": "JoeBloggs",
"email": "joe.bloggs@example.com",
"addresses": [
{
"rel": "self",
"href": "https://api.example.govt.nz/addresses/5046b8f2-e8aa-4ec9-a597-34d01af0fd32"
},
{
"rel": "self",
"href": "https://api.example.govt.nz/addresses/18a1a85c-3b1e-41b9-909c-668f2a803b69"
}
],
"_links": [
{
"rel": "self",
"href": "https://api.example.govt.nz/people/d9e1a2f6-6bc9-46af-9dbb-50de7f6eacd5"
}
]
}
Using this approach, the embedded object is replaced with a link to the ‘real’ object. Although this approach requires a larger number of network requests, it avoids the stale data problems and reduces payload size, which is an important consideration for APIs being used by mobile devices or over high latency networks.
1.8. Consideration of state
In almost all cases a REST API should be entirely stateless. As part of processing, it is possible for an API to gather context and pass this to a downstream system, however an API should not maintain that context for future requests. This should not be confused with caching, as discussed in section 1.12. Caching.
1.9. Batch handling and transactions
APIs are not designed for large payloads like batch handling for retrieving or uploading batches of data. APIs are geared towards stateless, usually synchronous, web-like requests for individual discrete data transactions. However, batch handling can be achieved through bundling multiple calls to the same API. This helps achieve atomicity of transactions and aids recoverability in event of errors.
When handling transactions, it is important to consider the troubleshooting and recovery aspects of error handling. This includes visibility of transaction progress and the ability to perform root cause analysis. To achieve this, it is advisable that logging is performed on all transactions coming through an API, with accurate timestamping, so that monitoring tools can visualise transaction progress. It also requires transaction identifiers (see section 1.6.1. Request headers) to be built into transactional API calls to ensure the transaction is traceable end to end.
In some cases, it may be deemed appropriate to provide an asynchronous batch type capability using an API. This would usually be implemented in a scenario where legacy system impact is a concern. An example of this could be a bulk creation of person records in a database based on a batch event in a consuming legacy application.
In such a scenario it would be preferable for the consuming application to treat each person as a unique event and to POST to a person API for each new unique resource. While this may seem like an unnecessary overhead, by individualising the transactions each success or error scenario can be handled in its own right, and the consumer can be informed of their transaction status.
If this is not possible, due to some system restriction, it is possible to asynchronously POST multiple person details to an API. This type of interaction should not be attempted synchronously as large batches will tie up HTTP threads and in some cases require client / server timeout control.
Example of a singleton create person request
{
"names": {
"firstName": "John",
"middleName": "Howard",
"lastName": "Doe",
"salutation": "Mr John Doe",
"title": "Mr"
}
}
Example of a bulk person create API call
#POST a collection of person resources
POST https://api.example.govt.nz/people
Accept: application/json,version=1.*
{
"people": [
{
"names": {
"firstName": "John",
"middleName": "Howard",
"lastName": "Doe",
"salutation": "Mr John Doe",
"title": "Mr"
}
},
{
"names": {
"firstName": "Mary",
"middleName": "Pauline",
"lastName": "Smith",
"salutation": "Ms Mary Smith",
"title": "Ms"
}
},
{
".....lots more people here.....": "..."
}
]
}
Example response
200 OK
Note that the API is unable to respond with headers such as Location as this request should be treated as asynchronous.
1.10. API version control
Learn more about the commonly-used types of versioning when developing an API.
1.10.1. API version control methods
There are 2 main API versioning methodologies commonly used. There are positives and negatives to both approaches and a large amount of debate over which is the most ‘RESTful’. Rather than stipulate a methodology to use in these guidelines, each methodology is described below in order of preference. Both, however, are acceptable. What is more important is that APIs are versioned and that there is an understanding of when and why to version an API.
1.10.2. When to version
Simply put, an API should be versioned when a change is considered a breaking change. One of the benefits of an API is that, if it is well designed, there should be fewer breaking changes. In government however, there are likely to be situations where legislative changes enforce a new version of an API and deprecation of all previous versions.
1.10.2.1. Breaking changes
A change is a breaking change if any consuming application requires changes to work with the new version. In other words, the new version will not successfully process messages provided by existing consumers. A breaking change should be considered as a major version change, for example, 1.3 to 2.0.
Examples of breaking changes include:
- the removal of any property from the response representation
- the change of datatype for an existing property or a change from optional to required
- the removal of any resource or HTTP Verb support
- a change to the way errors are handled
- any change to existing resource URIs.
1.10.2.2. Non-breaking changes
A change is a non-breaking change if any message that would have been processed by the previous version will be successfully processed by the new version (that is, backwards compatible). This will enable an existing consumer of the previous version to work with the new version without requiring modification. A non-breaking change should be considered as a minor version change, for example, 1.1 to 1.2.
Such changes include:
- the addition of new properties to the JSON representation
- the addition of new resources
- the addition of support for new HTTP Verbs (new operations) on existing resources
- support for new custom headers, for example, for request tracing.
1.10.3. Software configuration management
It is important to remember that version control is more than just versioning the resource. An API will inherently have associated code and artefacts. Consider what comprises an API and include these as a logical artefact stored and managed in a software configuration management (SCM) system.
It is a good idea to try and capture all components in a format that can be controlled by SCM. A good example of this is Unified Modeling Language (UML) diagrams — these cannot be version-controlled unless captured in a text-like format. For examples of textual UML modelling, see:
An API artefact should, where possible, comprise the following elements:
- any API code
- API specification (OpenAPI)
- unit test scripts such as JUnit or Mocha
- continuous integration support files such as grunt.js or Ant scripts.
1.11. Search
Search should be implemented using GET requests with filters provided as query string parameters.
Search can be used by API consumers to filter or sort information they want from an API.
For instance, ‘GET /groups/search?status=active’ would enable the consumer to filter the groups resource on status.
Alternatively, ‘GET /groups/search?status=active&sort=status,name’ enables the consumer to return active groups ordered by status and name. It can also be useful to allow the consumer to choose which fields they want back from the API, so they only receive the content they need in the API response.
Example
#Get a list of groups that are either active or inactive, sorted by
#name and status
GET https://api.example.govt.nz/groups/search?status=active,inactive&sort=name,status
{
"groups": [
{
"id": "0219c539-5885-4ab6-a55b-b0de7537c426",
"name": "API lovers",
"status": "active"
},
{
"id": "1219c539-5885-4ab6-a55b-b0de7537c427",
"name": "Developers",
"status": "active"
},
{
"id": "7219c539-5885-4ab6-a55b-b0de7537c42c",
"name": "Risk analysts",
"status": "active"
},
{
"id": "3219c539-5885-4ab6-a55b-b0de7537c42h",
"name": "Writers",
"status": "active"
},
{
"id": "g219c539-5885-4ab6-a55b-b0de7537c425",
"name": "Authors",
"status": "inactive"
}
],
"_links": [
{
"rel": "self",
"href": "https://api.example.govt.nz/groups?sort=status,name"
},
{
"rel": "API Lovers",
"href": "https://api.example.govt.nz/groups/0219c539-5885-4ab6-a55b-b0de7537c426"
},
{
"rel": "Developers",
"href": "https://api.example.govt.nz/groups/1219c539-5885-4ab6-a55b-b0de7537c427"
},
{
"rel": "Risk analysts",
"href": "https://api.example.govt.nz/groups/7219c539-5885-4ab6-a55b-b0de7537c42c"
},
{
"rel": "Writers",
"href": "https://api.example.govt.nz/groups/3219c539-5885-4ab6-a55b-b0de7537c42h"
},
{
"rel": "Authors",
"href": "https://api.example.govt.nz/groups/g219c539-5885-4ab6-a55b-b0de7537c425"
}
]
}
In rare circumstances it is possible that an extremely complex search API may run into query string or URL length limitations, although this is probably an indication that the design of the search operation is too complicated. The exact length limit is server- and client-dependent. For example, the Apache server has a default limit of 8KB, Microsoft Information Internet Services (IIS) has a default of 16KB, and many browsers are limited to around 2KB maximum URL length.
Where the total length can potentially exceed 2KB, the only option is to implement the search operation using the POST verb with an appropriately designed request object to contain the search parameters.
Example template
POST /{version}/{namespace}/{search-resource}
Example request
POST /v1/people/person-search?page=5 HTTP/1.1Host: www.example.com
Content-Type: application/json
{
"startDateBefore":"2010-01-01",
"position": "Manager",
"businessUnit": "Operations",
. . . many other search parameters . . .
}
Example response
HTTP/1.1 200 OK
Content-Type: application/json
{
"pageSize": 10,
"page": 5,
"totalItems": 77,
"totalPages": 8,
"items": [
. . . lots of people objects here . . .
]
"_links": [
{
"rel": "first",
"href": "https://www.example.com/v1/people/person-search?page=1"
},
{
"rel": "prev",
"href": " https://www.example.com/v1/people/person-search?page=4"
},
{
"rel": "next",
"href": " https://www.example.com/v1/people/person-search?page=6"
},
{
"rel": "last",
"href": " https://www.example.com/v1/people/person-search?page=8"
},
]
}
Other considerations include special characters or filters that add additional complexity due to URL encoding requirements. For example, the search function might need to support ‘<’ or ‘>’ operators, or special characters for non-English language support.
Additionally, SQL words should be avoided, as common threat detection filters may block requests containing words such as ‘DROP’, ‘ALTER’ or ‘DELETE’ in case they are SQL injection attacks. For more details, see the following Open Web Application Security Project (OWASP) cheat sheets:
- Cross-Site Scripting (XSS) Prevention Cheat Sheet — OWASP
- SQL Injection Prevention Cheat Sheet — OWASP
Paging behaviour for search results should be consistent with the interaction as described in section 1.5.6. Query arguments. Pagination can be implemented as a page number and page size, offset and limit, or continuation token, depending on the scale and changeability of the search results.
Note: It is worth pointing out that since the HTTP protocol treats POST operations as unsafe, the result is not cacheable. This applies even if the response has a Cache-Control header included, so consider the performance impacts on the API if this is a high-volume operation.
1.12. Caching
Caching enables faster responses from APIs and reduces server load. It is good for information that is frequently requested but does not change very often. The aim is to retrieve information once but re-use many times.
There are a number of caching methodologies available. This section focuses on 2 types of caching that should be owned and controlled by the provider, primarily for APIs.
1.12.1. Response cache
A response cache is a cache that contains the response to a GET request on a unique resource. The response should be cached as close to the consumer as possible while remaining inside the control boundaries of the agency. Response caches are usually fast, in-memory caches. Many off-the-shelf API gateway / management solutions provide a built-in response cache.
When using a response cache, agencies should ensure that they understand and monitor their cache to ensure that stale objects are kept to a minimum and that they have sufficient system memory to service caching loads. This means ensuring that the cache is refreshed once a user has updated the information or a cache timeout has occurred.
Consider the example in figure 3 of a response cache.
1.12.2. Object cache
An object cache is used to cache objects that are fundamental to the function of an API but may not change on a regular basis. For example, an API may include a tax code validation step. Tax code references are held in a backend database. To prevent redundant requests to the database, the API layer could cache tax codes in its object cache and refer to these until the cache becomes invalid. Figure 4 details an example sequence.
1.13. Error handling
Learn more about how important errors in the development of API are being handled.
Error handling is important because API consumers see the API as a black box, and when an error occurs they need to know how to handle it. Therefore error responses need to be informative yet avoid information leakage about the internals of the backend system. Errors should be handled in both a human- and machine-consumable way. When an error occurs, the response body should contain:
- the HTTP status code
- an API-specific error code, which API support staff will be able to look up to identify what has gone wrong
- a human readable error message (possibly including selective technical details if the API is a development / test release for developer consumption only).
1.13.1. HTTP status codes
RESTful APIs should always use the standard HTTP error responses when an error occurs, but different codes may be pertinent depending on the HTTP verb being used and the quantity of data being retrieved (for example, single item versus list / collection of data).
For a tutorial, see HTTP Status Codes — REST API Tutorial
The following table gives an example of good practice use of HTTP error codes.
Code | Meaning | Description |
---|---|---|
GET response status codes | ||
200 | OK | The request was successful, and the response body contains the representation requested. |
302 | FOUND | A common redirect response — you can GET the representation at the URI in the Location response header. |
304 | NOT MODIFIED | Your client’s cached version of the representation is still up to date. |
401 | UNAUTHORISED | The supplied credentials, if any, are not sufficient to access the resource. |
404 | NOT FOUND | The requested representation was not found. |
429 | TOO MANY REQUESTS | Your application is sending too many simultaneous requests. |
500 | SERVER ERROR | An internal server error prevented return of the representation response. |
503 | SERVICE UNAVAILABLE | We are temporarily unable to return the representation. Please wait and try again later. |
POST or PUT response status codes | ||
200 | OK | The request was successful, and the resource was updated. The response body contains the updated representation. |
201 | CREATED | The request was successful, a new resource was created, and the response body contains the representation. |
400 | BAD REQUEST | The data given in the POST or PUT failed validation. Inspect the response body for details. |
401 | UNAUTHORISED | The supplied credentials, if any, are not sufficient to create or update the resource. |
404 | NOT FOUND | The representation was not found. |
405 | METHOD NOT ALLOWED | You cannot POST or PUT to the resource. |
429 | TOO MANY REQUESTS | Your application is sending too many simultaneous requests. |
500 | SERVER ERROR | We could not create or update the resource. Please try again later. |
DELETE response status codes | ||
204 | OK | The request was successful. The resource was deleted. |
401 | UNAUTHORISED | The supplied credentials, if any, are not sufficient to delete the resource. |
404 | NOT FOUND | The representation was not found. |
405 | METHOD NOT ALLOWED | You cannot DELETE the resource. |
429 | TOO MANY REQUESTS | Your application is sending too many simultaneous requests. |
500 | SERVER ERROR | We could not delete the resource. Please try again later. |
501 | NOT IMPLEMENTED | The HTTP method is not supported by the server and cannot be handled. |
Note: Where a consumer attempts to call a non-existent API end point, respond with a ‘501 Not Implemented’ status code.
1.13.2. API-specific error code
Error responses need to supply enough information to allow the API consumer to react appropriately to the error, yet not give away too much. But when a critical error occurs it is important for the API provider to be able to trace the root cause and fix it as soon as possible.
One way of achieving this is to return an API-specific error code in the response to the consumer. When the consumer reports the error to the API support team, the consumer can relate the associated API-specific code. The support team can then look this code up and ascertain exactly what has gone wrong and who needs to address it.
1.13.3. Human-readable error message
The human readable error message should be as informative as is useful to an end customer, without offering too many technical details (for example, “An account with this ID already exists.”).
It is important to avoid revealing system information in the human readable response, such as composition of the backend system like component names, as this informs malicious consumers as to what vulnerabilities and back doors to look for. It is also important not to confirm or deny sensitive information, such as username in a username / password combination, as this informs potential attackers which criteria they have got correct.
A07: 2022 Identification and Authentication Failures — OWASP
In non-production instances of the API it may be appropriate to offer more verbose error messages with some technical detail (for example, “Body should be a JSON object.”). However, it is still recommended not to reveal too much about the internal systems that underpin the API, as non-production systems still reflect a lot of the composition of production.
Example
{
"errors": [
{
"code": 12345,
"description": "There has been an error - retrying the request will not succeed. Please contact support"
},
{
"code": 98765,
"description": "A more detailed description of the error if appropriate"
}
],
"_links": [
{
"href": "https://support.example.govt.nz",
"rel": "support"
}
]
}
2. API governance
API governance helps save time and money because it ensures that APIs are built proactively to achieve specific goals and bring value to the agency.
API governance also helps agencies make intelligent decisions regarding API programmes and establish best practices for building, deploying and consuming APIs.
API governance should include:
- specification and resource definitions — governing contracts and resource definitions help improve the consistency and reusability of APIs
- style and pattern guidelines — standardising API design ensures APIs remain consistent and improve usability
- automation — establishing guardrails to make it easier to comply with policies than not (for example, API scaffolding that automatically generates security and audit componentry for APIs or build pipelines that enforce security policies, such as coding standards and security scans)
- life cycle — governing the life cycle of an API, from publication to versioning and deprecation to retirement
- tracking and analytics — keeping track of where APIs are deployed, who is using them and how they are being used (this information helps inform other aspects of API governance)
- a robust code review process — ensuring that your development teams understand the concepts laid out in these guidelines, and establishing a practice where collaborative code reviews are performed (it is recommended that software is developed using a branch strategy with at least one peer review required prior to code merge and deployment).
When your API designers and developers are on the same page regarding APIs, the more efficient, valuable and successful your API programmes will be. Governance is especially beneficial for agencies that have a large API programme or microservices architectures.
For more information, see API Governance: How Important Is It for API Strategy? — NORDIC APIs.
3. Appendix A — HTTP verbs — Required
3.1. GET
GET is used:
- for retrieval of information
- if the interaction is more like a question, that is, it is a safe operation such as a query.
The HTTP GET method is used to read (or retrieve) a representation of a resource. It can be used to retrieve information about an individual resource (for example, ‘customers/123456’) or to retrieve a collection (list) of resources (for example, customers).
In the ‘happy’ (non-error) path, GET returns a representation in JSON and an HTTP response code of 200 (OK). In an error case, it most often returns a 404 (NOT FOUND) or 400 (BAD REQUEST) if the requested resource does not exist.
According to the HTTP specification, GET (along with HEAD) requests are used only to read data and not change it. Therefore, they are considered safe when used this way. That is, they can be called without risk of data modification or corruption — calling it once has the same effect as calling it 10 times. GET is idempotent, which means that making multiple identical requests ends up having the same result as a single request. Do not expose unsafe operations via GET — it should never modify any resources on the server.
3.1.1.Examples
Example
GET https://api.example.govt.nz/passengers/12345
Accept: application/json,version=1.*
Accept-Encoding: gzip,deflate
Host: api.example.com
Example
#Success Response
Content-Length: 612
Location: https://api.example.govt.nz/passengers/12345
Date: Fri Jul 08 16:52:21 NZST 2016
CorrelationID: 65347f577ade5f7750234abb
Transaction-Id: 65347f577bde5949ac7bd448
{
"id": 12345
"names": {
"firstName": "John",
"middleName": "Howard",
"lastName": "Doe",
"salutation": "Mr John Doe",
"title": "Mr"
},
"addresses": [
{
"type": "home",
"address1": "1 Some Street",
"address2": "Some Suburb",
"address3": "Some City",
"address4": null,
"country": "New Zealand",
"postcode": 1111
},
{
"type": "business",
"address1": "2 Work Street",
"address2": "Work Suburb",
"address3": "Work City",
"address4": null,
"country": "New Zealand",
"postcode": 2222
}
]
}
Example
GET https://api.example.govt.nz/passengers/12345/flights
GET https://api.example.govt.nz/schools/98765/learners
Some API GET calls will return a single resource item while some calls will return a collection of resource items. See section 1.7.6. Singletons versus collections for more details.
3.2. POST
POST is used:
- to create a piece of information or resource item
- to spawn an action
- if the interaction is like placing an order
- if the interaction changes the state of the resource in a way that the user would perceive (for example, a subscription to a service)
- if the user is held accountable for the results of the interaction.
The POST verb is most often utilised to create new resources. In particular, it is used to create sub-resources, that is, subordinate to some other (for example, parent) resource. When creating a new resource by a POST to the parent, the API takes care of associating the new resource with the parent, assigning an ID (new resource URI) and so on. On successful creation, POST returns an HTTP status 201 (CREATED), with a Location header containing a link to the newly created resource.
POST is neither safe nor idempotent. It is therefore recommended for non-idempotent resource requests. Making 2 identical POST requests will most likely result in 2 resources containing the same information.
The body content of a POST request will differ to that of a PUT, as the provider will create certain values such as a unique identifier for the resource and its creation date.
In many cases a POST may have a small subset of the data that can exist on a resource — a subsequent PUT to the resource can be performed to populate the data.
3.2.1. Examples
Example
#Create a new passenger resource
POST https://api.example.govt.nz/passengers
{
"names": {
"firstName": "John",
"middleName": "Howard",
"lastName": "Doe",
"salutation": "Mr John Doe",
"title": "Mr"
},
"addresses": [
{
"type": "home",
"address1": "1 Some Street",
"address2": "Some Suburb",
"address3": "Some City",
"address4": null,
"country": "New Zealand",
"postcode": 1111
},
{
"type": "business",
"address1": "2 Work Street",
"address2": "Work Suburb",
"address3": "Work City",
"address4": null,
"country": "New Zealand",
"postcode": 2222
}
]
}
Example
#Successful resource creation response
HTTP/1.1 201 Created
Location: https://api.example.govt.nz/passengers/12345
Content-Location: https://api.example.govt.nz/passengers/12345
Content-Type: application/json
{
"id": 12345,
"names": {
"firstName": "John",
"middleName": "Howard",
"lastName": "Doe",
"salutation": "Mr John Doe",
"title": "Mr"
},
"addresses": [
{
"type": "home",
"address1": "1 Some Street",
"address2": "Some Suburb",
"address3": "Some City",
"address4": null,
"country": "New Zealand",
"postcode": 1111
},
{
"type": "business",
"address1": "2 Work Street",
"address2": "Work Suburb",
"address3": "Work City",
"address4": null,
"country": "New Zealand",
"postcode": 2222
}
]
}
3.3. PUT
PUT is used to :
- update an existing resource item
- replace an existing resource item.
PUT is most often utilised to update information, PUT-ing to a known resource URI with the request body containing the newly updated representation of the original resource.
However, PUT can also be used to create a resource in the case where the resource ID is chosen by the consumer instead of by the API itself: where PUT is to a URI that contains the value of a non-existent resource ID. Again, the request body contains a resource representation. This method of creation should be avoided, if possible, or used sparingly. If consumer-defined resource IDs are required, use POST to create the new resource and provide the consumer-defined ID in the body representation (see section 3.2. POST).
On a successful update, PUT returns HTTP status 200 (or 204 if not returning any content in the body). If using PUT to create, PUT returns HTTP status 201 on successful creation. Content in the body in the response is optional. It is not necessary to return a link via a Location header in the creation case since the consumer already knows the resource ID, having set it themselves.
PUT is not a safe operation — it modifies (or creates) state on the server, but it is idempotent. That is, if you create or update a resource using PUT and then make that same call again, the resource is still there and still has the same state as it did with the first call. However, were the PUT call to increment a counter within the resource, the call is no longer idempotent. Sometimes this is necessary behaviour, and it may be enough to document that the call is not idempotent. However, it’s recommended to keep PUT requests idempotent. It is strongly recommended to use POST for non-idempotent requests.
It is good practice to perform a GET on a resource before you perform a PUT. This means that the consuming application has the latest representation of that resource. PUT should contain the entire resource content in the message body. By performing a GET prior to the PUT the consuming application can re-send the results of the GET with any modifications.
When using a resource response cache, any PUT to a resource should invalidate that cache or re-populate the cache with the updated resource.
3.3.1. Examples
Example
# Get a resource
GET https://api.example.govt.nz/passengers/12345/1w3r4y6u
# The response can be cached in a response cache with a cache key of the resource — for example, cachekey__12345_1w3r4y6u
# Response Body
{
"id": "1w3r4y6u",
"passenger_name": "SMITH/NICOLAS",
"pnr_number": "CG4X7U",
"travel_class": "business",
"seat": "74J",
"auxiliary_fields": [
{
"label": "Terminal",
"value": "T1"
},
{
"label": "Departure",
"value": "30OCT 19:05"
}
],
"secondary_fields": [
{
"label": "Boarding",
"value": "18:30"
},
{
"label": "Gate",
"value": "D57"
},
{
"label": "Seat",
"value": "74J"
},
{
"label": "Sec.Nr.",
"value": "003"
}
],
"flight_info": {
"flight_number": "KL0642",
"departure_airport": {
"airport_code": "JFK",
"city": "New York",
"terminal": "T1",
"gate": "D57"
},
"arrival_airport": {
"airport_code": "AMS",
"city": "Amsterdam"
},
"flight_schedule": {
"departure_time": "2016-01-02T19:05",
"arrival_time": "2016-01-05T17:30"
}
}
}
Example
# Put to a resource (update) changing the seat number
PUT https://api.example.govt.nz/passengers/12345/flights/1w3r4y6u
#The api invalidates cache object with key cachekey__12345_1w3r4y6u
{
"id": "1w3r4y6u",
"passenger_name": "SMITH/NICOLAS",
"pnr_number": "CG4X7U",
"travel_class": "business",
"seat": "10J",
"auxiliary_fields": [
{
"label": "Terminal",
"value": "T2"
},
{
"label": "Departure",
"value": "30OCT 19:05"
}
],
"secondary_fields": [
{
"label": "Boarding",
"value": "18:30"
},
{
"label": "Gate",
"value": "D57"
},
{
"label": "Seat",
"value": "10J"
},
{
"label": "Sec.Nr.",
"value": "003"
}
],
"flight_info": {
"flight_number": "KL0642",
"departure_airport": {
"airport_code": "JFK",
"city": "New York",
"terminal": "T1",
"gate": "D57"
},
"arrival_airport": {
"airport_code": "AMS",
"city": "Amsterdam"
},
"flight_schedule": {
"departure_time": "2016-01-02T19:05",
"arrival_time": "2016-01-05T17:30"
}
}
}
# Response that update was successful
HTTP/1.1 204 No Content
Content-Location: /passengers/12345/flights/1w3r4y6u
3.4. DELETE
DELETE is used to delete a resource item.
DELETE is only used to delete a resource identified by a URI.
On successful deletion, DELETE returns an HTTP status 200 (OK) along with a response body, and optionally the representation of the deleted item, or a wrapped response. Alternatively, DELETE can return HTTP status 204 (NO CONTENT) with no response body.
DELETE operations are idempotent. If you DELETE a resource, it is removed. Repeatedly calling DELETE on that resource ends up as the same result — the resource is gone. However, calling DELETE on a resource a second time will often return a 404 (NOT FOUND) since it was already removed and therefore is no longer findable. This, in some opinion, makes DELETE operations no longer idempotent, but the end state of the resource is the same. Returning a 404 is acceptable and communicates accurately the status of the call. If calling DELETE were to decrement a counter (within the resource), the DELETE call is no longer idempotent.
3.4.1. Examples
Example
#Delete a resource
DELETE https://api.example.govt.nz/passengers/12345/flights/1w3r4y6u
Example
#Response
HTTP/1.1 204 No Content
Content-Location: /passengers/12345/flights/1w3r4y6u
3.5. OPTIONS
OPTIONS is used to retrieve information about what the consumer is allowed to do with the resource.
3.6. Other
PATCH is a valid HTTP verb but is still in draft and its use is discouraged due to complexity. An exception to this might be where an agency-adopted standard, such as Restful API — FHIR , supports the use of PATCH.
HEAD is used to retrieve metadata about a resource, such as when it was last updated or the associated response headers. The response to a HEAD request must not contain a body. If a response body is returned it must be ignored.
4. Appendix B — HTTP headers
4.1. Request headers — Required
Request headers are supplied by the consuming application and must include the following elements:
- Accept
- Content-Type
- Authorization.
Header definitions can be found in Header Field Definitions: RFC 2616 — W3C.
4.1.1. Accept — Required
An Accept header is required to indicate what format the consuming application wants the information to be returned as (for example, JSON or XML). It is preferable for the consuming application to specify the response content type that they expect, however it is acceptable to use the ‘q’ parameter to specify the quality of support.
Example
#Accept JSON and latest version
Accept: application/json,version=*
#Prefer JSON, Accept XML and latest version 1 release
Accept: application/json;q=1.0,application/xml;q=0.8,version=1.*
#Accept XML only and version 1.2 only
Accept: application/xml;q=1.0,application/json;q=0.0,version=1.2
If the client has specified via the Accept header that it does not support any formats provided by the API, the server should return an HTTP 406 (Not Acceptable) response. The response should also include a Link header with a link to documentation or help on supported formats. The body of the representation should include an error message in human readable plain text or HTML.
Example response
# Response that indicates the request failed due to the client not
# supporting any response formats provided by the API
HTTP/1.1 406 Not Acceptable
Content-Type: text/plain
This API does not support any of the requested content types specified in the HTTP Accept header. Please see http://www.example.com/api/documentation/widgetservice for details on the supported content types.
The Accept header is required, however the ‘version’ and ‘q’ are recommended as this will depend on a provider’s implementation and in some cases technology choice.
4.1.2. Content-Type — Required
The Content-type header is required for all requests that include a request body like POST, PUT, DELETE.
Example
#PUT https://api.example.govt.nz/v2/learners/33245
Accept: application/json,version=1.*
Content-Type: application/json
Authorization: Bearer x6TLB4JaomezC5rJ3CIl3KxxNinq
{
"id": "33245",
"names":
{
"firstName": "John",
"middleName": "Howard",
"lastName": "Doe",
"salutation": "Mr John Doe",
"title": "Mr"
},
"addresses": [
{
"type": "home",
"address1": "1 Some Street",
"address2": "Some Suburb",
"address3": "Some City",
"address4": null,
"country": "New Zealand",
"postcode": 1111
},
{
"type": "business",
"address1": "2 Work Street",
"address2": "Work Suburb",
"address3": "Work City",
"address4": null,
"country": "New Zealand",
"postcode": 2222
}
],
"qualifications":[
{
"id": "1a4rf54ee345",
"type": "Some type",
"provider": "University of life",
"providerId": "0o9i8u7y6tt"
},
{
"id": "8u7y6t5r4ee",
"type": "Another type",
"provider": "School xyz",
"providerId": "1q2w3e4r5tt"
}
]
}
4.1.3. Authorization
Most API requests will be authorised. The Authorization header should be used for this purpose and no other. Note that the Authorization header is not the best place for an API key.
The Authorization header is comprised of a type and a token depending on the authorization type.
Example
#OAuth Access Token
Authorization: Bearer x6TLB4JaomezC5rJ3CIl3KxxNinq
#HTTP Basic
Authorization: Basic c29tZXVzZXI6c29tZSBwYXNzd29yZA==
4.1.4. Accept-Encoding — Recommended
In some cases, especially where API responses are very large, it may be worth compressing HTTP responses.
Note: Where possible it is best to avoid large payloads in API responses, however, this may not always be possible.
An Accept-Encoding value of gzip instructs a provider that supports HTTP compression that the consuming application supports compressed responses.
Example
Accept-Encoding: gzip,deflate
4.1.5. API key — Required
An API key header issued to a consuming application should be sent with every request made to the API. The name of the header is up to the agency, but it should not be an X- prefixed header as this use is deprecated. Sometimes API keys are passed in URIs, however, this is not considered best practice and should be avoided.
Example
KeyID: pX680LyiKasUBWLuZJ8AVBHVQK03Ghtr
4.1.6. If-Modified-Since / If-None-Match — Recommended
The If-Modified-Since header is a pre-condition header used to instruct a client on the validity of the resource requested. Note that pre-condition handling is not required, but should be considered where appropriate. Pre-condition control can be a complex topic so in some cases the complexity may eclipse the value.
Conditional header standards are available in Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests RFC 7232 — IETF.
A consuming application can specify an If-Modified-Since header with a date and time. The server checks the resource to see the last modified time. If the last modification was before the date / time specified in the header, the server will respond with an HTTP 304 (NOT MODIFIED) status code telling the consuming application that the resource they requested has not changed and therefore there is no need to return the full payload in the response.
When using If-Modified-Since headers you should also implement ETag as discussed in section 1.6. HTTP headers.
Example
# Request with If-Modified-Since and If-None-Match headers
If-Modified-Since: Wed, 18 Jun 2016 22:00:00 GMT
If-None-Match: "686897696a7c876b7e"
The If-None-Match header indicates to the provider the ETag for the resource that it currently holds for that resource.
Example response
# Response that indicates the client’s current representation is still
# valid
HTTP/1.1 304 Not Modified
ETag: "686897696a7c876b7e"
Content-Length: 0
4.1.7. Request tracking headers — Recommended
To support the tracking of API requests it is a good idea to apply a transaction ID header that will enable analysts to trace a request throughout its life cycle.
Note: There is no set naming standard for a transaction ID header.
There are 2 common approaches to this:
- To request that the consuming application generate a unique transaction ID and the provider will capture it and respond with a corresponding correlation ID.
- To allow the provider to generate the transaction ID as close to the edge of its domain as possible, and respond to the consuming application with that transaction ID. In this case a consuming application administrator or developer should capture and log that transaction ID for audit purposes so that it can be relayed to agency analysts if required.
Example request
#Request that includes a consuming application generated transaction
#ID
Request-Transaction-Id: c188cb0e-1a09-4184-890c-0f0bb2255425
Example response
#Response to a consuming application that provided a client generated #transaction ID
Request-Transaction-Id: c188cb0e-1a09-4184-890c-0f0bb2255425
Response-Correlation-Id: 514dbf50-4475-4e4d-86ee-e2001f158026
#Response to a consuming application where there was no client #generated transaction ID
#Note that the server has generated the transaction ID
Response-Correlation-Id: c188cb0e-1a09-4184-890c-0f0bb2255425
4.2. Response headers
Response headers are supplied by the provider and must contain Content-Type.
4.2.1. Content-Type — Required
The Content-Type must be returned in the response, and indicate the format type the response content is in, for example, ‘Content-Type: application/json’. The Content-Type header should also include the version of the API that processed the request, for example, ‘Content-Type: application/json,version=1.1’.
4.2.2. Location — Recommended
Where a new resource item has been created (POST), the server should respond with a 201 (Created) HTTP header and a Location header indicating the URI of the newly created resource item, for example, ‘Location: https://api.example.govt.nz/v1/crown-property/198373’.
A Location header should also be returned by a server when it sends a 3** HTTP response, indicating a temporary or permanent redirection.
A Location header can be, as recommended by this standard, absolute, for example, ‘Location: https://api.example.govt.nz/v1/crown-property/198373’ or relative, for example, ‘Location: /v1/crown-property/198373’.
4.2.3. Content-Location — Recommended
The Content-Location header indicates the location of the requested resource. It should be an absolute and not a relative link.
Example
#GET https://api.example.govt.nz/v1/crown-property/198373
Content-Type: application/json
Content-Location: https://api.example.govt.nz/v1/crown-property/198373
{
"propertyid": 12345,
"address1": "1 Some Street",
"address2": "Some Suburb",
"address3": "Some City",
"address4": null,
"country": "New Zealand",
"postcode": 1111,
"type": "commercial"
}
4.2.4. Cache-Control — Recommended
It is important to consider caching when designing APIs, to ensure that the APIs you build can perform at scale. The Cache-Control header is only concerned with caching that occurs externally to the API layer. Common examples may be client-side caching or caching within a Content Delivery Network (CDN). This header should not be concerned with caching mechanisms within the API layer such as response caches or object caches, which require a different caching strategy.
The Cache-Control header is comprised of the following:
- private|public — indicates if the cache is specific to the consumer or not. A setting of private will prevent the cache from being persisted in a public cache.
- no-cache — instructs a cache to revalidate the resource every time.
- no-store — instructs a cache not to store the response. It also ensures that no part of the request is stored too.
- max-age — instructs the cache on expiry time. This can be used but it makes more sense to use the Expires header discussed in section 4.2.5. Expires.
- s-max-age — similar to max-age, however it refers to a shared cache therefore only relevant to CDNs and other intermediary caches.
- must-revalidate — instructs the client to resend request headers and receive confirmation that the resource has not changed.
- proxy-revalidate — similar to must-revalidate, however specific to shared caches.
Example
# Example caching response
HTTP/1.1 200 OK
Date: Wed, 06 Jul 2016 21:14:52 GMT
Expires: Wed, 06 Jul 2016 22:14:52 GMT
Cache-Control: private,max-age=14400,must-revalidate
Content-Type: application/json
Care should be taken when using caches, especially shared or public caches. In some cases it may be more prudent to use the no-cache directive to ensure that content is not cached outside your network. This does not rule out caching within your API layer. For more information, see section 1.12. Caching.
HTTP Cache Control standards can be found in Hypertext Transfer Protocol (HTTP/1.1): Caching RFC 7234 — IETF.
4.2.5. Expires — Recommended
When using Cache-Control you should include an Expires header in the response for backward compatibility.
4.2.6. ETag (Entity Tag) — Recommended
The ETag header is a concurrency control header, and when in a response it is a tag that the consuming application can use when making a subsequent request to a resource.
Note that pre-condition handling is not required, but should be considered where appropriate. Pre-condition control can be a complex topic so in some cases the complexity may eclipse the value.
Example
# Response with ETag Header
# Consuming application would send this ETag in an If-None-Match #request header
ETag: "686897696a7c876b7e"
4.3. Custom X-HTTP headers
X- notation headers have been deprecated and should not be used.
Deprecating the “X-” Prefix and Similar Constructs in Application Protocols RFC 6648 — IETF
This is because the X- name often became a standard, making the X-notation inaccurate (for example, x-gzip became gzip) and requiring support for both the X- and the non-X- notation. Common uses of the X- headers are examples such as the X-HTTP-Method-Override header or the X-Forwarded-For header.
Where an agency feels that a custom HTTP header is required that is not available within the current HTTP standard they can use a custom header without the X- prefix.
5. Appendix C — Example interface specification
Figures 5 to 8 illustrate examples of an agency API interface. The examples are:
- Create customer
- Get customer
- Modify customer
- Delete customer.
Following the figures is the interface specification written in Swagger and visualised via a Confluence plugin, however most API developer portal products will support importing OpenAPI specifications.
The raw JSON, YAML and Swagger code is below. This is best viewed via the Swagger (OpenAPI) editor. Simply open the editor and paste the contents of the JSON, YAML and Swagger examples into the editor window.
JSON example
{
"swagger": "2.0",
"info": {
"version": "2.0.0",
"title": "Example Agency API",
"description": "A sample API specification demonstrating OpenAPI capabilities — this is for example purposes only",
"termsOfService": "https://developer.example.govt.nz/termsandconditions/",
"contact": {
"name": "Example Agency API team",
"email": "exampleteam@api.govt.nz",
"url": "https://agency.govt.nz"
}
},
"host": "api.example.govt.nz",
"basePath": "/v2",
"paths": {
"/customers/{customerId}": {
"get": {
"parameters": [
{
"name": "customerId",
"in": "path",
"description": "Unique ID of customer",
"required": true,
"type": "string"
}
],
"tags": [
"get customer details"
],
"description": "Returns details for a customer given a unique customer ID\n",
"security": [
{
"AuthorizationCode": [
"customerRead"
]
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "OK — Successful",
"schema": {
"$ref": "#/definitions/customerDetailsResponse"
}
},
"default": {
"description": "Error response",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
}
},
"put": {
"tags": [
"modify customer"
],
"description": "Updates a customer’s details",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "customerId",
"in": "path",
"description": "Unique ID of customer",
"required": true,
"type": "string"
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/customerRequestModel"
}
}
],
"responses": {
"200": {
"description": "Updated resource successfully"
},
"default": {
"description": "Error response",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
},
"security": [
{
"AuthorizationCode": [
"customerWrite"
]
}
]
},
"delete": {
"tags": [
"delete customer"
],
"description": "Deletes an existing customer resource",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "customerId",
"in": "path",
"description": "Unique ID of customer",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "Deleted resource successfully"
},
"default": {
"description": "Error response",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
},
"security": [
{
"AuthorizationCode": [
"customerDelete"
]
}
]
}
},
"/customers": {
"post": {
"tags": [
"create customer"
],
"description": "Creates a new customer. API will create a customer resource and, if successful, return a hyperlink to the created resource",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/customerRequestModel"
}
}
],
"responses": {
"201": {
"description": "Created resource successfully",
"schema": {
"$ref": "#/definitions/_links"
}
},
"default": {
"description": "Error response",
"schema": {
"$ref": "#/definitions/errorModel"
}
}
},
"security": [
{
"AuthorizationCode": [
"customerCreate"
]
}
]
}
}
},
"securityDefinitions": {
"AuthorizationCode": {
"type": "oauth2",
"scopes": {
"customerRead": "Grants read access to a customer resource",
"customerWrite": "Grants write (update) access to an existing customer resource",
"customerCreate": "Grants access to create a new customer resource",
"customerDelete": "Grants access to delete an existing customer resource"
},
"flow": "accessCode",
"authorizationUrl": "https://idm.example.govt.nz/oauth/authorize",
"tokenUrl": "https://idm.example.govt.nz/oauth/access_token"
}
},
"definitions": {
"customerRequestModel": {
"type": "object",
"properties": {
"names": {
"type": "object",
"properties": {
"firstName": {
"type": "string",
"description": "Customers first name"
},
"middleName": {
"type": "string",
"description": "Customers middle name"
},
"lastName": {
"type": "string",
"description": "Customers last name"
},
"salutation": {
"type": "string",
"description": "Customers salutation — for example, Mr John Doe"
},
"title": {
"type": "string",
"description": "Customers title — for example, Ms, Mr, Mrs, Dr, and so on."
}
}
},
"addresses": {
"type": "array",
"description": "An array of addresses. Unpopulated properties or items will be returned as null JSON objects.",
"items": {
"properties": {
"addressType": {
"type": "string",
"description": "Address type — for example, Home, Business"
},
"address1": {
"type": "string",
"description": "First line of the address — for example, 2 something street"
},
"address2": {
"type": "string",
"description": "Second line of address — for example, Khandallah"
},
"address3": {
"type": "string",
"description": "Third line of address — for example, Wellington"
},
"address4": {
"type": "string"
},
"country": {
"type": "string",
"description": "This is a human readable country name — for example, New Zealand and not a country code"
}
}
}
}
}
},
"customerDetailsResponse": {
"type": "object",
"required": [
"customerId"
],
"properties": {
"customerId": {
"type": "string",
"description": "Unique identifier for the customer resource GUID format"
},
"names": {
"type": "object",
"properties": {
"firstName": {
"type": "string",
"description": "Customers first name"
},
"middleName": {
"type": "string",
"description": "Customers middle name"
},
"lastName": {
"type": "string",
"description": "Customers last name"
},
"salutation": {
"type": "string",
"description": "Customers salutation — for example, Mr John Doe"
},
"title": {
"type": "string",
"description": "Customers title — for example, Ms, Mr, Mrs, Dr, and so on."
}
}
},
"addresses": {
"type": "array",
"description": "An array of addresses. Unpopulated properties or items will be returned as null JSON objects.",
"items": {
"properties": {
"addressType": {
"type": "string",
"description": "Address type — for example, Home, Business"
},
"address1": {
"type": "string",
"description": "First line of the address — for example, 2 something street"
},
"address2": {
"type": "string",
"description": "Second line of address — for example, Khandallah"
},
"address3": {
"type": "string",
"description": "Third line of address — for example, Wellington"
},
"address4": {
"type": "string"
},
"country": {
"type": "string",
"description": "This is a human readable country name — for example, New Zealand and not a country code"
}
}
}
},
"_links": {
"type": "array",
"description": "An array of related links relevant to the request/response.",
"items": {
"properties": {
"rel": {
"type": "string",
"description": "A piece of information providing context for the link — for example, self or accounts."
},
"href": {
"type": "string",
"description": "A fully qualified URL to the resource."
}
}
}
}
}
},
"errorModel": {
"type": "object",
"properties": {
"errors": {
"type": "array",
"items": {
"description": "An array of error items containing error codes and descriptions. Note that the error descriptions will be normalised.",
"properties": {
"code": {
"type": "integer",
"format": "int32",
"description": "An error code - should be used in conjunction with the HTPP response code."
},
"description": {
"type": "string",
"description": "A short description of the error."
}
}
}
},
"_links": {
"type": "array",
"items": {
"properties": {
"rel": {
"type": "string",
"description": "The relationship to the request — for example, self that contains the resource that was requested or {object name}, a link to a resource that is related to the requested resource"
},
"href": {
"type": "string",
"description": "A link to the related resource. In an error scenario this is likely to be something such as a link to a support portal."
}
}
}
}
}
},
"_links": {
"type": "array",
"items": {
"properties": {
"rel": {
"type": "string",
"description": "The relationship to the request — for example, self that contains the resource that was requested or {object name}, a link to a resource that is related to the requested resource"
},
"href": {
"type": "string",
"description": "A link to the related resource"
}
}
}
}
}
}
YAML example
openapi: 3.0.0
info:
version: 2.0.0
title: Example Agency API
description: A sample API specification demonstrating OpenAPI capabilities - this is for
example purposes only
termsOfService: https:// api.example.govt.nz/termsandconditions/
contact:
name: Example Agency API team
email: exampleteam@api.govt.nz
url: https://agency.govt.nz
paths:
"/customers/{customerId}":
get:
parameters:
- name: customerId
in: path
description: Unique ID of customer
required: true
schema:
type: string
tags:
- get customer details
description: |
Returns details for a customer given a unique customer ID
security:
- AuthorizationCode:
- customerRead
responses:
"200":
description: OK - Successful
content:
application/json:
schema:
$ref: "#/components/schemas/customer"
default:
description: Error response
content:
application/json:
schema:
$ref: "#/components/schemas/errorModel"
put:
tags:
- modify customer
description: Updates a customer’s details
parameters:
- name: customerId
in: path
description: Unique ID of customer
required: true
schema:
type: string
requestBody:
$ref: "#/components/requestBodies/customer"
responses:
"200":
description: Updated resource successfully
default:
description: Error response
content:
application/json:
schema:
$ref: "#/components/schemas/errorModel"
security:
- AuthorizationCode:
- customerWrite
delete:
tags:
- delete customer
description: Deletes an existing customer resource
parameters:
- name: customerId
in: path
description: Unique ID of customer
required: true
schema:
type: string
responses:
"200":
description: Deleted resource successfully
default:
description: Error response
content:
application/json:
schema:
$ref: "#/components/schemas/errorModel"
security:
- AuthorizationCode:
- customerDelete
/customers:
post:
tags:
- create customer
description: Creates a new customer. API will create a customer resource and, if
successful, return a hyperlink to the created resource
requestBody:
$ref: "#/components/requestBodies/customer"
responses:
"201":
description: Created resource successfully
content:
application/json:
schema:
$ref: "#/components/schemas/_links"
default:
description: Error response
content:
application/json:
schema:
$ref: "#/components/schemas/errorModel"
security:
- AuthorizationCode:
- customerCreate
servers:
- url: https:// api.example.govt.nz/v2
components:
parameters:
Authorization:
description: For demonstration purposes only — this should not be required in most
situations as OAuth Authorization Code grant type implicity indicates
this.
name: Authorization
in: header
required: true
schema:
type: string
default: "Bearer "
requestBodies:
customer:
content:
application/json:
schema:
$ref: "#/components/schemas/customer"
required: true
securitySchemes:
AuthorizationCode:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https:// api.example.govt.nz/web
tokenUrl: https:// api.example.govt.nz/oauth2/token
scopes:
customerRead: Grants read access to a customer resource
customerWrite: Grants write (update) access to an existing customer resource
customerCreate: Grants access to create a new customer resource
customerDelete: Grants access to delete an existing customer resource
schemas:
customer:
type: object
properties:
customerId:
type: string
description: Unique identifier for the customer resource — GUID format
readOnly: true
names:
type: object
properties:
firstName:
type: string
description: Customers first name
example: Swithin
middleName:
type: string
description: Customers middle name
example: Henry
lastName:
type: string
description: Customers last name
example: Foote
salutation:
type: string
description: Customers salutation — for example, Mr John Doe
example: Mr Swithin Henry Foote
title:
type: string
description: Customers title — for example, Ms, Mr, Mrs, Dr, and so on.
example: Mr
addresses:
type: array
description: An array of addresses. Unpopulated properties or items will be
returned as null JSON objects.
items:
properties:
addressType:
type: string
description: Address type — for example, Home, Business
example: Home
address1:
type: string
description: First line of the address — for example, 2 something street
example: 2 Queens Drive
address2:
type: string
description: Second line of address — for example, Khandallah
example: Khandallah
address3:
type: string
description: Third line of address — for example, Wellington
example: Wellington
address4:
type: string
country:
type: string
description: This is a human readable country name — for example, New Zealand and not
a country code
example: New Zealand
_links:
type: array
readOnly: true
description: An array of related links relevant to the request/response.
items:
properties:
rel:
type: string
description: A piece of information providing context for the link — for example,
self or accounts.
href:
type: string
description: A fully qualified URL to the resource.
errorModel:
type: object
properties:
errors:
type: array
items:
description: An array of error items containing error codes and descriptions.
Note that the error descriptions will be normalised.
properties:
code:
type: integer
format: int32
description: An error code — should be used in conjunction with the HTPP
response code.
description:
type: string
description: A short description of the error.
_links:
type: array
items:
properties:
rel:
type: string
description: The relationship to the request — for example,. self that contains the
resource that was requested or {object name}, a link to a
resource that is related to the requested resource
href:
type: string
description: A link to the related resource. In an error scenario this is
likely to be something such as a link to a support portal.
_links:
type: array
items:
properties:
rel:
type: string
description: The relationship to the request — for example, self that contains the
resource that was requested or {object name}, a link to a resource
that is related to the requested resource
href:
type: string
description: A link to the related resource
Swagger 2.0 example
#Example API Specification for Agency X
#Author Swithin Foote Middleware New Zealand
swagger: '2.0'
info:
version: 2.0.0
title: Example Agency API
description: A sample API specification demonstrating OpenAPI capabilities — this is for example purposes only
termsOfService: https://developer.example.govt.nz/termsandconditions/
contact:
name: Example Agency API team
email: exampleteam@api.govt.nz
url: https://agency.govt.nz
host: api.example.govt.nz
basePath: /v2
paths:
/customers/{customerId}:
get:
parameters:
- name: customerId
in: path
description: Unique ID of customer
required: true
type: string
tags:
- get customer details
description: |
Returns details for a customer given a unique customer ID
security:
- AuthorizationCode:
- customerRead
produces:
- application/json
responses:
200:
description: OK - Successful
schema:
$ref: '#/definitions/customerDetailsResponse'
default:
description: Error response
schema:
$ref: '#/definitions/errorModel'
put:
tags:
- modify customer
description: Updates a customer’s details
consumes:
- application/json
produces:
- application/json
parameters:
- name: customerId
in: path
description: Unique ID of customer
required: true
type: string
- name: body
in: body
required: true
schema:
$ref: "#/definitions/customerRequestModel"
responses:
200:
description: Updated resource successfully
default:
description: Error response
schema:
$ref: '#/definitions/errorModel'
security:
- AuthorizationCode:
- customerWrite
delete:
tags:
- delete customer
description: Deletes an existing customer resource
consumes:
- application/json
produces:
- application/json
parameters:
- name: customerId
in: path
description: Unique ID of customer
required: true
type: string
responses:
200:
description: Deleted resource successfully
default:
description: Error response
schema:
$ref: '#/definitions/errorModel'
security:
- AuthorizationCode:
- customerDelete
/customers:
post:
tags:
- create customer
description: Creates a new customer. API will create a customer resource and, if successful, return a hyperlink to the created resource
consumes:
- application/json
produces:
- application/json
parameters:
- name: body
in: body
required: true
schema:
$ref: "#/definitions/customerRequestModel"
responses:
201:
description: Created resource successfully
schema:
$ref: '#/definitions/_links'
default:
description: Error response
schema:
$ref: '#/definitions/errorModel'
security:
- AuthorizationCode:
- customerCreate
securityDefinitions:
AuthorizationCode:
type: oauth2
scopes:
customerRead: Grants read access to a customer resource
customerWrite: Grants write (update) access to an existing customer resource
customerCreate: Grants access to create a new customer resource
customerDelete: Grants access to delete an existing customer resource
flow: accessCode
authorizationUrl: https://idm.example.govt.nz/oauth/authorize
tokenUrl: https://idm.example.govt.nz/oauth/access_token
definitions:
customerRequestModel:
type: object
properties:
names:
type: object
properties:
firstName:
type: string
description: Customers first name
middleName:
type: string
description: Customers middle name
lastName:
type: string
description: Customers last name
salutation:
type: string
description: Customers salutation — for example, Mr John Doe
title:
type: string
description: Customers title — for example,. Ms, Mr, Mrs, Dr, and so on.
addresses:
type: array
description: An array of addresses. Unpopulated properties or items will be returned as null JSON objects.
items:
properties:
addressType:
type: string
description: Address type — for example, Home, Business
address1:
type: string
description: First line of the address — for example, 2 something street
address2:
type: string
description: Second line of address — for example, Khandallah
address3:
type: string
description: Third line of address — for example, Wellington
address4:
type: string
country:
type: string
description: This is a human readable country name — for example,. New Zealand and not a country code
customerDetailsResponse:
type: object
required:
- customerId
properties:
customerId:
type: string
description: Unique identifier for the customer resource — GUID format
names:
type: object
properties:
firstName:
type: string
description: Customers first name
middleName:
type: string
description: Customers middle name
lastName:
type: string
description: Customers last name
salutation:
type: string
description: Customers salutation — for example, Mr John Doe
title:
type: string
description: Customers title — for example, Ms, Mr, Mrs, Dr, and so on.
addresses:
type: array
description: An array of addresses. Unpopulated properties or items will be returned as null JSON objects.
items:
properties:
addressType:
type: string
description: Address type — for example, Home, Business
address1:
type: string
description: First line of the address — for example, 2 something street
address2:
type: string
description: Second line of address — for example, Khandallah
address3:
type: string
description: Third line of address — for example, Wellington
address4:
type: string
country:
type: string
description: This is a human readable country name — for example, New Zealand and not a country code
_links:
type: array
description: An array of related links relevant to the request/response.
items:
properties:
rel:
type: string
description: A piece of information providing context for the link — for example, self or accounts.
href:
type: string
description: A fully qualified URL to the resource.
errorModel:
type: object
properties:
errors:
type: array
items:
description: An array of error items containing error codes and descriptions. Note that the error descriptions will be normalised.
properties:
code:
type: integer
format: int32
description: An error code — should be used in conjunction with the HTPP response code.
description:
type: string
description: A short description of the error.
_links:
type: array
items:
properties:
rel:
type: string
description: The relationship to the request — for example, self that contains the resource that was requested or {object name}, a link to a resource that is related to the requested resource
href:
type: string
description: A link to the related resource. In an error scenario this is likely to be something such as a link to a support portal.
_links:
type: array
items:
properties:
rel:
type: string
description: The relationship to the request — for example, self that contains the resource that was requested or {object name}, a link to a resource that is related to the requested resource
href:
type: string
description: A link to the related resource
6. Appendix D — Synchronous / asynchronous example specifications
6.1. OpenAPI 3.0
To visualise, paste the content below into the OpenAPI editor SwaggerEditor.
Example
openapi: 3.0.0
info:
version: 1.0.0
title: Example Agency Claims API
description: A sample API specification demonstrating OpenAPI capabilities — this is for
example purposes only
termsOfService: https:// api.example.govt.nz/termsandconditions/
contact:
name: Example Agency API team
email: exampleteam@api.govt.nz
url: https://agency.govt.nz
paths:
"/claims":
get:
description: Retrieve details of a claim
tags:
- get claim
security:
- AuthorizationCode:
- read:claims
responses:
200:
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/claims"
post:
description: Create a claim
tags:
- Create CLaim
security:
- AuthorizationCode:
- write:claims
requestBody:
$ref: "#/components/schemas/createClaim"
responses:
"201":
description: Created
content:
application/json:
schema:
$ref: "#/components/schemas/claims"
default:
description: Error response
content:
application/json:
schema:
$ref: "#/components/schemas/errorModel"
components:
securitySchemes:
AuthorizationCode:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https:// api.example.govt.nz/web
tokenUrl: https:// api.example.govt.nz/oauth2/token
scopes:
read:claims: Grants read access to a claim resource
write:claims: Grants write (update) access to an existing claims resource
schemas:
createClaim:
description: Create Claim Model
type: object
properties:
customerName:
type: string
example: "John Henry Ford"
claimDetail:
type: string
description: Claims details
example: This is a long string describing the claim for evaluation......
claims:
description: Claim Response Model
type: object
properties:
claimId:
type: string
format: uuid
description: Unique identifier for the claim
description:
type: string
description: A short description of the claim
status:
type: string
enum: ["PROCESSING","REJECTED","ACCEPTED"]
claimUri:
type: string
description: Resource identifier for the claim
example: "https://api.exampleagency.govt.nz/claims/{claimId}"
errorModel:
type: object
properties:
errors:
type: array
items:
description: An array of error items containing error codes and descriptions.
Note that the error descriptions will be normalised.
properties:
code:
type: integer
format: int32
description: An error code — should be used in conjunction with the HTPP
response code.
description:
type: string
description: A short description of the error.
_links:
type: array
items:
properties:
rel:
type: string
description: The relationship to the request — for example, self that contains the
resource that was requested or {object name}, a link to a
resource that is related to the requested resource
href:
type: string
description: A link to the related resource. In an error scenario this is
likely to be something such as a link to a support portal.
6.2. AsyncAPI 2.0
To visualise, paste the content below into the AsyncAPI playground AsyncAPI 2.0.0 playground.
Example
asyncapi: 2.0.0
info:
title: Claims Notifications
version: 1.0.0
description: This channel provides claim information and status notifications
servers:
production:
url: broker.exampleagency.govt.nz
protocol: amqps,secure-mqtt,https
description: This is "Example Agencies" broker.
security:
- oauth2: []
channels:
claims/{claimId}:
publish:
message:
$ref: '#/components/messages/claim'
parameters:
claimId:
description: Unique identifier for the claim
schema:
type: string
format: uuid
subscribe:
message:
$ref: '#/components/messages/claim'
components:
messages:
claim:
payload:
type: object
properties:
claimId:
type: string
format: uuid
description: Unique identifier for the claim
description:
type: string
description: A short description of the claim
status:
type: string
enum: ["PROCESSING","REJECTED","ACCEPTED"]
claimUri:
type: string
description: Resource identifier for the claim
example: "https://api.exampleagency.govt.nz/claims/{claimId}"
securitySchemes:
user-password:
type: userPassword
oauth2:
type: oauth2
description: OAuth2 identity service for "Example Agency"
flows:
authorizationCode:
authorizationUrl: https://api.exampleagency.govt.nz/oauth/authorize
tokenUrl: https://api.exampleagency.govt.nz/oauth/token
scopes:
read:claims: Read details of claims
7. Appendix E — Standards for developing APIs
Learn more about the relevant standards for developing APIs.
In addition to the security standards captured in Part B: API security 2022, table 7 captures (current) API and web standards that should be considered as part of any API strategy.
For recommended standards and their current status, see the Government Digital Standards Catalogue.
Standard or standard organisation | Description |
---|---|
OpenAPI | The OpenAPI specification defines a standard, language-agnostic interface to RESTful APIs that allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation or through network traffic inspection. |
AsyncAPI | AsyncAPI is an open source initiative that seeks to improve the current state of Event-Driven Architectures (EDA). Their long-term goal is to make working with EDAs as easy as it is to work with REST APIs. |
Hyper Text Transfer Protocol (HTTP) | Communication between client computers and web servers is done by sending HTTP requests and receiving HTTP responses. In practice, most RESTful APIs use HTTP as a transport layer and rely on the use of HTTP verbs, like POST, GET, PUT and DELETE. |
OAuth2 / OIDC | The OAuth 2.0 Authorization Framework (OAuth2) is an industry standard framework that enables third parties limited access to protected resources and is a commonly used authorisation approach for APIs. OpenID Connect (OIDC) is an identity layer on top of OAuth2 that allows consuming applications to request information about authenticated sessions and end users. |
JSON / JSON Schema | JavaScript Object Notation (JSON) is an open standard for a lightweight format for storing and transporting data. It is a ‘self-describing’ notation and is easy for both humans and computers to understand. |
JWT / JWS / JWE / JWK / JWA | These are a set of open standards for securely transmitting information between systems using JSON. They include the data structure for a JSON Web Token (JWT), how to sign a JWT (JSON Web Signature (JWS)), how to encrypt a JWT (JSON Web Encryption (JWE), and some JSON cryptography practices (JSON Web Key (JWK) and JSON Web Algorithms (JWA)). |
Fast Healthcare Interoperability Resources (FHIR) | FHIR is an industry standard for exchanging healthcare information electronically. It was developed by the HL-7 standards organisation. |
Payments New Zealand (PNZ) API Centre Standards | PNZ is an industry standard for Open Banking. The security profile is quite interesting in regards to capturing customer consent for third parties to act on their behalf. |
Simple Object Access Protocol (SOAP) | SOAP is deprecated for the NZ government — the Government Enterprise Architecture Group (GEAG) has determined that the standard should not be used for new work, but may still be in use for existing APIs. |
Financial Grade API (FAPI) Security Profile | FAPI is another industry standard for the financial sector. It is an example of good security profile practices. |
Pan European Public Procurement Online (Peppol) | Peppol is a global standardised framework that enables businesses to exchange procurement documents electronically. To do this, Peppol provides and maintains artefacts and specifications to make it possible for businesses using different systems to ‘speak’ to each other. The use of Peppol is governed by a multi-lateral agreement structure, owned and maintained by OpenPeppol. |
Internet Engineering Task Force (IETF) | IETF is a large open international community concerned with the evolution of the Internet architecture and the smooth operation of the Internet. Their mission is to make the Internet work better by producing high quality, relevant technical documents that influence the way people design, use and manage the Internet. Many of the standards listed were developed through the IETF. |
Standards New Zealand | A business unit within the Ministry of Business, Innovation and Employment which specialises in managing the development of standards. They also publish and sell NZ, joint Australia–NZ, and international standards. |
Stats NZ: Standards and classifications | Information about the standards and classifications Stats NZ use and maintain, news about reviews, and tools to help classify and code responses. |
Health Information Standards Organisation (HISO) | HISO supports and promotes the development and adoption of fit-for-purpose health information standards for the NZ health system. |
Data Content Requirements |
A documented set of government data content requirements listed on Data.govt.nz — Register of government mandated data standards. |
8. Glossary of terms
- Analytics
- Analytics in the context of these guidelines is the capturing and reporting of API usage.
- API (Application Programming Interface)
- An API is a piece of software that provides a way for other disparate pieces of software (applications, systems) to talk to one another.
- API catalogue
- The API delivery component that lists the APIs offered, along with their interface specification and guidance on how to gain access and use the APIs.
- API developer
- The organisation (or person) who creates the API and maintains the interface specification for the API. An API developer creates and documents an agency’s APIs. API developers generally have a good understanding of an agency’s function.
- API developer portal
- The API delivery component that allows API providers to engage with, onboard, educate and manage application developers whether inside or outside the organisation. These management activities will generally include registration, documentation, analytics and so on.
- API gateway
- The API delivery component that allows API providers to offer APIs to the outside world. This component (physical or virtual) hosts the APIs ready for consumers to access. It provides an API provider with the ability to control who has access to their APIs by enforcing policies for access control. Some API gateways also offer additional capabilities.
- API manager
- The API delivery component that allows API providers to control an API’s visibility and behaviour. It is usually exposed as a UI / console to internal staff only, as it is seen as an administration component. It offers a variety of capabilities, including API registration and catalogue administration.
- API provider
- The organisation that provides the API to expose a resource (information or functionality) for consumers.
- Application
- The software behind the API that provides the business logic for the resource.
- Application developer
- Software developer or organisation that builds consuming applications that use the API. An application developer needs to be able to discover, understand and access your APIs. They can be internal to your agency, developers that work with trusted partners, developers from other agencies or developers from the private sector.
- Atomicity
- One of the ACID (Atomicity, Consistency, Isolation, Durability) transaction properties. An atomic transaction is an indivisible and irreducible series of database operations such that either all occurs, or nothing occurs. A guarantee of atomicity prevents updates to the database occurring only partially, which can cause greater problems than rejecting the whole series outright. As a consequence, the transaction cannot be observed to be in progress by another database client. At one moment in time, it has not yet happened, and at the next it has already occurred in whole (or nothing happened if the transaction was cancelled in progress).
- Consumers
- Customers, consuming applications and application developers who use the API.
- Consuming application
- Any application (on any device) that consumes or uses an API.
- Context
- Context in these guidelines generally refers to request context. For example, a JWT (JSON Web Token) may contain information about the customer initiating an API request.
- Customers
- People (or organisations) that use the consuming applications to access the API resources the API provider is offering.
- DELETE
- One of the most common HTTP methods, DELETE is used to delete a resource specified by its URI.
- Discovery
- The ability for application developers to find resources and associated APIs to use in their consuming applications.
- GET
- An HTTP request method, GET is applied while requesting information from a particular source. It is also used to get a specific variable derived from a group.
- HEAD
- An HTTP request method, HEAD asks for a response identical to a GET request, but without the response body. It is used to send the request and retrieve just the HTTP header as response. For example, a client application can issue a HEAD request to check the size of a file (from HTTP headers) without downloading it.
- Interface specification
- Provides technical information to the application developer community about the API. It includes information about how the API works, any access control features and any error conditions.
- Idempotent
- An HTTP method where making multiple identical requests ends up having the same result as a single request.
- PATCH
- An HTTP request method, PATCH is a request for making partial changes to an existing resource. The PATCH method provides an entity containing a list of changes to be applied to the resource requested using the URI.
- POST
- One of the most common HTTP methods for retrieving from and sending data to a server, the POST method sends data to the server and creates a new resource. The resource it creates is subordinate to some other parent resource. When a new resource is POSTed to the parent, the API service will automatically associate the new resource by assigning it a new resource URI. In short, this method is used to create a new data entry.
- Publish
- The act of releasing the interface specification and associated API to a location accessible by application developers.
- PUT
- One of the most common HTTP methods for retrieving from and sending data to a server, the PUT method is most often used to update an existing resource. If you want to update a specific resource (which comes with a specific URI), you can call the PUT method to that resource URI with the request body containing the complete new version of the resource you are trying to update.
- Resource
- The information or functionality exposed by the API.
- State
- State defines the point in time record of an in-flight transaction. Some systems maintain ‘user state’ for a period of time to enable a transaction to be continued from the point of last recorded state. APIs are usually, but not always, considered stateless.
9. Glossary of acronyms
- API
- Application Programming Interface
- ASCII
- American Standard Code for Information Interchange
- B2B
- Business to business
- CA
- Certificate Authority
- CDN
- Content Delivery Network
- CRUD
- Create, Read, Update and Delete
- DDoS
- Distributed Denial of Service
- EDA
- Event-Driven Architectures
- ETag
- Entity Tag
- FAPI
- Financial Grade API
- FHIR
- Fast Healthcare Interoperability Resources
- GEAG
- Government Enterprise Architecture Group
- HATEOAS
- Hypermedia As The Engine Of Application State
- HISO
- Health Information Standards Organisation
- HTML
- Hypertext Markup Language
- HTTP
- Hyper Text Transfer Protocol
- IETF
- Internet Engineering Taskforce
- IIS
- Information Internet Services
- JSON
- Javascript Object Notation
- JWA
- JSON Web Algorithms
- JWE
- JSON Web Encryption
- JWK
- JSON Web Key
- JWS
- JSON Web Signature
- JWT
- JSON Web Token
- MIME
- Multipurpose Internet Mail Extensions
- OAuth
- Open Authorization
- OData
- Open Data Protocol
- OIDC
- OpenID Connect
- OWASP
- Open Web Application Security Project
- Peppol
- Pan European Public Procurement Online
- PNZ
- Payments New Zealand
- RAML
- REST API Modelng Language
- REST
- Representative State Transfer
- RFC
- Request for Comments
- SCM
- Software Configuration Management
- SDLC
- Software Development Life Cycle
- SEO
- Search Engine Optimisation
- SLA
- Service Level Agreement
- SOAP
- Simple Object Access Protocol
- SQL
- Structured Query Language
- UI
- User interface
- UML
- Unified Modeling Language
- URI
- Uniform Resource Identifier
- UTF-8
- Unicode Transformation Format-8
- WADL
- Web API Description Language
- XML
- eXtensible Markup Language
- XSS
- Cross-Site Scripting
- YAML
- YAML Ain’t Markup Language
10. Further reading
- HTTP 1.1 Standards RFCs — IETF:
- RFC7230 HTTP/1.1: Message Syntax and Routing
- RFC7231 HTTP/1.1: Semantics and Content
- RFC7232 HTTP/1.1: Conditional Requests
- RFC7233 HTTP/1.1: Range Requests
- RFC7234 HTTP/1.1: Caching
- RFC7235 HTTP/1.1: Authentication
- RFC7236 HTTP/1.1: Authentication Scheme Registrations
- RFC7237 HTTP/1.1: Method Registrations
- NZ Protective Security — Protective Security Requirement (PSR)
- OpenAPI Specification — GitHub
- OWASP API Security Project — OWASP
- OWASP REST Security — REST Security Cheat Sheet
- OWASP Secure Coding Principles — OWASP
- OWASP Top Ten Project — OWASP
- Reserved JavaScript Keywords — W3Schools
- REST API Resource Modelling — Thoughtworks
- Strategy for a Digital Public Service
- Using HTTP Methods for RESTful Services — REST API Tutorial
Last updated