API Documentation

Introduction

The People First API is a collection of smaller APIs that fall into three distinct API categories, namely:


Within those categories, the APIs are further broken down into a set of context specific areas, e.g. 'hrm', 'talent', learning, recruitment. There is also a 'global' context that provides entry points and resources that aggregate data from multiple contexts.

API Requests

People First API requests are made to one of a number of RESTful endpoints. All API requests need to provide, as a minimum, the organisation's tenant code, environment code, and ther caller's authentication credentials. A simple HTTP request to a user's global profile endpoint might look like this:

GET /api/profile HTTP/1.1
        Host: TENANT_CODE-ENVIRONMENT_CODE.people-first.com
        Accept-Language: en-GB
        Content-Type: application/json; charset=utf-8
        TenantCode: TENANT_CODE
        EnvironmentCode: ENVIRONMENT_CODE
        Authorization: Bearer ACCESS_TOKEN
        

Where:

  • TENANT_CODE is the tenant code that was provided by MHR.
  • ENVIRONMENT_CODE is the environment code that was provided by MHR. When accessing your default environment (typically your production environment) the -ENVIRONMENT_CODE hostname segment, and EnvironmentCode HTTP header field can be omitted.
  • ACCESS_TOKEN is the access token supplied by your System Administrator, or ID token obtained separately (see:OIDC ID Tokens).

An example cURL command to construct the above request would be something like:

curl --location --request GET "https://tpruk-dev.people-first.com/api/profile" \
            --header "TenantCode: tpruk" \
            --header "EnvironmentCode: dev" \
            --header "Accept-Language: en-GB" \
            --header "Content-Type: application/json; charset=utf-8" \
            --header "Authorization: Bearer eyJ0eXAiOiJKVMsntWbLxWnNnRNZva2iueYc7KTFKP1Kz6Paog
        

Note: The URI segment: https://TENANT_CODE-ENVIRONMENT_CODE.people-first.com is commonly referred to as the "People First Base URI"

Authentication

People First APIs require authentication in order for an application to access them and make API calls. An API client authenticates itself with People First by providing a valid token within the Authorization header of each HTTP request (see: HTTP/1.1 Authentication for further details of the Authorization header).

The following types of token are supported within the Authorization header:

People First Access Token (recommended)

Access Tokens are generated in the admin area of People First by a user with a system administrator role. For details of how to do this, please see the REST API Integration section.

Depending upon the API being consumed, access tokens should be supplied within the HTTP Authorization header using one of two HTTP authentication schemes:

Bearer Authentication (recommended)

When using the Bearer Authentication scheme the header will be in the form:

Authorization: Bearer ACCESS_TOKEN

Where:

  • ACCESS_TOKEN is the access token supplied by your System Administrator.
Basic Authentication

When using the Basic Authentication scheme the header will be in the form:

Authorization: Basic USERNAME:ACCESS_TOKEN

Where:

  • USERNAME:ACCESS_TOKEN is base 64 encoded.
  • USERNAME will be ignored, so can be omitted. However, the : must always be included.
  • ACCESS_TOKEN is the access token supplied by your System Administrator.

OpenID Connect ID Token

An OpenID Connect (OIDC) ID token represents the identity of a People First User. They allow an application to call the People First APIs as though they were that user, inheriting whatever roles and permissions have been assigned to that user. They are only valid for a limited timespan before they need to be refreshed. Typically, ID tokens are used for one-time ad-hoc requests for data.

ID tokens are obtained from a tenant's registered identity provider and take the form of a JSON Web Token (JWT) signed by that provider. They can be requested from an identity provider by various methods, as outlined in the OpenID specifications. Usually, a JWT ID token will be captured via a browser-based flow with an actual user performing authentication.

JWTs should be supplied within the HTTP Authorization header using the HTTP Bearer authentication scheme. A typical API request is shown below:

GET /api/v1/profile HTTP/1.1
Host: eu.peoplefirst.com
Authorization: Bearer ID_TOKEN

Where:

  • ID_TOKEN is the ID token obtained from the tenant's registered identity provider.

REST API Integration

Overview

Applications need to be configured within the People First system before they can be granted access to the REST APIs, and an access/bearer token issued.

Application Configuration

Applications are configured using the Create Inbound Integration dialog screen. This can be accessed by pressing the + symbol against the Inbound Integration side menu item within the Settings page of the Administration App.




To configure an application, as a minimum, you will need to supply:


Field name Description
External application name A human readable name for the application.
Grant user roles access to A list of roles that are to be granted to the application - these will determine which areas of the system, and data entities can be accessed by the application.
Active A flag to indicate whether the application access is active or not.

If you are using the client credentials flow to request an access token, instead of generating one manually, you will also need to specify:


Field name Description
Application ID The application ID (aka client ID) of the application within the identity provider being used.
Authentication endpoint URI This is the Issuer Identifier URL for the identity provider issuing the access token. The OIDC Provider Configuration document (see: Obtaining OpenID Provider Configuration Information) must be available at the URL formed by appending /.well-known/openid-configuration to this URI.

Access Token Generation (recommended)

