NAV
cURL

Introduction

Welcome to the Engage API documentation.

API Sandbox

Honeydew can provide a sandbox account and authentication credentials for testing. Email support@honeydew-health.com to request a sandbox account.

Rate Limiting

Coming soon

To ensure our platform remains stable and available to everyone, the Engage API will be soon be rate-limited. We ask developers to use industry standard techniques for limiting calls, caching results and re- trying requests responsibly. Calls to our API will be managed by request-based limits, allowing up to 40 requests per minute using a leaky bucket algorithm. You’ll be able to see the current rate using the rate limit header, for example:

X-Engage-Api-Rate-Limit: 32/40

In this example 32 is the current request count and 40 is the bucket size. The request count decreases according to the leak rate (in this case 1.5 requests per second), so after waiting 10 seconds the response would be down to 17/40, and 0/40 after 48 seconds.

When a request goes over the rate limit, a "HTTP 429 - Too Many Requests" response is given from the API, this will be provided with a Retry-After header with a value in seconds.

X-Engage-Api-Rate-Limit: 40/40

X-Engage-Api-Retry-After: 1.5

Authentication

OAuth 2.0 Token Bearer

Engage supports bearer token authentication with a client credentials grant type. Within Engage you can generate API Client Applications, each of which has a unique id and secret. Your app should authenticate with this id and secret to obtain a JWT token. That token can be passed as a bearer token to authorise subsequent API requests. The token has a lifetime of up to 2 hours, once expired you will need to authenticate again to obtain a new token.

Authorising your requests

After authenticating and obtaining a token provide this token with all requests as the following header parameter.

curl /example -H "Authorization: Bearer abc"

Header Parameters

Parameter Type Description
Authorization string required Bearer abc

Obtain a token

HTTP Request

POST /auth/token

Authenticate with the API and obtain a JWT token

curl -X POST /auth/token -H "Content-Type: application/json" -d '{
    "grant_type": "client_credentials", 
    "client_id": "123", 
    "client_secret": "abc"
  }' 

Example JSON Response:

{
     "access_token": "abc",
     "token_type": "Bearer",
     "expires_in": 7200,
     "created_at": 1691747914
}

JSON Parameters

Parameter Type Description
grant_type string required The type of grant requested
client_id integer required Your API Client Application ID
client_secret integer required Your API Client Application Secret

Verify a token

HTTP Request

POST /auth/token/info

curl -X POST /auth/token/info -H "Authorization: abc"

Example JSON Response:

{
    "resource_owner_id": null,
    "scope": [],
    "expires_in": 6540,
    "application": {
        "uid": “abc”
    },
    "created_at": 1653667738
}

Header Parameters

Name Type Description
Authorization string required Your authorization token

Revoke a token

HTTP Request

POST /auth/revoke

curl -X POST /auth/revoke -H "Content-Type: application/json" -H "Authorization: Basic abc" -d '{
  "token": "abc"
  }'

Header Parameters

Name Type Description
Authorization string required Basic HTTP Authentication with your Base64 encoded "client_id:client_secret"

JSON Parameters

Name Type Description
token string required Your token

API Keys

Deprecated

This is a simple API Key that should be provided along with any requests to the API via QueryString in the format /?api_key={your api key}.

As all API requests are made over SSL this value is always encrypted. You can view or generate a new API Key on your Engage account under Administration > Preferences > Data API.

IP Whitelisting

Deprecated

All requests will be validated against a defined list of IP addresses, you can configure these addresses on your Engage account under Administration > Preferences > Data API.

Employees

Get an employee

GET /api/v1/employees/{employee_id}

curl -X GET /api/v1/employees/123 \
  -H "Authorization: Bearer abc"

Example JSON Response:

