Integrations Framework

💡 Context

In Factorial, we have developed the first version of our integrations framework, allowing Factorial to sync data (employee compensation, expenses…) into external systems (payroll software, ERPs).

This functionality is exposed through our public API, so that external partners and clients can integrate external software more easily and consistently.

✨ Introduction

This framework enables external developers to sync data from Factorial to the different external softwares in an easy and transparent way for the user, providing the best experience to the user. A summary of the flow of how the framework would be:

  1. Client defines all the data (compensations, expenses) in Factorial
  2. Client clicks on ‘Sync to [external_software_name]
  3. Partner receives a webhook notification
  4. Partner fetches data to sync from Factorial
  5. Partner syncs it to the external software
  6. Partner reports the sync result back to Factorial so the client can have visibility of the result of the sync

🔧 Setup integration for the first time

To get started, the partner needs to specify 2 things to Factorial team:

Data Fields

From Factorial, we can send a set of fields when syncing each item to the external software. The partner must specify 2 things: which data they want to receive and which fields are mandatory for their integration:

Compensations

Fields

Format

Do they want to have this data in our response? (Yes/No)

Is it mandatory to sync to the payroll software (Yes/No)

employee_id (in Factorial)

Integer

employee_company_identifier (in the payroll software)

String

legal_entity_id

Integer

payroll_concept_id

Integer

amount

Integer

  • cents for money
  • minutes for time
  • km for distance
  • units for unit

units (those are the available in Factorial, maybe a mapping needs to be done with the units of the payroll software)

String: one of distance, money, time, unit

effective_on (the effective date is typically the last day of the payroll run)

yyyy-mm-dd

Expenses

FieldsFormatDo they want to have this data in our response? (Yes/No)Is it mandatory to sync to the ERP software (Yes/No)
expense_id (in Factorial)Integer
tbd

Webhook URL

  • URLs where Factorial will send the webhook must be provided. One for production and another for the test environment
  • This URL will be used for all companies using the integration.

Integration UUI

  • Factorial will provide the Partner with an integration_uuid That represents your integration across Factorial environments and is the shared integration identifier between both systems. You will need it in the following steps.

🔧 Setup integration for a new client

When a new client starts using the integration for the first time, some configuration may be required on the partner's side.

1. Set up data mappings

Compensations

GET {baseUrl}/api/{version}/resources/compensations/concepts

  • It is important that the partner filters by

    • categories:
      • earnings_fixed_salary
      • earnings_variable
      • earnings_benefits_in_kind
      • earnings_others
      • deductions
    • with_active_status: true
  • Once the partner has the list of Payroll Concepts, they can ask the client to map them with the payroll concepts codes of their payroll software account.

Expenses

TBD

2. Notify Factorial about the new installation & set up

Once the setup is finished on the partner's side and the integration is ready to be used, it must be enabled in Factorial for that specific client (company) by calling the following endpoint:

Create an Installation FactorialCreates an Installation​


 POST {baseUrl}/api/{version}/resources/marketplace/installations 
 
{
  "integration_uuid": string,  // your integration identifier
  "company_id": integer
}

🔄 Async Integration Flow

1. Receive Webhook Notification

When a sync is triggered by the user in Factorial, Partners’ webhook will receive a POST request with the following payload:

{
  "sync_run_id": integer,
  "integration_uuid": string,  // your integration identifier
  "company_id": integer,
  "capability": string // the type of synchronization (compensation, expenses)
}
❗️

Important

  • Partners’ endpoint must respond with HTTP 200 OK to confirm receipt.
  • The partner should process the sync asynchronously.
  • Partner should not return errors in this response — errors must be handled and reported separately in Step 4.
  • If a webhook notification fails, Factorial will send an email to the partner and retry the notification up to 5 times within 15 minutes. If all retries fail, the user will be notified that the sync was unsuccessful.

2. Fetch Sync Data

To retrieve the data items to sync, call the following endpoint:

Reads all Syncable items

GET {baseUrl}/api/{version}/resources/integrations/syncable_items?sync_run_id={n}

using the sync_run_id value received in the webhook payload.

The response will contain a paginated list of data entries to sync. We encourage you to use the cursor pagination, which is more performant than the offset pagination.

Example with Compensation data

{ 
		"data": [
			{
        "syncable_sync_run_id": 1,
        "sync_payload": {
            "employee_id": 8,
            "payroll_concept_id": 16,
            "amount": 50,
            "unit": "units",
            "effective_on": "2025-08-31",
            "employee_company_identifier": "23537657",
            "legal_entity_id": 5
	        }
      },
			{
        "syncable_sync_run_id": 2,
        "sync_payload": {
            "employee_id": 6,
            "payroll_concept_id": 20,
            "amount": 5198, # Amount in cents (5198 -> 51.98)
            "unit": "money",
            "effective_on": "2025-08-31",
            "employee_company_identifier": "23456787",
            "legal_entity_id": 3
	        }
       },
			{...}
		],
		"meta": {
        "has_next_page": true,
        "has_previous_page": false,
        "start_cursor": "MQ==",
        "end_cursor": "MTAw",
        "total": 869,
        "limit": 100
		}
	}

3. Process Sync Data

When processing the data, the partner is responsible for:

  • Performing the delta:
    • The client may trigger the sync multiple times for the same period and company, potentially with different filters. These are not concurrent, but your system must ensure idempotency or manage updates accordingly.
  • Pushing data to the external system:
    • Create or update items (compensations, expenses) in the external payroll provider based on the sync data.
❗️

Please clarify with Factorial how updates should be managed:

Should we overwrite previous values defined directly in the external system, or should a merge be done? (This depends on which system is the source of truth.)


4. Report Sync Status

Once sync items are processed, the Partner needs to report the final status of each syncable_sync_run received in the step 2 back to Factorial by calling:

Updates a Syncable sync run

PUT {baseUrl}/api/{version}/resources/integrations/syncable_sync_runs/{id}

{
  "status": "success"| "failed" | "invalid",
  "error_messages": {
    "sycn_api_error": string,
    "sync_validation_error": string
      ...
    }
}
  • Use success when the item was synced correctly to the external software.
  • Use failed if something went wrong and the item was not synced.
  • Use invalid if the data is structurally incorrect or missing required fields.
    • Example: Factorial sends you a compensation with a payroll_concept_id that you don’t have mapped with any code of the payroll software
❗️

One hour after the user starts the sync process, any items without a reported status will be marked as 'failed,' and the sync_run will be updated to its corresponding final state.