The recommended method for obtaining an access token is to generate one from within the People First System Settings area. This can be achieved by pressing the Generate Access Token button in the Inbound Integration panel for the application you wish to create a token for, or by pressing the + symbol against the application name underneath the Inbound Integration side menu item.




This will bring up the Generate Access Token page.




You will be required to enter the following details:


Field name Description
Access token name - this is a human readable name for the token being generated.
Grant user roles access to A list of roles that are to be granted to the application - these will determine which areas of the system, and data entities can be accessed by the application using this specific access token.
Token valid until The access token will expire at 00:00 on this date.

Upon selecting Create, you will be presented with a dialog displaying the generated access token. You will not be able to view the access token once this dialog has been dismissed.




Access Token Requesting

An alternative method for obtaining an access token is to request one from an authentication/authorisation server using the OAuth2 client credentials flow (see: rfc6749. This is a much more complex procedure and generally only required where a generated token cannot be supported, such as with some third-party partner integrations.

The general flow is as follows, the client makes a request to an authorization server for an access token passing a client id, a client secret and a resource. These will be provided by MHR to partners. It is the responsibility of the client to keep these credentials safe.




The client can then pass this access token to the server protecting the resource (i.e. the API) which will use the access token to allow or deny access depending on the contents of the token.

Webhook API Integration

Overview

People First can be configured to send real-time events to external systems (see: Event Stream API). This is commonly used to satisfy real time systems integration requirements. People First uses Webhook callbacks to deliver the events. Webhooks can be configured for any event type and are secured with a HMAC signature.

Configuration

Webhooks can be configured through the webhook configuration section in the administration area. To configure a new webhook, press the + symbol within the Webhook side menu item within the Settings page of the Administration App.



You will be required to enter the following details:


Field name Description
Title A name to identify the webhook.
Webhook Url HTTPS endpoint URI where the webhook calls (requests) will be POST(ed). The endpoint must use TLS (HTTPS).
Events to send A list of events that will be sent to the webhook endpoint. The events are listed here.
Active Status of the webhook. Events will only be sent to the webhook endpoint if this is active.
Generate Access Key This button is used to generate and display (one-time) the HMAC key. This key is required to verify the HMAC Signature and must be securely stored.

Once your webhook is configured, you will need to generate an access (HMAC) key. This will enable you to verify the authenticity of the webhook messages you receive.

To do this, press the Generate Access Key button within the configuration page of your newly created webhook.



You will then be presented with a dialog displaying the generated access key. You will not be able to view the access key once this dialog has been dismissed.



NOTE: It is imperative that the access (HMAC) key is stored securely. If it is compromised, then a malicious attacker could use the key to mimic requests that appear to originate from People First. People First will only display the HMAC key at the point of generation, after which the key is encrypted and never again surfaced over the api. If the key is lost, or there is any risk it may have been disclosed, or compromised, then a new key must be generated by pressing the "Generate Access Key" button within the configuration page of the affected webhook.

HMAC Signature Verification

All People First webhook request messages are digitally signed using a hash-based machine authentication code (HMAC). This allows the webhook consumer to verify that the request originated from People First and that it's content wasn't changed in transit.

All webhook requests will contain a HMAC signature. The HMAC signature is provided within the peoplefirst-signature HTTP header of the webhook request.

In order to verify that the request is from a trusted source follow these steps:

  1. Base64 decode the HMAC key generated for the webhook.
  2. Hash the HTTP request body using the decoded HMAC key from step 1, and the HMAC-SHA256 algorithm. The body will be UTF8 encoded.
  3. Base64 encode the hash computed in the step 2.
  4. Compare the base64 encoded value computed in step 3 with the value obtained from the peoplefirst-signature HTTP header.
  5. If the two values match, then the request was sent from People First and it's content hasn't been modified.


// A Postman Test that calculates the HMAC hash and verifies that it matches the supplied signature
//
// The signature has already been extracted and stored in the "peoplefirst-signature" variable.
// The HMAC key has already been stored in the "PeopleFirstEventStreamHmacKey" variable.
//
let peopleFirstSignature = pm.variables.get("peoplefirst-signature");

pm.test("verify signature", function(){

    let base64EncodedHmacKey = pm.variables.get("PeopleFirstEventStreamHmacKey");
    let decodedHmacKey = CryptoJS.enc.Base64.parse(base64EncodedHmacKey);
    let sha256signature = CryptoJS.HmacSHA256(pm.response.text(), decodedHmacKey).toString(CryptoJS.enc.Base64);

    pm.expect(sha256signature).to.eql(peopleFirstSignature);
});



  # A PowerShell script that calculates the HMAC hash and verifies that it matches the supplied signature

  $peoplefirstWebhookHmacKey = "WEBHOOK_HMAC_KEY>"
  $peoplefirstSignature = "WEBHOOK_SIGNATURE"
  $body = 'WEBHOOK_REQUEST_BODY'
  
  
  # Step 1. Base64 decode the HMAC key generated for the webhook.
  $decodedHmacKeyBytes = [System.Convert]::FromBase64String($peoplefirstWebhookHmacKey)
  
  # Step 2. Hash the HTTP request body using the decoded HMAC key from step 1, and the HMAC-SHA256 algorithm. The body will be UTF8 encoded.
  $hmacSha = New-Object System.Security.Cryptography.HMACSHA256
  $hmacSha.key = $decodedHmacKeyBytes
  $hashedBody = $hmacSha.ComputeHash([Text.Encoding]::UTF8.GetBytes($body))
  
  # Step 3. Base64 encode the hash computed in the step 2.
  $base64EncodedHashedBody = [Convert]::ToBase64String($hashedBody)
  
  # Step 4. Compare the base64 encoded value computed in step 3 with the value obtained from the peoplefirst-signature HTTP header.
  if ($base64EncodedHashedBody -eq $peoplefirstSignature)
  {
      Write-Host "Signatures MATCH : " -NoNewline
  }
  else
  {
      Write-Host "Signatures DO NOT MATCH : " -NoNewline
  }
  
  Write-Host "$base64EncodedHashedBody $peoplefirstSignature"
  

API Authorisation

The authorisation of API requests is governed by the assignment of one or more roles to either the application itself (if an OAuth2 client credentials flow is being used), or to the People First generated access token that the application supplies. If using an ID token then authorisation will be governed by the roles and permissions assigned to the user who granted that ID token.

The following roles are currently available:


Custom Cards Integration
  • Provides access to the Custom Card OData and import endpoints.

HRM Integration
  • Provides access to HRM import endpoints and several HRM resource endpoints (e.g. People, Unit, Job, and Occupancy related).

IAM Integration
  • Provides access to selected HRM Occupancy resource endpoints, and IAM resource and import endpoints.

Learning Integration
  • Provides access to the Learning OData endpoints.

Recognitions Integration
  • Provides access to the Recognitions OData endpoints.

Recruitment Integration
  • Provides access to the Recruitment OData endpoints.

Socialfeed Integration
  • Provides access to the Social Feed OData endpoints.

Talent Integration
  • Provides access to the Talent OData and selected resource endpoints.

Workforce Management Integration
  • Provides access to selected Time and Attendance resource and import endpoints.

System Integration
  • Provides access to the majority of People First API endpoints, including the Event Stream API.

Custom Cards Imports
  • Provides access to Custom Card import and export endpoints.

HRM Imports
  • Provides access to the HRM import endpoints

Talent Data Imports
  • Provides access to the Talent import endpoints.

API Compatibility Considerations

The People First APIs are under continuous development and are constantly evolving as new functionality is developed. Wherever possible, changes to the current version of an API will be made in such a way as to maintain backward compatibility with existing consumers. Where this is not possible, a new version of the API will be created and the compatibility breaking changes incorporated into it (leaving the existing version unchanged). The mechanism for working with different API versions is detailed in the API Versioning section.

As an API consumer you need to be aware of the type of changes that are considered to be backwards compatible, and code your application to handle them. This applies to all People First APIs including the Domain APIs, OData APIs, and the Event Stream API.

The following changes are considered to still maintain backward compatibility and not require a new API version:

  • New resources (endpoints).
  • New HTTP methods on existing resources.
  • New attributes, links, or elements on existing data types within a resource.
  • New optional query parameters.
  • The support of new Content-Types in the body of responses (the use of that Content-Type having been indicated in the associated Accept request header).

API Versioning

People First APIs use a versioning scheme to allow clients to migrate smoothly to newer versions when new functionality is added to the People First product. The versioning scheme is based on an HTTP header named Api-Version. When this header is absent the request is treated as if it is requesting version 1 of the API. For later versions of APIs the Api-Version header must be supplied as per the example request below for a v2 API.

GET /api/v1/profile HTTP/1.1
Host: eu.peoplefirst.com
Authorization: Bearer ACCESS_TOKEN
Api-Version: 2

Where:

  • ACCESS_TOKEN is the access token supplied by your System Administrator, or ID token obtained separately (see:OIDC ID Tokens).

Note: The "v1" portion of the URI has no impact on the version of the API requested. This portion of the URI will be removed in the future.

When an API is superseded by a new version there will be a period of time where both old and new versions are supported in parallel to allow clients time to migrate to the newer version. Deprecation of APIs will be documented on this site and will also be communicated to customers to allow them fair notice to migrate.

Where APIs are documented without a version it should be assumed the API is available as version 1. Where versions later than 1 exist these will be explicitly documented.

Entry Points

These are well-known URLs which provide a fixed starting point in the API to determine the functionality available and obtain URLs for navigation to it.

The /profile entry point is like the 'Home Page' for the people first API, it provides information about your user, locale preferences and links into the functionality available to you.

A profile entry point is also exposed in the Admin, Custom Cards and Talent modules e.g. /admin/profile, providing access into the functionality specific to that area.

These endpoints answer the question 'Who am I and what can I do?' in the people first application.

Aggregations

Aggregations are provided for well-known concepts which cross API modules in order to simplify the navigation to related functionality.

Name Endpoint Purpose
Person Profile /personprofile/{personId} Provides core information and links to functionality relating to the specified person
Search /search Exposes a search for people and departments, providing navigation into functionality related to the results
Search Groups /searchgroups Provides all the available filters that can be applied when searching for people

Data Types

  • string: Variable length UTF-8 encoded string
  • number: Number compliant with the JSON specification
  • boolean: Boolean compliant with the JSON specification
  • dateTime: These attributes have names appended with 'DateTime' and are ISO 8601 compliant strings which must always be UTC
  • date: These attributes have names appended with 'Date' and are ISO 8601 compliant strings in the format 'YYYY-MM-DD'
  • time: These attributes have name appended with 'Time' and are ISO 8601 compliant strings with no date component

Example fictional resource:

{
  "data": {
    "person": {
      "id": "031c0575-ed2e-4e04-bd01-cc7565045d8b",
      "firstName": "Susan",
      "lastName": "Smith",
      "children": 2,
      "bornDateTime": "1980-06-15T13:28:00Z",
      "employedDate": "2001-07-12",
      "startTime": "13:06:12",
      "isMarried": true,
      "_links": {
        "self": {
          "href": "people/031c0575-ed2e-4e04-bd01-cc7565045d8b"
        }
      }
    }
  }
}

Status Codes

The table below describes the possible response codes to API requests, why the code would be returned and if it would contain an error message.

Code Methods Meaning Message
200 GET, PUT The request was successful (and a resource was not created or deleted) Optional
201 POST, PUT A request to create a resource was successful Optional
202 POST, PUT A request has been accepted for future processing No
204 DELETE A request to delete a resource was successful No
400 All The request was not valid, it may be missing appropriate tenant headers, or it had a malformed payload Required
401 All Authentication was required but was either not provided or invalid No
403 All You do not have access to this resource Optional
404 All The requested resource does not exist, or you are not authorised to access it No
405 All The method used is not allowed No
409 PUT, DELETE The request was not processed because of a concurrency issue, most commonly the 'If-Match' header was required but not provided Optional
412 PUT, DELETE The request was not processed because a 'If-Match' request header condition was not met, see concurrency) Optional
500 All An internal error occurred Required
503 All The API is temporarily unavailable No