{
  "employee_id": "123",
  "first_name": "Joe",
  "surname": "Bloggs",
  "date_of_birth": "1980-04-23",
  "email": "joe.bloggs@example.com",
  "job_title": "Administrative Assistant",
  "department_name": "HR",
  "start_date": "2023-08-01",
  "end_date": "2023-08-30",
  "first_manager_id": "456",
  "employment_terms": "Standard",
  "calendar_name": "Mon-Fri 8hr",
  "custom_fields": {
    "pay_group": "PG001",
    "sick_pay_policy": "SSP",
    "country": "Scotland",
    "secondary_employee_id": "38475"
  }
}

Check if an employee exists

GET /api/v1/employees/exists?employee_id={employee_id}

curl -X GET /api/v1/employees/exists?employee_id={employee_id} \
  -H "Authorization: Bearer abc"

Example JSON Response:

{
  exists: true
}

Will return a value of true + HTTP 200 if employee exists, false + HTTP 404 if employee doesn't exist.

Create an employee

POST /api/v1/employees

curl -X POST /api/v1/employees \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
     "employee_id": "123",
     "first_name": "Joe",
     "surname": "Bloggs",
     "date_of_birth": "1980-04-23",
     "email": "joe.bloggs@example.com",
     "job_title": "Administrative Assistant",
     "department_name": "HR",
     "start_date": "2023-08-01",
     "first_manager_id": "456",
     "employment_terms": "Standard",
     "calendar_name": "Mon-Fri 8hr",
     "needs_engage_login": "true",
     "send_account_activation_email": "false",
     "custom_fields": {
        "pay_group": "PG001",
        "sick_pay_policy": "SSP",
        "country": "Scotland",
        "secondary_employee_id": "38475"
     }
  }' 

 JSON Parameters

Name Type Description
employee_id string required Your unique identifier for the employee.
first_name string required
middle_name string
surname string required
date_of_birth date Must be YYYY-MM-DD format.
email string required* Required if needs_engage_login true.
mobile string
job_title string Must match an existing job title. Reporting by job title must be enabled in company settings if provided. Invalid values ignored.
department_name string required* Must match an existing department name. Must provide one of department_id, department_name or department_external_id.
department_id integer required* Must match an existing department id. Must provide one of department_id, department_name or department_external_id.
department_external_id string required* Must match an existing department external_id. Must provide one of department_id, department_name or department_external_id.
start_date date required Must be YYYY-MM-DD format. Employees start date in current role (not overall employment).
end_date date Must be YYYY-MM-DD format. Employees end date in current role.
first_manager_id integer Must match an existing employee id.
second_manager_id integer Must match an existing employee id.
employment_terms string Must match existing employment terms.
fte decimal Full-Time Equivalent for the employee's current role. Valid range: 0.01 to 2.00.
hourly_rate decimal Must be greater than 0. Up to 2 decimal places, e.g. 19.75
calendar_name string Must match existing calendar name.
needs_engage_login boolean Creates an Engage login for the employee, email required.
send_account_activation_email boolean required* Required if needs_engage_login provided.
custom_fields string Key/value pairs of existing custom fields. Key must be snake case.

Update an employee

PUT /api/v1/employees

curl -X PUT /api/v1/employees \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
     "employee_id": "123",
     "first_name": "Joe",
     "surname": "Bloggs",
     "date_of_birth": "1980-04-23",
     "email": "joe.bloggs@example.com",
     "job_title": "Administrative Assistant",
     "department_name": "HR",
     "start_date": "2023-08-01",
     "end_date": "2023-08-30",
     "first_manager_id": "456",
     "employment_terms": "Standard",
     "calendar_name": "Mon-Fri 8hr",
     "custom_fields": {
        "pay_group": "PG001",
        "sick_pay_policy": "SSP",
        "country": "Scotland",
        "secondary_employee_id": "38475"
     }
  }' 

 JSON Parameters

