-
Notifications
You must be signed in to change notification settings - Fork 1
API Guide
Status: Work In Progress
This is a guide to using the Book A Secure Move API.
This API is part of the solution built in order to create and manage booking requests to transfer people between locations involved in the justice system. In particular, this API allows suppliers to interact with the booking system, and supports the front-end application (see https://github.com/ministryofjustice/hmpps-book-secure-move-frontend).
This guide is an explanation of how to use the API to accomplish supplier activities
This guide is aimed at move suppliers, and in particular the teams responsible for integrating with the Book a Secure Move API
The first few sections of this guide should be read and understood. The walkthroughs will be added to over time, and so readers may choose to only read the detail of each walkthrough at the point of implementation. Answers to potential questions can be found in Asked Questions
Swagger allows you to describe the structure of your APIs so that machines can read them.
The Swagger spec for the current version of the Book a Secure Move API can be found at:- https://api.bookasecuremove.service.justice.gov.uk/api-docs/index.html.
This spec covers the fine-grained detail of each of the methods and data structures available in the API. Consumers are not necessarily expected to implement all of these. The Swagger spec is instead best used as a reference for those methods and data structures that are required to fulfil a need.
There are several webhooks that can be configured as part of Book a Secure Move. They are documented here - Webhooks
The source code for the Book a Secure Move API is publicly available on GitHub at this URL: https://github.com/ministryofjustice/hmpps-book-secure-move-api.
Please see the Readme in that repository for more information.
The whole service is designed around moving people safely. The people in this context may be prisoners, detainees, children or a young person. In this guide, we will refer to Person (plural either People or Persons) to refer to whichever of these is being moved.
The unit of reference when booking a location transfer for a Person will be a Move. This could be one of a number of different types of Move (for example Prison to Court, Court to Prison, Prison to Prison, and others), and encapsulates the purpose for the move, as well as the intended start and end location, regardless of intermediate stops. A Person may have multiple Moves on the same day - especially if they are for a different purpose. It is also possible that a Move could last for longer than a single day if it starts late at night and overnight lodging is necessary.
A Journey is a unit of travel from one location to another that forms a part of a Move. There will always be at least one Journey as part of a completed Move, but in some cases more. For example, a Move that takes place over two days will often have a Journey from the pickup location to a holding location, followed by another Journey from the holding location to the destination location.
Each of the Moves and Journeys within the service have a start point and an end point. These are Locations. In this service, every Location is pre-defined (with the exception of some unplanned emergencies), where the reference data comes from NOMIS. Locations are normally Police Stations (Custody Suites), Prisons, Courts, Secure Children's Homes (SCH), Secure Training Centres (STCs) and Young Offender Institutions (YOIs)
If a Person has multiple Moves over time, the information about that Person may change. In particular, new or changed risks or assessments may become apparent. In order to represent the specific information relevant to a particular Move, we have the concept of a Profile. A Profile is a subset of information about a Person that is related to a particular Move. A Move will always be linked to a single Profile, but it is possible for a Profile to be linked to several Moves (for example, if a person has two Moves on the same day and nothing changes between them).
As part of a Move or a Journey, there may be a need to record that something has happened, or that something has changed. These are captured in this service as Events.
A handover is the process of passing custody of the Person from one party to another. It may come with sign-off or other documentation.
As part of a Person being moved, they will often take with them some personal items. These can be captured as part of the Person Escort Record (see Person Escort Record).
An Allocation is a construct designed to facilitate the movement of People between establishments (usually prisons) before the exact People to be moved are known. They are commonly used in order to address capacity issues. Normally, suppliers would not create Allocations, but may need to be able to prepare resources in order to handle them.
Abbreviation | Description |
---|---|
IPT | Inter Prison Transfer |
PER | Person Escort Record |
PMU | Population Management Unit |
PNC | Police National Computer (number) |
PN | Prison Number |
PTR | Person Transfer Request |
SCH | Secure Children's Home |
STC | Secure Training Centre |
YOI | Young Offenders Institution |
There are two elements of the API that have security requirements - the Book a Secure Move API itself, and (optionally) the Webhooks. For Webhook security, see Webhooks.
The API is secured using an API key. These are generated by MoJ, and distributed to suppliers as required. The API key itself is then hashed by MoJ and the hash is stored. It is then not possible to recover the API key from the hash.
For production environments, API keys must be transferred securely. The recommendation is that the suppliers generate a GPG public-private key pair, then send the public key to MoJ, who will generate a securely random string, encrypt with the public key, and send the encrypted key back to the supplier. In addition, MoJ will create a secure hash from the API key and store this. The supplier will decrypt the key using their private key, and it will be sent in an authorisation header. MoJ will take the hash of the sent key and compare it to the one on file.
(For production webhook credentials, it would be appropriate to use a similar mechanism).
Supplier systems will authenticate with the APIs using OAuth2.
Each Supplier will be given
- A client ID
- A client secret
These will be sent to the authorization server, and exchanged for a token, using the OAuth2 client credentials grant type:
POST /oauth/token HTTP/1.1
Host: pecs-platform.example.com
grant_type=client_credentials
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx
The token will be supplied with an expiry time (60 minutes in this example), after which a new token must be requested.
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type": "bearer",
"expires_in": 3600
}
This token can then be used as a Bearer token to authorize requests to the API.
GET /ping HTTP/1.1
Host: pecs-platform.example.com
Authorization: Bearer MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3
If the supplied credentials are invalid, the OAuth2 spec requires that we return the following response.
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error":"invalid_client"
}
Provision will be made to allow these credentials to be replaced and rotated.
This will mean that two Client Secrets may be in use at the same time, and both can be used to create a token.
Once the Supplier’s systems have been updated to use the new Client Secret, the old Client Secret will be disabled, at which point it will no longer work to create a Token.
Once the Token expires, it will give a “401 Unauthorized” error
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"errors": [
{
"status": "401",
"title": "unauthorized",
"detail": "Token invalid"
}
]
}
The Supplier’s System should now request a new Token.
Supplier Systems must use secure mechanisms to authenticate and authorise all end users.
There may be a requirement to supply details of the supplier user that is carrying out actions in the supplier system, but as of now, that requirement is not present. Details will be added if that becomes the case. Some Events may allow/require details to be entered of the staff involved in the event, and these would be captured as attributes in the message body.
There is a separation between the Authentication APIs and the PECS APIs.
The Authentication endpoints use standard OAuth2, and therefore use the standard JSON content type.
Content-Type: application/json
The PECS API endpoints follow the JSON:API 1.0 spec (https://jsonapi.org).
All requests should set the JSON:API Content Type.
Content-Type: application/vnd.api+json
There may also be a benefit in using JSON:API tooling to automatically navigate and parse the JSON responses.
A typical Single Resource response looks like this:
{
"data": {
// ids are uuids represented as strings
"id": "c55edcaa-f27f-4a2e-bde9-37a70f939665",
// this typically matches the resource path - eg. /api/moves/{id}
"type": "moves"
// the attributes are listed here
"attributes": {
// note this "type" is an attribute of the Move
// and does not conflict with the above "type" that states it is a "move"
"type": "prison_to_court",
// for simplicity attributes may be included in the response
// and nested many levels deep
"person": {
"id": "5da78c83-4882-4704-a015-78d350d4618b",
"details": {
"surname": "Ting",
"forenames": "Tess T"
}
}
}
}
}
When multiple Resources are returned, they will be sent as an array inside the data field.
The structure of each Resource is the same as for a Single Resource.
{
"data": [
// each resource specifies its type, id, and attributes
{
"type": "organisations",
"id": "example-supplier",
"attributes": {
"label": "Example Supplier",
"description": "Example Supplier PLC - Justice Division"
}
},
{
"type": "organisations",
"id": "moj",
"attributes": {
"label": "MOJ",
"description": "Ministry of Justice and associated bodies"
}
}
]
}
When an endpoint returns a large number of resources, it will be paginated.
Paginated resources include metadata on the total number of pages, and includes links to access the first, last, next, and previous pages.
By default all paginated resources will use a page size of 20.
This can easily be increased by setting the page[size]
parameter, however, the Platform will set a maximum page size for each resource, depending on performance and payload size.
{
"meta": {
// the number of pages
"total_pages": 42,
// the number of records
"total_count": 42,
// the current set page size (may be defaulted)
"page_size": 1,
// the current set page number (defaults to 1)
"page_number": 3
},
"data": [
{
"id": "c55edcaa-f27f-4a2e-bde9-37a70f939665",
"type": "moves"
}
],
"links": {
"self": "https://pecs-platform.example.com/api/moves?page[number]=3&page[size]=1",
"first": "https://pecs-platform.example.com/api/moves?page[number]=1&page[size]=1",
"prev": "https://pecs-platform.example.com/api/moves?page[number]=2&page[size]=1",
"next": "https://pecs-platform.example.com/api/moves?page[number]=4&page[size]=1",
"last": "https://pecs-platform.example.com/api/moves?page[number]=42&page[size]=1"
}
}
Dates and Times should be represented as strings according to the format specified by ISO8601.
For example, a date and timestamp should be formatted as below:
{
"date": "2020-08-29",
"time_with_zone": "2020-08-29T14:52:41+01:00",
"time_in_utc": "2020-08-29T13:52:41Z"
}
We will use HTTP status codes to express errors semantically.
- 200 - everything is ok
- 201 - everything is ok, and we've created a resource
- 202 - the data has been accepted, but will be processed later
- 204 - everything is ok, but there's no content to return
- 404 - the page or resource doesn't exist
- 400 - the data sent is incomplete or malformed - maybe a missing or unsupported parameter
- 422 - the data sent is complete and formatted correctly, but invalid - eg a number is too large
- 409 - the data is valid, but there is a conflict - most likely the resource already exists
- 401 - the token is invalid or expired - get a new token
- 403 - the token is valid, but permissions are insufficient - speak to us
- 500 - unexpected error - try again with backoff
- 503 - the servers are temporarily unavailable - try again with backoff
Error responses are presented as an array of error messages.
We will add details and metadata to help explain the error.
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"errors": [
{
"status": "422",
"title": "validation failed",
"detail": "surname too long",
"meta": {
"attributes": {
"surname": "Just for illustration, 56 characters may not be too long"
}
}
}
]
}
Multiple errors may be expressed in the same response.
In the case that there are multiple errors of different kinds, the most appropriate status code will be used.
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"errors": [
{
"status": "422",
"title": "validation failed",
"detail": "surname too long"
},
{
"status": "400",
"title": "invalid date format",
"detail": "date of birth was invalid"
}
]
}
From Version 2 of the API onwards, the client must provide an Accept header which includes the version of the API in it.
Accept: application/vnd.api+json;
version=2
The previous V1 path-style versioning will be deprecated.
As the Book a Secure Move API evolves, more features will be added, and features that already exist will change. Both Suppliers and the MoJ must be prepared to handle these changes, and to manage them carefully.
There will broadly speaking be two kinds of changes - "non-breaking" changes, where consumers should be able to use the new version without modifying their usage, and "breaking" changes where we would expect that consumers would not be able to seamlessly use the new version.
We would like to minimise the number of breaking changes, and where this is not possible, we will increment the API version and manage the migration.
- MoJ - must not change or remove an existing data element unless we are certain it is not in use
- MoJ - must update documentation to reflect any changes
- MoJ - may add new elements and change/remove elements that we know are not in use
- Suppliers - implementation must accept new elements and changes to elements that are not in use (i.e. it must be resilient to non-breaking API changes)
Where a non-breaking change is not possible, there is then a breaking change. In this case, we will increment the version of the API
- MoJ - must update documentation, and explicitly call out the change between versions
- MoJ - must distribute the new documentation as soon as practical
- MoJ - must run both versions of the API, as long as this is practical, for at least 1 month
- Suppliers - must commit to moving to the new version of the API within a reasonable timescale, usually 1 month
- Depending on the size of the change, MoJ may require the Supplier to go through additional re-assurance steps
In order to supply a stable, secure, safe, and reliable service, Suppliers systems will need to be resilient to system outages, and in extreme cases to failover to paper based processes.
Moving is event-based and recorded asynchronously.
We must assume that Events can be received out of order and with variable latency (from milliseconds in the best case, to multiple days when things go wrong). Messages may be sent on the road, where connectivity may be less reliable. Messages may have to be queued and/or resent multiple times
Our APIs will receive events, potentially out-of-order and duplicated, and attempt reassemble them into an accurate depiction of events.
To avoid any duplication of Event updates, each endpoint accepts an Idempotency header. This should be a randomly-generated uuid/guid.
IDEMPOTENCY-KEY: aaacd97f-cf69-438d-b387-db3bffd1363c
This can be set to any value, and we will ensure that no two requests are accepted with the same key for a reasonable amount of time (currently: 1 hour).
In the case that the IDEMPOTENCY-KEY has already been used within the time period, an error will be returned:
HTTP/1.1 409 Conflict
{
"errors": [
{
"status": "409",
"title": "conflict",
"detail": "IDEMPOTENCY-KEY already used"
}
]
}
Because of potential connection issues and the queuing of messages, event-driven messages may be received in the wrong order. Each message should therefore be correctly timestamped in order for a meaningful chain of events to be drawn. Each message should concern itself only with the specific event in question, as opposed to attempting to maintain a complete or meaningful state. Validation of messages will be sufficiently liberal so as not to reject them based on the present state: i.e. a message to indicate a handover has taken place should not be rejected if it is received before the message to indicate a journey is in progress.
However, calls to update a specific record cannot be requested until the call to create the record has completed, as the id of the record will not be known until it is persisted. It is the responsibility of the supplier / client application to ensure that a move or journey is created before it is updated. It may be necessary to implement client-side queueing in order to achieve this.
- API Guide
- Version 2
- Asked Questions
- Webhook & Email notifications
- Walkthroughs
- Deployment
- Useful Queries
- Data quality improvements
-
Journeys and Payment Related Calls
- Single journey move
- Redirection before move commences
- Redirection after move commences
- Lockouts and Lodgings
- Assessments
- Event Documentation
- GPS Track a move