Concurrency

To prevent concurrency issues when updating or deleting resources an If-Match header is expected on the request, which should be set to the current version of the resource. The resource version is provided in the ETag header on a GET request.

If a request to update or delete a concurrency-controlled resource does not contain a If-Match header a 409 status code will be returned. If the header is provided and does not match the latest version a 412 status code is returned.

Errors

API Requests may result in an error, either due to an invalid request or because of an internal error.

When this happens the status code of the response will indicate the nature of the error and the response may contain error messages describing the error(s).

Error messages may contain the following attributes:

  • id: In the case of an internal error (500) this is a unique identifier for the exception occurrence, otherwise it's a code for the type of error.
  • text: A localised description of the error
  • object: If the source of the error is a data attribute on the request, this points to the attribute(s) (not applicable to internal errors)

See the example below showing a set of errors that have been returned from an invalid request to update the fictional insurance resource.

{
  "msg": {
    "error": [
      {
        "id": "0037INSVALIDFROM",
        "object": [
          "insurance.validFrom",
          "insurance.validTo"
        ],
        "text": "Insurance 'Valid from' must be before the 'Valid to' date"
      },
      {
        "id": "0013NOPERSON",
        "text": "The person related to this record does not exist"
      }
    ]
  },
  "meta": {
    "links": {
      "self": {
        "href": "insurance"
      }
    }
  }
}
                    