Name Type Description
employee_id string required Your unique identifier for the employee.
first_name string required
middle_name string
surname string required
date_of_birth date Must be YYYY-MM-DD format.
email string required* Required if needs_engage_login true.
mobile string
job_title string Must match an existing job title. Reporting by job title must be enabled in company settings if provided. Invalid values ignored.
department_name string required* Must match an existing department name. Must provide one of department_id, department_name or department_external_id.
department_id integer required* Must match an existing department id. Must provide one of department_id, department_name or department_external_id.
department_external_id string required* Must match an existing department external_id. Must provide one of department_id, department_name or department_external_id.
start_date date required* Must be YYYY-MM-DD format. Employees start date in current role (not overall employment). Required unless end_date is provided and updating employee to leaver.
end_date date Must be YYYY-MM-DD format. Employees end date in current role. Ignored if new start date is provided.
first_manager_id integer Must match an existing employee id.
second_manager_id integer Must match an existing employee id.
employment_terms string Must match existing employment terms.
fte decimal Full-Time Equivalent for the employee's current role. Valid range: 0.01 to 2.00.
hourly_rate decimal Must be greater than 0. Up to 2 decimal places, e.g. 19.75
calendar_name string Must match existing calendar name.
needs_engage_login boolean Creates an Engage login for the employee, email required.
send_account_activation_email boolean required* Required if needs_engage_login provided.
custom_fields string Key/value pairs of existing custom fields. Key must be snake case.

Employee logins

If you need for the employee to login to Engage, you can provide the needs_engage_login and send_account_activation_email fields to provision their user account. By default the employee will not be able to login.

send_account_activation_email

For companies with SSO enabled, this will send a welcome email to the employee advising them that they can now login with their company email.

For companies without SSO enabled, this will send a welcome email to the employee with a secure link from which they can set their password and login.

New starters

New managers

Leavers

Transfers

Job Titles

Create a job title

POST /api/v1/job_titles

curl -X POST /api/v1/job_titles \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
     "title": "abc"
  }' 

 JSON Parameters

Name Type Description
title string required Unique job title.

Departments

List departments

GET /api/v1/departments

curl -X GET /api/v1/departments \
  -H "Authorization: Bearer abc"

Example JSON Response:

[
  {
    "id": "1",
    "external_id": "abc1",
    "name": "abc",
    "parent_department_id": null,
    "parent_department_external_id": null,
    "parent_department": null,
    "created_at": "2022-09-01T14:15:59.000+01:00",
    "updated_at": "2022-09-01T14:15:59.000+01:00",
    "retired_at": null
  },
  {
    "id": "2",
    "external_id": "xyz2",
    "name": "xyz",
    "parent_department_id": "1",
    "parent_department_external_id": "abc1",
    “parent_department": "abc",
    "created_at": "2022-09-01T14:15:59.000+01:00",
    "updated_at": "2022-09-01T14:15:59.000+01:00",
    "retired_at": null
  }
]

Query Parameters

Name Type Description
parent_department_id integer Id of parent department to filter by.
parent_department_external_id string Your id of parent department to filter by.

Get a department

GET /api/v1/departments/{id}

GET /api/v1/departments/external_id/{id}

Using our Id:

curl -X GET /api/v1/departments/123 \
  -H "Authorization: Bearer abc"

Using your Id:

curl -X GET /api/v1/departments/external_id/abc123 \
  -H "Authorization: Bearer abc"

Example JSON Response:

{
  "id": "123",
  "external_id": "abc123",
  "name": "abc",
  "parent_department_id": null,
  "parent_department_external_id": null,
  "parent_department": null,
  "created_at": "2022-09-01T14:15:59.000+01:00",
  "updated_at": "2023-08-01T14:15:59.000+01:00",
  "retired_at": null
}

Check if a department exists

GET /api/v1/departments/exists

curl -X GET /api/v1/departments/exists?name={name} \
  -H "Authorization: Bearer abc"

Example JSON Response:

{
  exists: true
}

Query Parameters

Name Type Description
external_id string Your id of the department.
name string The name of the department.

Will return a value of true + HTTP 200 if department exists, false + HTTP 404 if department doesn't exist.

