Outbound: Factorial → External Software
How a partner handles an outbound sync run: handle the run notification, fetch the data, write in the external software and report each row.
💡 Context
Outbound is the synchronization or import process that fetches data from Factorial and introduces it into the external software. Inbound goes in the opposite direction, data is fetched from the external sofware and pushed into Factorial.
In outbound, Factorial knows what needs to be synced. Records (i.e., employees, compensations or expenses) have an associated SyncableState that reports if the record was synced / exported successfully, had an error in the last sync attempt, or has been marked as outdated. In any case, record data must be fetched though the relevant public api endpoints.
✨ Introduction
A summary of the outbound flow is:
- Client defines all the data (compensations, expenses) in Factorial
- Client clicks on ‘Sync to [external_software_name]
- Partner receives a webhook notification
- Partner fetches data to sync from Factorial
- Partner syncs it to the external software
- Partner reports the sync result back to Factorial so the client can have visibility of the result of the sync
Important
Please note that steps 3,4,5 and 6 are mandatory for the partner to be implemented. See Async Integration Flow for more details.
🔧 Setup
Before triggering the Outbound Sync Flow, the integration must be setup in Factorial, and installed for this client. Please refer to Integrations Framework on how to achieve these steps.
🔄 Outbound Sync Flow
Important
Please note all described steps are mandatory for the partner to be implemented.
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
}Important
- Partners’ endpoint must respond with HTTP
200 OKto 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, there is a 3 steps process:
GET {baseUrl}/api/{version}/resources/integrations/syncable_sync_runs?sync_run_id={n}
using the sync_run_id value received in the webhook payload.
Syncable Sync Runs links a sync run (user clicked in sync button) with the next steps, Syncable states
The response will contain a paginated list of all the Syncable sync runs. You must use Pagination to get the full set of items.
{
"data": [
{
"id": "1",
"status": "running",
"error_messages": [],
"sync_run_id": "11",
"syncable_state_id": "34",
"company_id: "7"
},
{
"id": "2",
"status": "running",
"error_messages": [],
"sync_run_id": "11",
"syncable_state_id": "35",
"company_id: "7"
},
{...}
],
"meta": {
"has_next_page": true,
"hasprevious_page": false,
"start_cursor": "MQ==",
"end_cursor": "MTAw",
"total": 869,
"limit": 100
}
}GET {baseUrl}/api/{version}/resources/integrations/syncable_states?ids[]=34&ids[]=35
using the syncable_state_id values received in the last call payload.
Syncable states reflect the current status of each of the syncable (i.e. employee, compensation, expense)
The response contains references to the final list of endpoints to call, represented in syncable_id. and resource_syncable_type
{
"data": [
{
"id": "1",
"syncable_id": "71",
"resource_syncable_type": "expenses/expensable",
"integration_uuid": "12207342-52c5-4888-9349-3270780e1234",
"status": "syncing",
"error_messages": [],
"external_identifier": "external-software-234"
},
{
"id": "2",
"syncable_id": "72",
"resource_syncable_type": "expenses/expensable",
"integration_uuid": "12207342-52c5-4888-9349-3270780e1234",
"status": "syncing",
"error_messages": [],
"external_identifier": "external-software-235"
},
{...}
],
"meta": {
"has_next_page": true,
"hasprevious_page": false,
"start_cursor": "MQ==",
"end_cursor": "MTAw",
"total": 869,
"limit": 100
}
}2.3 Read each resource
From resource_syncable_type we infer the endpoint to call. Just map it to the url
GET {baseUrl}/api/{version}/resources/{resource_syncable_type}?ids[]=71&ids[]=72
For instance:
expensables/expenses. is mapped to Reads all Expensables
Call extra endpoints as neededBesides the root resource, determined by
resource_syncable_typefields, you might need to call other extra endpoints.
Please refer to Payroll integrations, Finance integrations and Time tracking integrations for indications on these endpoints
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.1 back to Factorial by calling:
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
successwhen the item was synced correctly to the external software. - Use
failedif something went wrong and the item was not synced. - Use
invalidif the data is structurally incorrect or missing required fields.- Example: Factorial sends you a compensation with a
payroll_concept_idthat you don’t have mapped with any code of the payroll software
- Example: Factorial sends you a compensation with a
One hour after the user starts the sync process, any items without a reported status will be marked as
failed, and thesync_runwill be updated to its corresponding final state.
Updated 2 days ago