Pagination

Collections of resources may be paginated; these collections provide additional meta data and hypermedia links allowing the navigation of the paginated set.

The additional links provided are:

  • first: the first page of data
  • last: the last page of data
  • prev: the previous page of data
  • next: the next page of data

The additional meta data attributes provided are:

  • totalPages: the total number of pages in the set
  • pageNumber: the current page number
  • pageSize: the number of resources returned per page
  • maxPageSize: the maximum number of resources that can be requested on a page
  • count: the total number of resources in the set

The following query string parameters can be provided to control the pagination of the returned results:

  • page[offset]: The page index to return (zero indexed)
  • page[limit]: The number of resources to return per page

Example of a paginated collection:

{
  "data": {
    "people": [
      {
        "id": "031c0575-ed2e-4e04-bd01-cc7565045d8b",
        "type": "person",
        "firstName": "Susan",
        "lastName": "Smith",
        "_links": {
          "self": {
            "href": "people/031c0575-ed2e-4e04-bd01-cc7565045d8b"
          }
        }
      },{
        "id": "80e3ef54-2905-42f7-87ac-eaaed55df144",
        "type": "person",
        "firstName": "David",
        "lastName": "Jones",
        "_links": {
          "self": {
            "href": "people/80e3ef54-2905-42f7-87ac-eaaed55df144"
          }
        }
      }
    ]
  },
  "meta": {
    "totalPages": 5,
    "pageNumber": 2,
    "pageSize": 2,
    "maxPageSize": 100,
    "count": 10,
    "links": {
      "self": {
        "href": "people"
      },
      "next": {
        "href": "people?page%5Boffset%5D=4&page%5Blimit%5D=2"
      },
      "prev": {
        "href": "people?page%5Boffset%5D=0&page%5Blimit%5D=2"
      },
      "last": {
        "href": "people?page%5Boffset%5D=8&page%5Blimit%5D=2"
      },
      "first": {
        "href": "people?page%5Boffset%5D=0&page%5Blimit%5D=2"
      }
    }
  }
}
                

