---
title: "HR Integration"
url: "https://dev.4hse.com/guides/people-sync"
description: "How to integrate and synchronize HR data with 4HSE using APIs and sample scripts."
---

# HR Integration

4HSE provides tools for managing personnel records. However, it is increasingly common for structured companies to continue using their own HR software and integrate it with a safety management system like 4HSE. This need arises both during the project start-up phase and in subsequent daily operations, where it is essential to maintain solid alignment between the two systems.

We will see how to develop software that, by leveraging the APIs provided by 4HSE, allows any HR software to integrate with 4HSE, keeping personnel records always synchronized.

We will not focus on a specific HR software, but will take a generic approach to the problem, developing an integration script that can be easily adapted to various HR solutions on the market.

## Prerequisites

[Section titled “Prerequisites”](#prerequisites)

What do you need to develop an HR integration script?

-   Data source: we will use a simple CSV file as the data source, which we imagine is periodically exported from the HR software database to be aligned with 4HSE every 24 hours.
-   `project_id`: you must have an active project on 4HSE and know its ID. The ID is visible in the “info” section of the project window.
-   `access_token`: you need an active user with admin role for the specified project, as this user will perform the synchronization. To authenticate this user via API, you need their access token, provided by 4HSE upon client request.

## Data Source

[Section titled “Data Source”](#data-source)

To prepare the CSV file extracted from the data source, you must always follow these simple rules:

1.  The file follows a column structure predefined by 4HSE.
2.  The file always contains the entire, updated dataset of employees.
3.  The field values for each employee in the dataset overwrite the values of the same employee in 4HSE.
4.  Employees present in 4HSE but not in the source will be deactivated in 4HSE.
5.  Employees present in the source but not in 4HSE will be added to 4HSE.

The CSV file consists of the following columns:

field

required

code

no

first\_name

yes

last\_name

yes

street

no

locality

no

postal\_code

no

region

no

sex

no

country

no

birth\_date

no

birth\_place

no

tax\_code

no

note

no

is\_employee

yes

Among these fields, you need to identify a key field, i.e., a field whose value uniquely identifies an employee. It can be any of these fields, usually `code` or the tax code `tax_code`.

## 4HSE REST API

[Section titled “4HSE REST API”](#4hse-rest-api)

4HSE provides a standard OpenAPI REST API to perform and monitor synchronization. The `create` endpoint receives a request containing an input CSV and creates a task on the 4HSE processing servers. The service responds immediately if the creation is successful, providing the unique task identifier. The task will be processed as soon as possible. The `view` endpoint returns the processing status of the task identified by its ID.

The complete API documentation is available here: [PeopleSync API](/api/peoplesync).

## Example

[Section titled “Example”](#example)

In this example, we want to perform periodic synchronization between the data extracted from our HR system and the “La mia azienda” project on 4HSE. Suppose the CSV file `employees-1.csv` has been exported from our HR software and properly transformed to follow the column structure required by 4HSE. The file contains 7 employees.

code

first\_name

last\_name

street

locality

postal\_code

region

sex

country

birth\_date

birth\_place

tax\_code

note

is\_employee

1

John

Doe

123 Main St

Anytown

12345

CA

M

USA

1980-05-15

Anytown

ABC123456

Note for John Doe

1

2

Jane

Smith

456 Oak St

Someville

67890

NY

F

USA

1985-08-21

Someville

DEF789012

Note for Jane Smith

1

3

Robert

Johnson

789 Pine St

Another City

54321

TX

M

USA

1972-11-03

Another City

GHI345678

Note for Robert Johnson

1

4

Emily

Davis

101 Elm St

Yourtown

98765

FL

F

USA

1992-04-18

Yourtown

JKL901234

Note for Emily Davis

1

5

Michael

Miller

202 Cedar St

Cityville

13579

IL

M

USA

1988-07-27

Cityville

MNO567890

Note for Michael Miller

1

6

Sophia

Brown

303 Maple St

Smalltown

24680

GA

F

USA

1995-02-09

Smalltown

PQR123456

Note for Sophia Brown

1

7

Daniel

Wilson

404 Birch St

Newtown

86420

WA

M

USA

1983-09-12

Newtown

STU789012

Note for Daniel Wilson

1

Our project, identified by `project_id` `41ec7395-eeda-4c8e-bc73-5585d48e5161`, does not yet contain any people. It includes a project admin user `john@4hse.com` identified by the access token `test-key-1`. The first synchronization we perform is therefore a sort of “onboarding” or “zero point” on which all subsequent synchronizations will be based.

Now let’s write the import script in Java. We create a `Main` class that will be instantiated when the script starts. Let’s start by defining the constants within the class with the input parameters required for the request.

```
public class Main {    private final static String ACCESS_TOKEN = "test-key-1";    private final static String PROJECT_ID = "41ec7395-eeda-4c8e-bc73-5585d48e5161";    private final static String PRIMARY_KEY = "tax_code";    private final static int MAX_CHANGES = 150;    private final static boolean EMULATION = true;}
```

-   `ACCESS_TOKEN`: The token that identifies the 4HSE user who will perform the operation, a project admin.
-   `PROJECT_ID`: The ID of the project we just created.
-   `PRIMARY_KEY`: The column in the CSV that uniquely identifies an employee. We chose “tax\_code.”
-   `EMULATION`: Allows you to run the request in emulation mode. This means you can run the synchronization and get the results without actually making any changes. This allows you to test the outcome of the operation before applying it. Initially, we set this value to `TRUE`.
-   `MAX_CHANGES`: A safety parameter that controls the maximum number of changes that can be applied in the request. If the total number of changes exceeds this value, we will be notified and the request will be rejected. Initially, we set it to `150`.

Now let’s define the `createTask` method, which will be used to create the synchronization task at the `people-sync` service of 4HSE. Creating the task is a simple `HTTP POST` request to the server, which responds with a `task_id`. The task ID is an identifier for the task that allows us to query the server to know its progress status (queued, executed, etc.).

The method only requires the input of the file name to be synchronized and returns the ID of the created task.

```
private static String createTask(String filePath) throws IOException {    ...}
```

The first thing to do is to instantiate an HTTP client to send the request.

```
private static String createTask(String filePath) throws IOException {    HttpClient httpClient = HttpClients.createDefault();    HttpPost httpPost = new HttpPost("https://service.4hse.com/people-sync/create");}
```

Since you need to send a file, the data must be transmitted in multipart format. We populate the multipart request body by providing the file and the remaining input parameters:

```
private static String createTask(String filePath) throws IOException {    HttpClient httpClient = HttpClients.createDefault();    HttpPost httpPost = new HttpPost("https://service.4hse.com/people-sync/create");
    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    // Add the file parameter    builder.addBinaryBody("file", new File(filePath), ContentType.APPLICATION_OCTET_STREAM, "file.csv");
    // Add other parameters    builder.addTextBody("access-token", ACCESS_TOKEN);    builder.addTextBody("project_id", PROJECT_ID);    builder.addTextBody("pk", PRIMARY_KEY);    builder.addTextBody("emulation", String.valueOf(EMULATION));    builder.addTextBody("max_changes", String.valueOf(MAX_CHANGES));
    HttpEntity multipart = builder.build();    httpPost.setEntity(multipart);}
```

Finally, we execute the request. If successful, the server will respond with a JSON that must be parsed to obtain the `task_id`, which we will return as output. In case of error, we print the error to the screen.

```
private static String createTask(String filePath) throws IOException {    HttpClient httpClient = HttpClients.createDefault();    HttpPost httpPost = new HttpPost("https://service.4hse.com/people-sync/create");
    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
    // Add the file parameter    builder.addBinaryBody("file", new File(filePath), ContentType.APPLICATION_OCTET_STREAM, "file.csv");
    // Add other parameters    builder.addTextBody("access-token", ACCESS_TOKEN);    builder.addTextBody("project_id", PROJECT_ID);    builder.addTextBody("pk", PRIMARY_KEY);    builder.addTextBody("emulation", String.valueOf(EMULATION));    builder.addTextBody("max_changes", String.valueOf(MAX_CHANGES));
    HttpEntity multipart = builder.build();    httpPost.setEntity(multipart);
    // Execute the request    HttpResponse response = httpClient.execute(httpPost);
    // Parse the JSON response    JSONParser parser = new JSONParser();    try (InputStream content = response.getEntity().getContent()) {        JSONObject jsonResponse = (JSONObject) parser.parse(new InputStreamReader(content));        return (String) jsonResponse.get("task_id");    } catch (Exception e) {        e.printStackTrace();        return null;    }}
```

Now, let’s define a `readTask` method that, using the `task_id`, will get the current status of the task and print it to the screen.

```
private static void readTask(String taskId) throws IOException {    ...}
```

Similarly to the previous method, we instantiate an HTTP client that, this time, makes a request using the `GET` method, providing the `task_id` and the `access-token` for user identification.

```
private static void readTask(String taskId) throws IOException {    HttpClient httpClient = HttpClients.createDefault();    HttpGet httpGet = new HttpGet("https://service.4hse.com/people-sync/view?id=" + taskId + "&access-token=" + ACCESS_TOKEN);}
```

We execute the request, parse the JSON response, and print the `task_id` to the screen.

```
private static void readTask(String taskId) throws IOException {    HttpClient httpClient = HttpClients.createDefault();    HttpGet httpGet = new HttpGet("https://service.4hse.com/people-sync/view?id=" + taskId + "&access-token=" + ACCESS_TOKEN);
    // Execute the request    HttpResponse response = httpClient.execute(httpGet);
    // Parse and print the JSON response    JSONParser parser = new JSONParser();    try (InputStream content = response.getEntity().getContent()) {        JSONObject jsonResponse = (JSONObject) parser.parse(new InputStreamReader(content));        System.out.println("Task: " + jsonResponse.toJSONString());    } catch (Exception e) {        e.printStackTrace();    }}
```

Finally, let’s define the main method, which will be the entry point of our script. The available commands for the script will be:

-   `create` to create the task
    
    java -jar your-jar-file.jar create filename.csv
    
-   `read` to read the task
    
    java -jar your-jar-file.jar read task-id
    

We then check that the input parameters are provided; otherwise, we display a message on the screen.

```
public static void main(String[] args) {    String command = args[0]; //create or read
    if (args.length < 2) {        System.out.println("Usage:");        System.out.println("java -jar your-jar-file.jar create file.csv");        System.out.println("java -jar your-jar-file.jar read task-id");        return;    }}
```

We then check if the first parameter passed to the command is `create` or `read` and call the respective methods, passing the CSV file name as an argument for creation or the task ID for reading.

```
public static void main(String[] args) {    String command = args[0]; //create or read
    if (args.length < 2) {        System.out.println("Usage:");        System.out.println("java -jar your-jar-file.jar create file.csv");        System.out.println("java -jar your-jar-file.jar read task-id");        return;    }
    try {        if ("create".equals(args[0])) {            String taskId = createTask(args[1]);            System.out.println("Created Task ID: " + taskId);        }        else if ("read".equals(args[0])) {            readTask(args[1]);        }        else {            System.out.println("Action must be 'create' or 'read'");        }    } catch (IOException e) {        e.printStackTrace();    }}
```

Now let’s run the script by starting the task creation with the command:

Terminal window

```
    java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar create employees2-1.csv
```

In response, we get the created task ID:

Created Task ID: be9eec92-621c-31b1-a525-7df391f599bf

After waiting a few seconds, we run the command to read the status of the created task:

Terminal window

```
    java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar read be9eec92-621c-31b1-a525-7df391f599bf
```

In response, we get the complete report of the server actions:

-   `task_id`: Task ID
    
-   `entity_id`: Project ID
    
-   `user_id`: the user who created the task
    
-   `status`: indicates whether the task was successfully executed or not
    
-   `created_at`, `updated_at`: provide timestamps for task creation and update
    
-   `response.result`: is a history of the operations performed for each record in our CSV file. In this case, since they were new employees, each employee, identified by their `tax_code`, was added to the project.
    
-   `response.changes`: is a summary of all operations performed. In this case, the task resulted in a total of 7 changes, all “add”.
    
-   `log`: the server log recorded during the execution of the task. It is useful for identifying problems in case of errors.
    
    ```
    {    "task_id": "a386a5bf-5bc4-3be7-9392-c81c69918bfc",    "entity_id": "41ec7395-eeda-4c8e-bc73-5585d48e5161",    "user_id": "john@4hse.com",    "status": "DONE",    "created_at": "2024-01-11 08:14:53",    "updated_at": "2024-01-11 08:15:07",    "response": {        "result": {            "ABC123456": [                {                    "action": "add",                    "id": "ABC123456"                }            ],            "PQR123456": [                {                    "action": "add",                    "id": "PQR123456"                }            ],            "DEF789012": [                {                    "action": "add",                    "id": "DEF789012"                }            ],            "STU789012": [                {                    "action": "add",                    "id": "STU789012"                }            ],            "MNO567890": [                {                    "action": "add",                    "id": "MNO567890"                }            ],            "GHI345678": [                {                    "action": "add",                    "id": "GHI345678"                }            ],            "JKL901234": [                {                    "action": "add",                    "id": "JKL901234"                }            ]        },        "log": [            {                "level": "INFO",                "text": "Task a386a5bf-5bc4-3be7-9392-c81c69918bfc executed",                "timestamp": 1704960907            },            {                "level": "INFO",                "text": "Executing task a386a5bf-5bc4-3be7-9392-c81c69918bfc in emulation mode",                "timestamp": 1704960906            }        ],        "changes": {            "add": 7,            "total": 7,            "activate": 0,            "update": 0,            "deactivate": 0        }    }}
    ```
    

At this point, change the emulation parameter to `false` and rerun the creation command.

private final static boolean EMULATION = false;

You will receive the same response as before, but in this case, the changes have actually been made and you will have 7 employees in the “La mia azienda” project.

Now, suppose that in the company, 2 people have retired. As a result, our HR has created a new file `employees2-2.csv` containing only the 5 remaining individuals. Also, for “John Doe”, the `NOTE` field has been updated. In total, there are 3 differences compared to the `employees2-1.csv` file.

code

first\_name

last\_name

street

locality

postal\_code

region

sex

country

birth\_date

birth\_place

tax\_code

note

is\_employee

1

John

Doe

123 Main St

Anytown

12345

CA

M

USA

1980-05-15

Anytown

ABC123456

Updated note for John Doe

1

2

Jane

Smith

456 Oak St

Someville

67890

NY

F

USA

1985-08-21

Someville

DEF789012

Note for Jane Smith

1

3

Robert

Johnson

789 Pine St

Another City

54321

TX

M

USA

1972-11-03

Another City

GHI345678

Note for Robert Johnson

1

4

Emily

Davis

101 Elm St

Yourtown

98765

FL

F

USA

1992-04-18

Yourtown

JKL901234

Note for Emily Davis

1

5

Michael

Miller

202 Cedar St

Cityville

13579

IL

M

USA

1988-07-27

Cityville

MNO567890

Note for Michael Miller

1

The file now contains only active employees, and for some of them, the `NOTE` field has been updated. There have been no changes to the tax codes.

Now proceed with the synchronization by uploading the new file. Since the number of changes exceeds the `MAX_CHANGES` value set to 150, the request will be rejected and you will receive an error message.

Therefore, reduce the number of changes to 50 to allow the request to be applied.

private final static int MAX\_CHANGES = 50;

Repeat the task creation command.

java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar create employees2-2.csv

After obtaining the task ID, read its status.

java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar read task-id

The result will show that 2 employees have been removed (deactivated) and 3 have been updated.

Now, prepare a new file `employees2-3.csv` in which all employees have been deactivated. The file will therefore contain only the `TAX_CODE` column with the respective tax codes.

tax\_code

ABC123456

DEF789012

GHI345678

JKL901234

MNO567890

PQR123456

STU789012

Set the `MAX_CHANGES` parameter to 150 and upload the new file.

```
    private final static int MAX_CHANGES = 150;
```

Run the creation command and then read the task status.

Terminal window

```
    java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar create employees2-3.csv
    java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar read task-id
```

The result will show that 7 employees have been deactivated.

Finally, prepare a last file `employees2-4.csv` in which all employees have been reactivated. The file will therefore contain only the `TAX_CODE` column with the respective tax codes.

tax\_code

ABC123456

DEF789012

GHI345678

JKL901234

MNO567890

PQR123456

STU789012

Set the `MAX_CHANGES` parameter to 150 and upload the new file.

```
    private final static int MAX_CHANGES = 150;
```

Run the creation command and then read the task status.

Terminal window

```
    java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar create employees2-4.csv
    java -jar target/my-java-project-1.0-SNAPSHOT-jar-with-dependencies.jar read task-id
```

The result will show that 7 employees have been reactivated.

## Conclusions

[Section titled “Conclusions”](#conclusions)

We have seen how to develop an integration script between a generic HR software and 4HSE. Using the standard 4HSE APIs, we created a Java software capable of uploading a CSV file with employee data and synchronizing it with the 4HSE safety management system.

The script was designed to be easily adaptable to different HR software solutions by simply modifying the format of the input CSV file.

In this example, we used a CSV file as the data source. However, it is possible to adapt the script to extract data directly from a database or other data sources, depending on specific integration needs.

In addition, we implemented an emulation mode that allows you to test the outcome of the synchronization without making actual changes to the data. This is especially useful during the development and testing phase of the integration script.

Finally, it is important to emphasize that, although the script provides an automated solution for synchronizing HR data with 4HSE, it is always advisable to perform periodic manual checks and verifications to ensure the accuracy and consistency of data between the two systems.

This site is protected by reCAPTCHA and the Google [Privacy Policy](https://policies.google.com/privacy) and [Terms of Service](https://policies.google.com/terms) apply.