2. API
We expect software vendors to create a mechanism by which data can be digitally retrieved from the application. This furthers our efforts as an organisation to provide joined up services and make good use of the data our services collect.
2.1 Document your API
APIs need to be documented to be useful. Good documentation will allow the users of the API to quickly integrate with the service and provide value.
We suggest using the guidance here to get an idea of the sections that should be included when formulating your guidance.
2.2 Use REST
Follow the industry standard and where appropriate build APIs that are RESTful, which use HTTP verb requests to manipulate data.
When handling requests, you should use HTTP verbs for their specified purpose.
One of the advantages of REST is that it gives you a framework for communicating error states.
In some cases, it may not be applicable to build a REST API, for example, when you are building an API to stream data.
You can read more about the characteristics of using RESTful APIs for the purpose of service integration in the MAMPU documentation here.
2.3 Use HTTPS
You should use HTTPS when creating APIs.
Adding HTTPS will secure connections to your API, preserve user privacy, ensure data integrity, and authenticate the server providing the API. The Service Manual provides more guidance on HTTPS.
Secure APIs using Transport Layer Security (TLS) 1.2 or above. Do not use Secure Sockets Layer (SSL), TLS 1.0 or TLS 1.1 as these are deprecated.
There are multiple free and low-cost vendors that offer TLS certificates. Make sure potential API users can establish trust in your certificates and make sure you have a robust process for timely certificate renewal and revocation.
2.4 Use JSON
Your first choice for all web APIs should be JSON where possible.
Only use another representation to build something in exceptional cases, like when you:
- need to connect to a legacy system, for example, one that only uses XML
- will receive clear advantages from complying with a broadly adopted standard (for example, SAML)
We recommend you should:
- create responses as a JSON object and not an array (JSON objects can contain JSON arrays) - arrays can limit the ability to include metadata about results and limit the API’s ability to add additional top-level keys in the future
- document the JSON object/s your API returns. This helps users of your API to be sure they are using the object in the appropriate way
- avoid JSON object keys that are not consistent for all objects of the same type, such as those derived from data as this adds friction for clients
- use consistent grammar case for object keys - choose under scores (eg 'email_address': 'example@example.com') or camel case (eg. 'emailAddress': 'example@example.com') and be consistent
2.5 Standardize date / time format
It can be useful to use the RFC3339 standard to represent date and time in your payload response. This helps people read the time correctly.
Use a consistent date format. For dates, this looks like 2017-08-09. For dates and times, use the form 2017-08-09T13:58:07Z or 2017-08-09 13:58:07Z.
2.6 Use Unicode for encoding
The Unicode Transformation Format (UTF-8) standard is used by many governments when encoding text or other textual representations of data.
2.7 Keep a log of requests for personal data
If your API serves personal or sensitive data, you should log when the data is provided and to whom. This will help you ensure ‘privacy by design’ in the delivery of your service. .
2.8 When to authenticate your API
Authentication is required when you want to identify clients for the purposes of:
- rate limiting/throttling
- auditing
- billing
- authorisation
Your purpose will dictate the security requirements for your authentication solution.
For example, if you need to identify users purely for rate limiting, you may not need to refresh tokens very often as a token in the wrong hands will be unlikely to threaten your service.
2.9 To provide application-level authorisation
Use application-level authorisation if you want to control which applications can access the API, but not which specific end users. This is suitable if you want to implement rate limiting, auditing, or billing functionality. Application-level authorisation is probably not suitable for APIs that will hold personal or sensitive data unless you really trust the end consumers, for example, another department in the same organisation.
We recommend using OAuth 2.0, the open authorisation framework (specifically with the Client Credentials grant type). This service gives each registered application an OAuth2 Bearer Token, which can be used to make API requests on the application’s own behalf.
2.10 To provide user-level authorisation
Use user-level authorisation if you want to control which end users can access your API. This is suitable for dealing with personal or sensitive data.
We recommend using OAuth 2.0, the open authorisation framework (specifically with the Authorisation Code grant type). Use OAuth 2.0 Scopes for more granular access control.
In a more complex environment such as a microservice or federated system OpenID Connect, which builds on top of OAuth 2.0 using JSON Web Tokens (JWT), may also be suitable. OpenID Connect provides a granular and machine readable mechanism to request and receive information about authenticated sessions and end-users in a system.
2.11 Provide ways to monitor your APIs for unusual activity
Your API security is only as good as your day-to-day security processes.
It should be possible to monitor your API for unusual behaviour just like you’d closely monitor any website. Where appropriate you might look for changes in IP addresses or users using APIs at unusual times of the day. The UK has the National Cyber Security Centre (NCSC) guidance on how to implement a monitoring strategy and the specifics of how to monitor the security status of networks and systems.
2.12 When naming and hosting your API
All API naming in URLs (including the name of your API, namespaces and resources) should:
- use nouns rather than verbs
https://www.api.com/users
not https://www.api.com/get-users
- be short, simple and clearly understandable. Overly long or hard to understand urls may indicate that you should restructure your api or split/ move functionality to elsewhere.
https://www.api.com/users/123/discount
not https://www.api.com/get-user-and-calculate-appropriate-discount/123
- be human-guessable, avoiding technical or specialist terms where possible
https://www.api.com/users/123/discount
not https://www.api.com/get-most-recent-pricing-object-for-user/123
- use hyphens rather than underscores as word separators for multiword names
https://www.api.com/users/123/email-preferences
not https://www.api.com/users/123/email_preferences
2.13 When using sub-resources
Sub-resources must appear under the resource they relate to, but should go no more than three deep, for example:
https://www.api.com/resource/id/sub-resource/id/sub-sub-resource
If you reach a third level of granularity (sub-sub-resource), you should review your resource construction to see if it is actually a combination of multiple first or second level resources.
2.14 When using query arguments
You should use path parameters to identify a specific resource or resources. For example:
https://www.api.com/users/123
You should only allow query strings to be used in GET requests for filtering the values returned from an individual resource, for example:
https://www.api.com/users?state=active
https://www.api.com/users?page=2.
You should never use query strings in GET requests for identification purposes, for example:
https://www.api.com/users?id=123
Query strings should not be used for defining the behaviour of your API, for example:
https://www.api.com/users?action=getUser&id=1
2.15 When iterating your API
When iterating your API to add new or improved functionality, you should minimise disruption for your users.
To minimise disruption for users, you should:
- make backwards compatible changes where possible - your users can ignore properties they don’t expect or understand. This allows you to add fields to deliver new functionality without requiring changes to the users application.
- make a new endpoint available for significant changes
- provide notices for deprecated endpoints.
2.16 When making a backwards incompatible change
When you need to make a backwards incompatible change you should consider:
- incrementing a version number in the URL or the HTTP header (start with /v1/ and increment with whole numbers)
- supporting both old and new endpoints in parallel for a suitable time period before discontinuing the old one
- telling users of your API how to validate data, for example, let them know when a field is not going to be present so they can make sure their validation rules will treat that field as optional
Sometimes you’ll need to make a larger change and simplify a complex object structure by folding data from multiple objects together. In this case, make a new object available at a new endpoint, for example:
Combine data about users and accounts from: https://www.api.com/users/123 https://www.api.com/accounts/123
To produce: https://www.api.com/consolidated-account/123
2.17 Set clear deprecation policies
Set clear API deprecation policies so you’re not supporting old client applications forever.
State how long users have to upgrade, and how you’ll notify them of these deadlines.
Your strategy will depend on a number of factors; how many users your API has, whether they are within your organisation or external users, how significant the changes are.
For example if an API is consumed by a single integration from another department in the same organisation it may be possible to approach the integration developers directly to inform them of upcoming changes. However in the case that your API is used by 100’s of users you will need to implement a mechanism for alerting these users. One popular method is to announce deprecation in HTTP responses using a ‘Warning’ header.
2.18 Provide users with a test service
Your API consumers will want to test their application against your API before they go live. If you have a read only API then you do not necessarily need to provide a test service.
Provide them with a test service (sometimes referred to as a sandbox).
If your API has complex or stateful behaviour, consider providing a test service that mimics the live service as much as possible, but bear in mind the cost of doing this.
If your API requires authorisation, for example using OAuth 2.0, you’ll need to include this in your test service or provide multiple levels of a test service.
To help you decide what to provide, do user research - ask your API consumers what a sufficient test service would look like.
2.19 Test your API’s compliance
You should provide your development team with the ability to test your API using sample test data, if applicable. Testing your API should not involve using production systems and production data.