HATEOAS

What is HATEOAS?

HATEOAS stands for Hypermedia As The Engine Of Application State. HATEOAS is an architectural constraint of REST based APIs whereby in response to requests from the client the server provides not only resource data, but also hyperlinks which allow the client to dynamically traverse various related resources kept on the API server merely by association.

What benefit does HATEOAS provide?

HATEOAS can make a REST application self discoverable by the consumer, meaning the consumer need only know a single fixed entry URI and thereby the hypermedia links provided dynamically by each subsequent response should allow any and all resources provided by the REST application to be discoverable by traversing the associated links. These restrictions imposed by HATEOAS decouple the client and server meaning that both the frontend and backend designs can evolve independently and allow for much better scalability. HATEOAS also applies a standardised design to the REST application meaning there is little to no need for documentation.

What kind of hypermedia links are provided by People First API responses? and how can these be utilised by PF API integrators?

If you haven't yet read through this websites quick start guides for authentication and first API calls then click here and read those first. It will give you a better understanding of this section and its applications.

Within the guide you will have sent a request to the People First APIs for 'Create Person', the example response object can be viewed below and its full content can be fully accessed by the using the scroll bar to the right-hand side of the viewing window:


{
    "meta": {
        "links": {
            "self": {
                "href": "/hrm/people"
            }
        },
        "person.personalReference": {
            "disabled": "true"
        },
        "person.lastName": {
            "mandatory": "true"
        },
        "person.firstName": {
            "mandatory": "true"
        },
        "person.title": {
            "mandatory": "true",
            "values": [
                {
                    "value": "Mr",
                    "id": "9e188b9d-737b-4c9d-92bb-199d8a6fba5c",
                    "code": "TITLE0001"
                }
            ]
        },
        "person.socialSecurityNumbers": {
            "mandatory": "true"
        },
        "person.socialSecurityNumbers.legislation": {
            "mandatory": "true",
            "values": [
                {
                    "value": "United Kingdom",
                    "id": "47c23873-057b-4b22-b2d9-a1a31ccec3fb",
                    "code": "UK"
                }
            ]
        },
        "person.socialSecurityNumbers.socialSecurityNumber": {
            "mandatory": "true"
        },
        "person.startDate": {
            "disabled": "true",
            "hidden": "true"
        }
    },
    "data": {
        "person": {
            "_links": {
                "createBannerImage": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/bannerimage/resourcetemplate"
                },
                "disabilityInformation": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/disabilityinformation"
                },
                "sensitiveInformation": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/sensitiveinformation"
                },
                "team": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/team?page%5BLimit%5D=10&page%5BOffset%5D=0"
                },
                "teamPeople": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/team/people?page%5BLimit%5D=10&page%5BOffset%5D=0"
                },
                "photoUpload": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/photo"
                },
                "attachments": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/attachments",
                    "template": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/attachments/resourcetemplate"
                },
                "notes": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/notes",
                    "template": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/notes/resourcetemplate"
                },
                "events": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/events"
                },
                "absenceTypes": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/absencetypes"
                },
                "contacts": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/contactinfo"
                },
                "absenceDisabledPeriods": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/globalAbsenceDisabledPeriods"
                },
                "createHolidayAbsence": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/absences/absencetypes/c7a9ef1a-69a3-4cf6-bd82-67a34d3a9c2d/resourcetemplate"
                },
                "createSicknessAbsence": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/absences/absencetypes/31467a35-a27b-45dd-be22-629816d9aa93/resourcetemplate"
                },
                "transfer": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/transferrequest/resourcetemplate"
                },
                "startOccupancy": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/occupancies/resourcetemplate"
                },
                "rightToWork": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/righttowork"
                },
                "passports": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/passports"
                },
                "residencyPermits": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/residencyPermits"
                },
                "emergencyContacts": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/emergencyContacts"
                },
                "createEmergencyContacts": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/friendsfamily/resourcetemplate"
                },
                "friendsFamily": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/friendsfamily"
                },
                "createFriendsFamily": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/friendsfamily/resourcetemplate"
                },
                "drivers": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/drivers"
                },
                "createDrivers": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/drivers/resourcetemplate"
                },
                "visas": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/visas"
                },
                "address": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/address"
                },
                "createAddress": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/address"
                },
                "peers": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/peers?page%5BLimit%5D=10&page%5BOffset%5D=0"
                },
                "personStatement": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/statement"
                },
                "editPersonStatement": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/statement"
                },
                "personManagerStatement": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/managerstatement"
                },
                "publicHolidays": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/publicholidays"
                },
                "employmentPeriods": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/employmentperiods"
                },
                "occupancies": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/occupancies"
                },
                "bankAccounts": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/bankaccounts"
                },
                "absenceProfile": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/absenceProfile"
                },
                "outOfOffice": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/colleagues/outofoffice"
                },
                "outOfOfficePeers": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/outofoffice/peers"
                },
                "outOfOfficeManagers": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/outofoffice/managers"
                },
                "employmentProfile": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/employmentProfile"
                },
                "employmentStatus": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/employmentstatus"
                },
                "editLeaveAllowance": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/absenceallowancedetails/effdate/2022-06-20"
                },
                "patternSchedule": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/patterntype/WP/schedule"
                },
                "absenceAllowanceScheme": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/absenceAllowanceScheme/effdate/2022-06-20"
                },
                "certificateOfSponsorships": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/sponsorshipcertificates"
                },
                "workPermits": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/workpermits"
                },
                "settlementStatuses": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/settlementstatuses"
                },
                "leaveOccupancy": {
                    "href": "/hrm/employmentperiods/38596d64-11c4-4525-9953-aeba007316d8"
                },
                "personSignatureHeld": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/signatureheld"
                },
                "self": {
                    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4"
                }
            },
            "personId": "7c24544e-2b36-495f-91b6-aeba007316a4",
            "firstName": "Edward",
            "lastName": "Norton",
            "title": "9e188b9d-737b-4c9d-92bb-199d8a6fba5c",
            "otherNames": "",
            "knownAs": "Edward",
            "pronouns": "",
            "previousLastName": "",
            "startDate": "2022-06-01",
            "personalReference": "A07",
            "socialSecurityNumbers": [
                {
                    "legislation": "47c23873-057b-4b22-b2d9-a1a31ccec3fb",
                    "socialSecurityNumber": "AB123457A",
                    "_status": "est"
                }
            ]
        }
    }
}
            