Create a department

POST /api/v1/departments/

curl -X POST /api/v1/departments \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
      "name": "abc",
      "external_id": "abc123",
      "parent_department_id": null,
      "parent_department_external_id": null,
      "parent_department": null,
     }'

JSON Parameters

Name Type Description
name string required Unique department name.
external_id string Your unique identifier for the department, 25 character max.
parent_department string Name of the existing parent department.
parent_department_id string Id of the existing parent department.
parent_department_external_id string Your unique identifier for the existing parent department.

Update a department

PUT /api/v1/departments/{id}

PUT /api/v1/departments/external_id/{external_id}

Using our id:

curl -X PUT /api/v1/departments/123 \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
     "name": "abc"
  }' 

Using your id:

curl -X PUT /api/v1/departments/external_id/abc123 \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
     "name": "abc"
  }' 

Example JSON Response:

{
  "id": "123",
  "external_id": "abc123",
  "name": "abc",
  "parent_department_id": null,
  "parent_department_external_id": null,
  "parent_department": null,
  "created_at": "2022-09-01T14:15:59.000+01:00",
  "updated_at": "2023-08-01T14:15:59.000+01:00",
  "retired_at": null
}

JSON Parameters

Name Type Description
name string required Unique department name.
external_id string Your unique identifier for the department, 25 character max.
parent_department string Name of the existing parent department.
parent_department_id string Id of the existing parent department.
parent_department_external_id string Your unique identifier for the existing parent department.

Retire a department

PUT /api/v1/departments/{id}/retire

PUT /api/v1/departments/external_id/{external_id}/retire

Using our id:

curl -X PUT /api/v1/departments/123/retire \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
     "retire_at": "2023-08-01",
     "employee_action": "retire"
  }' 

Using your id:

curl -X PUT /api/v1/departments/external_id/abc123/retire \
  -H "Authorization: Bearer abc" \
  -H "Content-Type: application/json" \
  -d '{
     "retire_at": "2023-08-01",
     "employee_action": "transfer",
     "transfer_department_external_id": "abc123"
  }' 

Example JSON Response:

{
  "id": "123",
  "external_id": "abc123",
  "name": "abc",
  "parent_department_id": null,
  "parent_department_external_id": null,
  "parent_department": null,
  "created_at": "2022-09-01T14:15:59.000+01:00",
  "updated_at": "2023-08-01T14:15:59.000+01:00",
  "retired_at": "2023-08-01T00:00:00.000+01:00"
}

JSON Parameters

Name Type Description
retire_at date required Must be YYYY-MM-DD format.
employee_action string retire or transfer. Defaults to retire.
sub_department_action string retire or promote. Defaults to retire.
transfer_department_id integer required* Required if employee action = transfer and transfer_department_external_id not provided.
transfer_department_external_id string required* Required if employee action = transfer and transfer_department_id not provided.

Employee Action

Name Description
retire Employees current role will be ended.
transfer Employees current role will be ended and a new role started at transfer department.

Sub Department Action

Name Description
retire Sub departments will be retired.
promote Sub departments will be promoted to level of retiring department.

Absences

List absences

GET /api/v1/absences

curl -X GET /api/v1/absences?start_date=2019-04-18 \
  -H "Authorization: Bearer abc"

Example JSON Response:

