> For the complete documentation index, see [llms.txt](https://api-docs.myhappyforce.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://api-docs.myhappyforce.com/docs/employee-sync-via-aws-s3.md).

# Employee sync via AWS S3

Learn how to sync your users with Happyforce via CSV files

**How it works:** you upload a CSV file to an Amazon S3 folder provided by Happyforce. Each upload automatically triggers the sync: the employees in the file are created, updated or deleted in Happyforce until the state in Happyforce exactly matches the CSV.

Happyforce provides, for each company: the upload bucket and folder (`employee_bulk/<companyId>/`), the access credentials, the encryption key, and the IDs and types of its profile segments.

***

### 1. CSV file format

* **Encoding:** UTF-8.
* **Delimiter:** comma (`,`). Values containing commas must be wrapped in double quotes (`"…"`).
* **First line:** header (required).
* **Remaining lines:** one employee per row. Blank lines are ignored.

#### 1.1 Fixed columns (positions 1 to 7, in this order)

| # | Field                  | Required | Format                                                                                                                                                       |
| - | ---------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1 | **externalId**         | **Yes**  | Unique, stable identifier of the employee in the source system. It is the **key** Happyforce uses to recognize each person across loads. It may be an email. |
| 2 | **email**              | **Yes**  | Valid employee email.                                                                                                                                        |
| 3 | **name**               | No       | Full name.                                                                                                                                                   |
| 4 | **area\_id**           | No       | Area/department ID in Happyforce (24-character hexadecimal identifier). Empty to leave unassigned, or `x` to remove the assignment.                          |
| 5 | **groups**             | No       | One or more Happyforce group IDs separated by a hyphen `-` (e.g. `id1-id2`). Each ID is 24 hex.                                                              |
| 6 | **role**               | No       | One of: `ADMIN`, `MANAGER`, `EMPLOYEE`. If left empty, `EMPLOYEE` is assigned.                                                                               |
| 7 | **managed\_area\_ids** | No       | Areas managed (for managers), IDs separated by `-`. Each ID is 24 hex.                                                                                       |

Area and group IDs (24 hex) are provided by Happyforce.

#### 1.2 Profile columns (position 8 onward) — optional

Used to populate demographic segments (e.g. seniority, a Yes/No, a category).

* In the **header**, each column 8+ contains the **segment ID** (see §3). They must be **exactly** the segments configured for the company — no more, no fewer.
* In each **employee row**, the same column contains the **value** of that segment for that person.
* The valid value depends on the segment **type** (§3):
  * **Yes/No** type: `Y` or `N`.
  * **Date** type: `dd/mm/yyyy` (e.g. `15/09/2019`).
  * **Identifier** (list) type: a 24-hex ID (the option's `valueId`).
  * **Tree** (hierarchical) type: the 24-hex ID of the corresponding **leaf** node.
* For any segment: `x` **removes** that person's value; **empty** leaves it unchanged.

#### 1.3 Example

```
externalId,email,name,area_id,groups,role,managed_area_ids,<yesNoSegment>,<dateSegment>
E001,ana@company.com,Ana García,5e30151c9e0d85dc30cfe8dc,,EMPLOYEE,,N,01/03/2021
E002,luis@company.com,Luis Pérez,5e30151c9e0d85dc30cfe8dc,663a...-664b...,MANAGER,5e30...,Y,15/09/2019
```

***

### 2. Validation rules

Validation is **all-or-nothing**: if **any** row breaks a rule, the **entire file** is rejected (no row is processed) and the list of errors is returned (§5). The file is rejected if:

* It is empty.
* Rows have a different number of profile columns.
* There is a **duplicate externalId** or a **duplicate email** within the file.
* The **externalId** is missing or the **email** is not a valid format.
* `area_id`, any `group`, or any `managed_area` is not a 24-hex ID (and is neither empty nor `x`).
* `role` is not `ADMIN`, `MANAGER` or `EMPLOYEE`.
* A **segment ID** in the header does not match those configured for the company.
* A **profile value** is not valid for its segment type (Yes/No, date or ID).

***

### 3. Profile segments

Each company receives from Happyforce the list of its segments: for each one, its **ID** (24 hex), its **type** and the accepted values. If the company does not use profile segments, the CSV must not include columns from position 8 onward.

| Type                | Value in the CSV cell              |
| ------------------- | ---------------------------------- |
| Yes/No              | `Y` or `N`                         |
| Date                | `dd/mm/yyyy`                       |
| Identifier (list)   | 24-hex ID (the option's `valueId`) |
| Tree (hierarchical) | 24-hex ID of a **leaf** node       |

The **order** of the profile columns in the CSV must match the order of the segment IDs in the header. Any segment ID not configured for the company causes the file to be rejected.

***

### 4. How to upload the file

* **Location:** upload the file to `employee_bulk/<companyId>/` in the bucket provided by Happyforce.
* **Name:** any name ending in `.csv`.
* **Do not** upload to the `/errors/` or `/processed/` subfolders: they are internal and are ignored.
* **Frequency:** each upload triggers a full sync. Upload the file whenever roster changes need to be reflected.

#### 4.1 Encryption (required)

The file **must be uploaded encrypted**. It uses **client-side encryption with AWS KMS** (AWS Encryption SDK / CSE-KMS), using the key Happyforce authorizes for the company's IAM user. An unencrypted file must not be uploaded.

***

### 5. Result of each load

* **Successful:** the file is moved to a `…/processed/` subfolder with a date and time stamp.
* **With errors:** the file is moved to `…/errors/`, a `.log` with the detail is written next to it, and Happyforce receives a notification. Fix the file and upload it again.

***

### 6. Sync behaviour (mirror)

**The CSV is the source of truth.** On each load, Happyforce makes its roster identical to the file:

* Employee **in the CSV that does not exist** in Happyforce → is **created**.
* Employee **in the CSV that already exists** (same `externalId`) → is **updated**. An `x` in a field **removes** that value (except email/externalId); an **empty** value leaves it unchanged. If nothing changes, no operation is performed.
* Employee **in Happyforce but NOT present in the CSV** → is **deactivated**.
* Employees with the **ADMIN** role: are neither modified nor deactivated.

> ⚠️ **Key consequence:** each file must contain the **complete roster**, not just additions or changes. Any employee missing from the CSV (except ADMINs) is interpreted as a deactivation. Do not upload a partial CSV expecting it to only add or change records.