This JSON response object contains two initial member objects which are 'meta' and 'data'. The data object, as the name suggests, contains the response data relating to the request which called it. In this case we have made a request to create a person with the relevant data attached in the body, the person has been created by the functionality of the API then saved within the relevant database and then the response object has been returned to the client.

From the initial Create Person response object we are going to traverse a couple of the hypermedia links using the Postman requests which were used in the quick start guide referenced above (Making first call to People First API), as well as some of our own in order to demonstrate the HATEOAS principal using the People First APIs.



  "createDrivers": {
    "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/drivers/resourcetemplate"
  },
              

For this HATEOAS demonstration we shall be adding Drivers Details to the example Person (Edward Norton) as per the above response object. When we created our person we needed a resource template in order to provide the structure for our request body object. We will also require a 'Create Drivers Resource Template' to again provide the structure for our next request. If we were to use Dot Notation on the above response object to locate the required URI path for our request then the JSON object path would be 'x.data.person._links.createDrivers.href'. This will locate the href property in the above object called "createDrivers".



Driver Resource Template Request

We then copy our URI path value from the 'href' key and paste it into a new Postman 'GET' request immediately after the People First base URI as indicated above. Make sure your 'access-token' is valid, hasn't expired and is inserted as a header within the request, as per the quick start guide demonstration. Everything is now in place, so hit send.



{
    "meta": {
        "links": {
            "self": {
                "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/drivers/resourcetemplate"
            }
        }
    },
    "data": {
        "persondrivers": {
            "_links": {
                "create": {
                    "href":  "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/drivers"
                }
            },
            "countryId": "",
            "dateVerified": "",
            "disqualificationEndDate": "",
            "disqualificationStartDate": "",
            "isCurrentlyDisqualified": false,
            "licenceNumber": "",
            "carRegistrationNumber": "",
            "documentDiscriminator": "",
            "issuingStateId": "",
            "nameOnLicence": "",
            "validFrom": "",
            "validTo": "",
            "insuranceDateVerified": "",
            "insuranceValidFrom": "",
            "insuranceValidTo": "",
            "internationalDrivingPermitType": "",
            "internationalDrivingPermitExpiryDate": ""
        }
    }
}
              

The resource template required for adding driver details for our person can be seen above within the 'data' object, which is nested within the resource template response object. The object is called "persondrivers" (object name highlighted in blue). The response also contains a hyperlink for our next action to create the driver details (hyperlink highlighted in red).


Create driver details request

As we did with our previous request copy the URI path value from within the last response body received in reaction to the 'Drivers Details Resource Template'. The URI path is highlighted in red above. Paste the URI path value onto the end of your own People First base URI in a new Postman 'POST' request as indicated in the image above. As before ensure that your 'access-token' is valid and that your tenant / environment codes are correct.

Within your request, select 'Body' from the tabs below the request URI, and then click the 'raw' radio button then select 'JSON' from the dropdown menu at the end of the row. Extract the driver details resource template from the resource template response object and paste it into your Postman request body as demonstrated in the example above. Switch out the demo driver details for your details you wish to create. The fields which have been filled in are mandatory, so cannot be left blank. When you have completed all the details hit send.