[
  {
    "absence_id": "372846",
    "employee_id": "11223344",
    "start_date": "2019-04-18",
    "confirmed_return_date": null,
    "end_date": null,
    "created_at": "2019-04-18 18:01:57", 
    "updated_at": "2019-04-18 18:01:57", 
    "absence_cause": "Flu",
    "absence_cause_category": "Infections",
    "absence_cause_periods": [
      {
        "absence_cause_period_id": 77,
        "absence_cause": "Flu",
        "absence_cause_category": "Infections",
        "start_date": "2019-04-18",
        "end_date": "2019-05-01",
        "half_day_start": 0,
        "half_day_end": 0,
      },
      {
        "absence_cause_period_id": 76,
        "absence_cause": "Morning sickness",
        "absence_cause_category": "Pregnancy related",
        "start_date": "2019-05-02",
        "end_date": null,
        "half_day_start": 0,
        "half_day_end": 1,
      },
    ],
    "half_day_start": 0,
    "half_day_end": 1,
  },
  {
    "absence_id": "372847",
    "employee_id": "11223344",
    "start_date": "2020-04-18",
    "confirmed_return_date": "2020-04-19",
    "end_date": "2020-04-18",
    "created_at": "2020-04-18 18:01:57",
    "updated_at": "2020-04-19 18:01:57",
    "absence_cause": "Flu",
    "absence_cause_category": "Infections",
    "absence_cause_periods": [
      {
        "absence_cause_period_id": 76,
        "absence_cause": "Flu",
        "absence_cause_category": "Infections,
        "start_date": "2020-04-18",
        "end_date": "2020-04-19"
      },
    "half_day_start": "0",
    "half_day_end": "0",
  }
]

Query Parameters

Name Type Description
start_date date Must be YYYY-MM-DD format. Matches equal to or greater than.
confirmed_return_date date Must be YYYY-MM-DD format. Matches equal to or greater than.
created_at date Must be YYYY-MM-DD format. Matches equal to or greater than.
updated_at date Must be YYYY-MM-DD format. Matches equal to or greater than.

Errors

The Engage API will return a HTTP 200 response code for all successful requests. You should check the response code to determine the success of each request.

Some common error responses that you may encounter include:

Code Name Description
400 Bad Request Your request is invalid, most commonly a validation issue
401 Unauthorized Your authentication is invalid
403 Forbidden You cannot perform that action
404 Not Found The resource that you requested could not be found
406 Not Acceptable Your requested format isn't supported
429 Too Many Requests You have made too many requests in a short period of time See Rate Limiting
500 Internal Server Error We're having a problem processing your request. Please try again later or contact support if persistent.
503 Service Unavailable We're temporarily offline. Please try again later

Most errors will include a JSON body with further details.

Webhooks

Allow your system to receive live events from Engage.

Absence events can occur at any time of the day or night and on any day of the week. Knowing when to fetch a new data set is not always easy and fetching large sets of data means more data processing at the receiving end. Using webhooks, Engage can tell you when something happens, optimising the data flows.

The specific actions your webhook endpoint may take differs based upon the event. Some examples include:

Configuring Webhooks

You can manage webhooks on your Engage account under Administration > Data API > Webhooks.

New webhook endpoints will be authorised through our Firewall by support.

If using bearer token authentication please provide the following to support for configuration:

Webhook Authentication

HMAC Signature

Webhooks carry a HMAC signature which can be used to verify the authenticity of the request. This will be provided as the HTTP header:

X-Engage-API-Signature: t=epoch,v1=hmac

To verify the signature, you should split this value to obtain the epoch timestamp and the hmac separately. In future, there may be multiple versions of the HMAC so be sure to programatically select the correct HMAC version.

The v1 HMAC uses the SHA256 hash algorithm with your api key as the secret. The content is comprised of the epoch timestamp and the HTTP body payload separated by a period.

Here’s an example of creating the hash in Ruby:

OpenSSL::HMAC.hexdigest('SHA256', api_key, "#{timestamp}.#{body}")

If you construct your own hash with the same timestamp and the received body this should match the received signature, allowing you to verify the integrity of the HTTP body and the time validity of the request. If either the header epoch value doesn’t match the hash epoch value, or the body doesn’t match the hash body then the signatures will not match.

OAuth 2.0 Bearer Token

Webhooks can be configured to authenticate against OAuth 2.0 bearer token authentication to retrieve a token from your servers, which will then be included in webhook request headers.

Please provide support with your Authentication API documentation and test/production credentials for verification of compatibility and setup.

New Absence

HTTP POST {your_url}

Headers

Name Type Description
Content-Type string application/json
X-Engage-API-Version string v1
X-Engage-API-Webhook-Event string Absence.Created
X-Engage-API-Signature string epoch=x,v1=y See HMAC Signature
Authorization string Bearer abc (if applicable, See OAuth 2.0 Bearer Token)

Example JSON body:

  "event": {
    "name": "Absence.Created",
    "object_type": "Absence",
    "object_id": 123,
    "object": {
      "absence_id": 123,
      "employee_id": "456",
      "start_date": "2022-06-15",
      "end_date": null,
      "expected_end_date": "2022-06-17",
      "confirmed_return_date": null,
      "half_day_start": 1,
      "half_day_end": 0,
      "absence_cause": null,
      "absence_cause_category": null,
      "medical_appointment_arranged": null,
      "medical_appointment_date": null,
      "created_at": "2022-06-15T08:21:56.000+00:00",
      "updated_at": "2022-06-15T09:35:25.000+00:00"
    } 
  }

Updated Absence

HTTP POST {your_url}

Headers

Name Type Description
Content-Type string application/json
X-Engage-API-Version string v1
X-Engage-API-Webhook-Event string Absence.Updated
X-Engage-API-Signature string epoch=x,v1=y See HMAC Signature
Authorization string Bearer abc (if applicable, See OAuth 2.0 Bearer Token)

Example JSON body:

  "event": {
    "name": "Absence.Updated",
    "object_type": "Absence",
    "object_id": 123,
    "object": {
      "absence_id": 123,
      "employee_id": "456",
      "start_date": "2022-06-15",
      "end_date": null,
      "expected_end_date": "2022-06-17",
      "confirmed_return_date": null,
      "half_day_start": 1,
      "half_day_end": 0,
      "absence_cause": null,
      "absence_cause_category": null,
      "medical_appointment_arranged": null,
      "medical_appointment_date": null,
      "created_at": "2022-06-15T08:21:56.000+00:00",
      "updated_at": "2022-06-15T09:35:25.000+00:00"
    } 
  }

Deleted Absence

HTTP POST {your_url}

Headers

Name Type Description
Content-Type string application/json
X-Engage-API-Version string v1
X-Engage-API-Webhook-Event string Absence.Deleted
X-Engage-API-Signature string epoch=x,v1=y See HMAC Signature
Authorization string Bearer abc (if applicable, See OAuth 2.0 Bearer Token)

Example JSON body:

  "event": {
    "name": "Absence.Deleted",
    "object_type": "Absence",
    "object_id": 123,
    "object": {
      "id": 123
    } 
  }

Best Practices

Receiving webhooks

After your webhook endpoint is created, Engage sends a HTTP POST to your endpoint url every time the subscribed events occur. The request body will be a JSON Event object detailing the event and the associated object.

Your service should ingest the webhook and return a HTTP 200 response as fast as possible. Any non HTTP 200 response (including redirects) will be treated as an error response by Engage. A common pattern is to store the payload in a message queue for later processing by a background worker. This reduces the chance of the request timing out, and the webhook delivery counting as a failure.

Implementing reconciliation jobs

Webhook delivery cannot be guaranteed, therefore we recommend that you implement regular reconciliation tasks to fetch and update data from the Engage API.

Frequency of retries

Webhooks have a timeout on HTTP response of 5 seconds - if your service doesn’t return a response within that time, or returns a non HTTP 200 response Engage will retry the webhook up to 5 times.

The first retry will run 30 seconds later, then 1m 45s, 4m 30s and finally 10 minutes after the initial request at which point it will fail permanently.

Managing webhook API versions

Add logic to your code so that it handles webhooks differently depending on their API version. To check the API version, your app can use the X-Engage-API-Version request header in every webhook POST request.

You should select the API version you are using in the Company Preferences DATA API section in Engage.