{
    "meta": {
        "links": {
            "self": {
                "href": "/hrm/people/7c24544e-2b36-495f-91b6-aeba007316a4/drivers"
            }
        },
        "persondrivers.countryId": {
            "values": [
                {
                    "value": "United Kingdom",
                    "id": "5c586b7e-9417-4465-ab37-c8bb1471f967",
                    "code": "GBR"
                }
            ]
        }
    },
    "data": {
        "persondrivers": {
            "_links": {
                "self": {
                    "href": "/hrm/persondrivers/0c7cf253-5415-47dd-9399-aee500850a68"
                },
                "attachments": {
                    "href": "/hrm/persondrivers/0c7cf253-5415-47dd-9399-aee500850a68/attachments",
                    "template": "/hrm/persondrivers/0c7cf253-5415-47dd-9399-aee500850a68/attachments/resourcetemplate"
                }
            },
            "driverId": "0c7cf253-5415-47dd-9399-aee500850a68",
            "countryId": "5c586b7e-9417-4465-ab37-c8bb1471f967",
            "dateVerified": "",
            "disqualificationEndDate": "",
            "disqualificationStartDate": "",
            "isCurrentlyDisqualified": false,
            "licenceNumber": "NORT9701317TW9UB",
            "nameOnLicence": "Mr Edward Norton",
            "carRegistrationNumber": "YT02MYS",
            "documentDiscriminator": "",
            "issuingStateId": "",
            "personId": "7c24544e-2b36-495f-91b6-aeba007316a4",
            "validFrom": "1990-02-20",
            "validTo": "2040-02-20",
            "insuranceDateVerified": "",
            "insuranceValidFrom": "",
            "insuranceValidTo": "2023-03-12",
            "internationalDrivingPermitType": "",
            "internationalDrivingPermitExpiryDate": ""
        }
    }
}
              

Our example response can be seen in the above code extract. We can see that as well as returning the successfully created drivers details further hyperlinks have been returned to carry out further actions in relation to 'attachments' (highlighted in red). This brings the demonstration aspect an end. You should now have enough information to explore different resources using the People First APIs.

In this demonstration we have applied the basic principles of HATEOAS to the People First APIs. This has allowed us to explore different resources by traversing from one resource to another purely by using the hyperlinks provided in the response objects returned from the current PF API request.

Resource Templates

Introduction

The aim of this section of the API documentation is to explain the concept of a Resource Template. Before explaining what a Resource Template is in the context of the People First APIs let's first break this term down to understand its meaning and application.

What is a REST Resource?

A REST Resource can be almost any content which is returned from a REST application in response to an API endpoint request. A resource could be any of the follow formats:

  • Image
  • Video
  • HTML file or extract
  • XML object
  • JSON object

Resources returned from PF API endpoints will nearly always be JSON objects containing information retrieved from the PF API Databases.

What is a Template?

A template can be best described as a blueprint or design that provides a pre-determined structure, layout and / or style.

What is a Resource Template? and what are they used for?

A resource template is a blank version of a resource, which also provides the structure and layout of that resource. This is provided to the client to fill in and post back to create a new resource. Below is an example of a resource template to create a person. This example is in JSON format and is provided with all the fields blank bar a few which are auto-populated by the PF APIs.



{
  "person": {
    "_links": {},
    "firstName": "",
    "lastName": "",
    "title": "",
    "otherNames": "",
    "knownAs": "",
    "pronouns": "",
    "previousLastName": "",
    "startDate": "2022-06-01",
    "personalReference": "",
    "socialSecurityNumbers": [
      {
        "legislation": "47c23873-057b-4b22-b2d9-a1a31ccec3fb",
        "socialSecurityNumber": "",
        "_status": "new"
      }
    ]
  }
}
                

Metadata

What is Metadata?

Metadata is data that describes other data, but does not form part of the data it is describing. A simple example of this is the information that is displayed when you right-click on a file and select 'properties'. The properties view displays information such as the type of file, which application the file opens with, the size of the file and when it was created.

How is metadata utilised with People First APIs?

Let's take a look at one of the example requests which was the first to be carried out in the 'Quick Start Guide'. You don't have to have read the quick start guide in order to understand this explanation.

Displayed below is the 'data' element from the 'Create Person Resource Template' response object. The response object is made up of two child objects, 'data' and 'metadata', which can have further nested child objects or fields within them. The 'person' object within the 'data' object is what forms the actual resource template.

This resource template is a blank version of the resource which is ready to be populated with a person's data / info. These fields are pre-labelled to guide the user / client on what should go in the field e.g. 'firstName', 'lastName' and 'title'. Most of the fields are pretty intuitively labelled in order to guide the user as to what kind of information should be entered into the field, however what about the field labelled 'legislation'? It's not immediately obvious exactly what should be entered into this field to satisfy the criteria. This is where metadata comes in.



{
    "data": {
        "person": {
            "_links": {},
            "firstName": "",
            "lastName": "",
            "title": "",
            "otherNames": "",
            "knownAs": "",
            "pronouns": "",
            "previousLastName": "",
            "startDate": "",
            "personalReference": "",
            "socialSecurityNumbers": [
                {
                    "legislation": "",
                    "socialSecurityNumber": "",
                    "_status": "new"
                }
            ]
        }
    }
}

Displayed below is the 'metdata' part of the response object for 'Create Person Resource Template'. If we scroll down the metadata object about half way we can see the metadata nested object which relates to the 'legislation' field highlighted in red. This object provides information on which values can be entered into the legislation field. The first thing to note is that the field 'mandatory' has a value of 'true' meaning that this field cannot be left empty, whereas other fields where the value is 'false' can be left empty. Looking through the values array it is now easy to realise that 'legislation' is in reference to a country's legislation. For the purposes of this example we will be choosing the legislation of the 'United Kingdom', so we populate the legislation field with the 'id' within the 'United Kingdom' object ('47c23873-057b-4b22-b2d9-a1a31ccec3fb') and that field is then complete with valid information.



{
    "meta": {
        "links": {
            "self": {
                "href": "/hrm/people/resourcetemplate?annotate=t"
            }
        },
        "person.personalReference": {
            "hidden": "true",
            "disabled": "true"
        },
        "person.startDate": {
            "mandatory": "true"
        },
        "person.lastName": {
            "mandatory": "true"
        },
        "person.firstName": {
            "mandatory": "true"
        },
        "person.title": {
            "mandatory": "true",
            "values": [
                {
                    "value": "Dr",
                    "id": "cf14f246-385f-4fb3-b90f-a1254ef5520e",
                    "code": "TITLE0006"
                },
                {
                    "value": "Miss",
                    "id": "5175ae20-63d7-48e3-824d-72d35d1e960c",
                    "code": "TITLE0004"
                },
                {
                    "value": "Mr",
                    "id": "9e188b9d-737b-4c9d-92bb-199d8a6fba5c",
                    "code": "TITLE0001"
                },
                {
                    "value": "Mrs",
                    "id": "930fe73a-c2d5-446c-9819-75823d4fd7fe",
                    "code": "TITLE0005"
                },
                {
                    "value": "Ms",
                    "id": "77165d3e-aebb-41fd-891e-5fc7ce169ace",
                    "code": "TITLE0003"
                },
                {
                    "value": "Professor",
                    "id": "1a564fa5-4de3-4a6a-bca3-2869c20414eb",
                    "code": "TITLE0002"
                }
            ]
        },
        "person.socialSecurityNumbers": {
            "mandatory": "true",
            "links": {
                "template": {
                    "href": "/hrm/people/resourcetemplate?organisationId=00000000-0000-0000-0000-000000000000"
                }
            }
        },
            "person.socialSecurityNumbers.legislation": {
            "mandatory": "true",
            "values": [
                {
                    "value": "Isle of Man",
                    "id": "6895aa29-fe9d-4ee8-a349-91f19554677f",
                    "code": "IOM"
                },
                {
                    "value": "Malaysia",
                    "id": "96bf4445-11fe-4ab2-910e-a34101d3a00a",
                    "code": "MYS"
                },
                {
                    "value": "Other",
                    "id": "909e4ce5-9acd-44b7-9d21-a9f671f45f62",
                    "code": "OTHER"
                },
                {
                    "value": "Republic of Ireland",
                    "id": "9f1a256d-6a8e-4d59-b7a6-e7b4e9dae64e",
                    "code": "ROI"
                },
                {
                    "value": "Singapore",
                    "id": "630de51f-f7c8-407c-8439-201ee9bbb6ee",
                    "code": "SG"
                },
                {
                    "value": "States of Guernsey",
                    "id": "b98ef498-d2e7-4d50-871a-7313253ef007",
                    "code": "SOG"
                },
                {
                    "value": "States of Jersey",
                    "id": "c23f94c9-232e-4875-a9e4-84d9d4a1ab57",
                    "code": "SOJ"
                },
                {
                    "value": "United Kingdom",
                    "id": "47c23873-057b-4b22-b2d9-a1a31ccec3fb",
                    "code": "UK"
                },
                {
                    "value": "United States of America",
                    "id": "b1291fff-e37c-4323-a395-812cfd159395",
                    "code": "US"
                }
            ]
        },
        "person.socialSecurityNumbers.socialSecurityNumber": {
            "mandatory": "true"
        },
        "person.pronouns": {
            "values": [
                {
                    "value": "He/Him",
                    "id": "eefac58f-030e-419f-9bf0-4f454e79838d",
                    "code": "GP1"
                },
                {
                    "value": "She/Her",
                    "id": "6b43db2e-0de7-4238-870e-e0094afa1e49",
                    "code": "GP2"
                },
                {
                    "value": "They/Them",
                    "id": "cc0edab2-535c-4537-8fc9-68f95f077547",
                    "code": "GP3"
                }
            ]
        }
    }
}
              

This section of the API documentation relating to 'metadata' has provided a basic explanation as to what metadata is and how we can use it in relation to the People First APIs. Metadata can provide information about a particular data field, what info can be entered into that data field, and also if a data field is mandatory or not. The utility of metadata reaches even further than this section, and should be explored further in order to get the most from People First APIs.