The Data Origin Problem
Payroll calculations don't operate on data they create. They operate on data that originates somewhere else entirely — in HR platforms, time tracking systems, benefits administration tools, and employment contracts managed by people who never interact with the payroll engine directly. An employee's tax class, social security number, salary, working hours, benefit elections, and employment start date all live in systems designed for HR management, not statutory calculation.
This creates a fundamental integration challenge. The payroll system needs current, accurate, structured data from these source systems before every payroll run. Miss a salary change, and the gross pay is wrong. Miss a tax class update, and the withholding is incorrect. Miss a new hire, and someone doesn't get paid at all.
The traditional approach is manual data entry. A payroll administrator logs into the payroll system, opens each employee record, and types in the values from a spreadsheet or a printout of the HR system. This works for a bureau processing payroll for a single company with thirty employees. It breaks down immediately at scale.
A payroll provider serving 200 client companies across five countries has tens of thousands of employees. Each month brings hundreds of changes: new hires, terminations, salary adjustments, address changes, benefit elections, tax class updates. Manual entry is not just slow — it's a reliability risk. Every keystroke is a potential error. Every missed update is a potential compliance violation. Every duplicate entry is a potential overpayment or underpayment that requires retro correction.
The solution is automated data flow. But automated data flow raises its own architectural question: in which directions should data flow, and who owns the truth?
Bidirectional by design: PayrollEx adapters import HR master data from source systems into the payroll engine (inbound), and selected adapters export computed payroll results to ERP and finance systems (outbound). The HR system remains the single source of truth for employee master data. Payroll Engine is the single source of truth for calculation results. Master data never writes back — computed results flow forward to finance.
This is a deliberate architectural decision with concrete benefits. The HR platform is the system of record for employee information — one writer, no conflicts. The payroll engine is the system of record for payroll results — statutory outputs that flow onward to finance ledgers, reports, and consolidation queries. Each system is authoritative for its own domain. Data flows in the directions that domain ownership requires.
Ten Provider Adapters
PayrollEx ships adapters for ten source systems, covering the major HR, ERP, and payroll platforms used across the eleven supported countries. Each adapter understands its source system's API, authentication model, data structures, and pagination behavior. Together, they cover the vast majority of HR and ERP platforms that a multi-country payroll provider will encounter in practice. Five of the ten connectors also support outbound export of computed results to the target system's finance or accounting module.
AFAS Software
AFAS is a Dutch ERP platform widely used in the Netherlands and Belgium. It combines HRM, finance, CRM, and project management in a single suite. For payroll providers in the Benelux region, AFAS is frequently the system of record for employee master data, employment contracts, and salary mutations. The AFAS adapter connects via the AFAS REST API using token-based authentication, pulling employee records, contract details, salary components, and organizational structure. For outbound flows, it exports computed payroll results as journal entries to the AFAS Finance module.
CSV
Not every source system has an API. Legacy systems, custom-built HR tools, and smaller platforms often export data only as tabular files — CSV, TSV, or fixed-width text. The CSV adapter is the universal fallback: it reads structured tabular data from delimited files, applies column mapping to PayrollEx case fields, and imports the result. Any system that can produce a structured export can feed into PayrollEx through this adapter. The CSV adapter also supports outbound: after a payrun, it writes a structured result file that any downstream system can consume.
Microsoft Dynamics 365
Microsoft Dynamics 365 is an enterprise ERP and CRM suite used across Europe, North America, and globally. The Dynamics 365 adapter connects to its Human Resources module via the OData-based REST API, pulling employee profiles, position data, and compensation records. For outbound flows, it posts computed payroll results as journal entries to the Finance module's general ledger — enabling payroll costs to appear in accounting without manual transfer.
Odoo
Odoo is an open-source ERP platform widely adopted by mid-sized companies globally, combining HR, accounting, project management, and CRM in a single system. The Odoo adapter connects via its XML-RPC API to import employee master data and contract details. For outbound flows, it exports computed payroll results as journal entries directly into Odoo's accounting module — making payroll costs visible in the general ledger without any manual bookkeeping step.
PayFit
PayFit is a European payroll and HR platform operating in France, Germany, Spain, and the United Kingdom. The PayFit adapter imports employee records, contract data, and variable pay elements from its API, mapping them to the corresponding PayrollEx country regulation fields for each supported market.
Personio
Personio is the dominant European HR platform for small and mid-sized companies, serving over 10,000 organizations. Its REST API provides structured access to employee profiles, salary history, absence records, benefit elections, and custom attributes. The Personio adapter handles OAuth 2.0 authentication, paginated list endpoints, and the platform's event-based change tracking for efficient delta synchronization.
Sage
Sage operates globally with a product portfolio spanning Sage 50, Sage 200, Sage Intacct, and Sage Business Cloud. Different Sage products serve different markets — Sage 50 for small businesses, Sage 200 for mid-market, Intacct for larger enterprises. The Sage adapter supports multiple API versions and authentication flows to accommodate this product diversity, pulling HR and payroll-relevant data from whichever Sage product the client operates. For outbound flows, it exports payroll journal entries into the Sage accounting module.
Silae
Silae is a French payroll and HR platform dominant in the bureau market. French payroll bureaus manage payroll for hundreds of client companies, and Silae is their primary tool. The Silae adapter connects to its API to pull employee data, contract information, and variable pay elements that feed into PayrollEx's French regulation calculations.
SAP SuccessFactors
SAP SuccessFactors is a global HCM platform used by large enterprises across all regions. The SuccessFactors adapter connects via its OData API to pull employee master data, organizational assignments, and compensation information into Payroll Engine, supporting multi-country enterprise deployments where HR data lives in a central SAP system.
Workday
Workday is a global human capital management platform used by large enterprises worldwide. The Workday adapter connects via its REST API using OAuth 2.0 (Integration System User), importing worker identity, employment, job, and compensation data into Payroll Engine for multi-country enterprise deployments where HR data is centralized in Workday.
| Provider | Primary Market | API Type | Authentication | Direction |
|---|---|---|---|---|
| AFAS Software | Netherlands, Belgium | REST | Token (App Connector) | Inbound + Outbound |
| CSV | Universal | File (CSV/TSV) | Filesystem | Inbound + Outbound |
| Microsoft Dynamics 365 | Global | REST (OData) | OAuth 2.0 | Inbound + Outbound |
| Odoo | Global | XML-RPC | API Key | Inbound + Outbound |
| PayFit | FR, DE, ES, UK | REST | OAuth 2.0 | Inbound |
| Personio | Europe (pan-regional) | REST | OAuth 2.0 | Inbound |
| Sage | Global (multi-product) | REST / SOAP | OAuth 2.0 / API Key | Inbound + Outbound |
| Silae | France | REST | API Key | Inbound |
| SAP SuccessFactors | Global (enterprise) | REST (OData) | OAuth 2.0 | Inbound |
| Workday | Global (enterprise) | REST (v1) | OAuth 2.0 | Inbound |
The ten adapters are not mutually exclusive. A provider serving a Dutch logistics company might use the AFAS adapter for employee master data while also pulling time records via CSV from a warehouse management system. A French bureau might use Silae for domestic employees and Personio for a client's German subsidiary. The adapter layer is composable — multiple adapters can feed into the same PayrollEx tenant.
Adapter Architecture
Every adapter shares the same internal architecture, provided by the Adapter Core framework. This framework defines the pipeline stages, handles retry logic, manages state between runs, and provides the extension points that each provider adapter implements. The result is a consistent operational model across all ten adapters — same logging, same error handling, same configuration structure — regardless of the source system's API quirks.
The Adapter Core Framework
At the foundation is PayrollEngine.Adapter.Core — a NuGet package that provides the pipeline infrastructure. It defines the interfaces each adapter must implement, manages pipeline execution, handles transient failures with configurable retry policies, and maintains synchronization state between runs. The core framework is source-system-agnostic: it knows how to orchestrate a data import pipeline, but not how to talk to Personio or AFAS.
Provider Adapters as Deployable Units
Each provider adapter — AFAS, CSV, Dynamics 365, Odoo, PayFit, Personio, Sage, Silae, SAP SuccessFactors, Workday — is a deployable unit, not a NuGet library. It references the Adapter Core framework and implements the provider-specific logic: API client, authentication flow, response parsing, and field extraction. You deploy a provider adapter onto one of the two hosting options (Console or Service), configure it with connection credentials and mapping rules, and run it.
This separation means upgrading one adapter doesn't affect others. A breaking change in Personio's API requires updating only the Personio adapter. The AFAS adapter, the Sage adapter, and the hosting infrastructure remain untouched.
Pipeline Stages
Every adapter run executes the same five-stage pipeline:
- Authenticate — Establish a session with the source system. OAuth token exchange, API key validation, or file access. The adapter caches tokens within their validity window to avoid redundant authentication calls.
- Fetch — Retrieve records from the source system. This stage handles pagination (cursor-based, offset-based, or link-based), rate limiting, and partial failure recovery. For delta sync, it fetches only records changed since the last successful run.
- Map — Transform source system fields into PayrollEx case fields. A Personio "Steuerklasse" becomes a PE "DE.Steuerklasse". A Sage "NI Number" becomes a PE "UK.NiNumber". Mapping is configurable per provider, per country, and per tenant.
- Validate — Check that mapped data meets PayrollEx's requirements. Required fields must be present. Types must be convertible. Values must fall within acceptable ranges. A salary of -500 or a tax class of 7 fails validation before it reaches the payroll engine.
- Import — Write validated case values into PayrollEx via its REST API. The import creates or updates case values with proper timestamps, ensuring the payroll engine's case history remains correct and auditable.
Delta synchronization: After the first full import, subsequent runs transfer only what changed. The adapter tracks synchronization state — timestamps, ETags, or content hashes depending on the source system's capabilities — and fetches only modified records. A monthly payroll run for 500 employees might transfer only 30 changed records instead of the full dataset.
Each stage produces structured log output. A failed authentication produces a clear error with the OAuth endpoint and response code. A validation failure identifies the specific employee, field, and constraint that was violated. A mapping gap — a source field with no configured target — is logged as a warning, not silently dropped.
Field Mapping: From Personio to PE Case Fields
HR systems and payroll engines use different names for the same concepts. Personio calls it "Steuerklasse". PayrollEx stores it as "DE.Steuerklasse". Sage calls it "NI Number". PayrollEx stores it as "UK.NiNumber". AFAS calls it "BSN". PayrollEx stores it as "NL.Bsn". This is not a minor cosmetic difference — it's a structural mapping problem that varies by provider and by country.
The same source system may feed data into multiple country regulations. A Personio instance serving a company with employees in Germany, Austria, and the Netherlands contains German tax classes, Austrian social insurance numbers, and Dutch BSN identifiers — all in Personio's own field naming scheme, all needing to land in different PE case fields depending on the employee's country.
Mapping Configuration
Field mapping is defined in JSON configuration files, structured by provider and country. A mapping entry specifies the source field path (in the provider's data model), the target PE case field (fully qualified with country prefix), and any transformation rules needed during conversion.
Example mappings for the Personio adapter importing German employee data:
| Personio Field | PE Case Field | Transformation |
|---|---|---|
tax_data.tax_class |
DE.Steuerklasse |
Integer validation (1–6) |
social_security_number |
DE.SvNummer |
Format validation (12 digits) |
fix_salary |
DE.Gehalt |
Decimal, currency EUR |
tax_data.church_tax |
DE.Konfession |
Boolean → enum mapping |
tax_data.health_insurance |
DE.Krankenkasse |
String, lookup validation |
Tenant-Level Overrides
The default mapping covers standard field names as documented in each provider's API. But real deployments encounter customization. A Personio customer might use a custom attribute for car allowance amounts. A Sage instance might store the social insurance branch in a non-standard field. The mapping configuration supports tenant-level overrides: a specific client's adapter instance can extend or replace the default mapping without affecting other clients using the same provider adapter.
Validation Before Import
Mapping alone doesn't guarantee data quality. After transformation, every value passes through validation rules specific to the target case field:
- Required fields — PE case fields marked as mandatory must have non-null values. A missing salary triggers an import failure for that employee, not a silent zero.
- Type conversion — Source values must be convertible to the target field's value type. A string "abc" in a numeric salary field fails explicitly.
- Range checks — Statutory fields have valid ranges. German tax class must be 1–6. Austrian social insurance percentage must be between 0 and 100. Values outside these ranges are rejected.
- Referential integrity — Fields that reference lookups (health insurance provider, pension fund) are validated against the regulation's lookup tables. An unknown insurance provider code is flagged before import.
Validation failures don't abort the entire import. They quarantine the affected employee record and continue with the rest. The provider receives a detailed report of which employees failed validation, which fields were problematic, and what values were rejected — enabling targeted correction in the source system rather than re-running the entire import.
Deployment Options
An adapter needs to run somewhere. The question is where, how often, and with what operational model. PayrollEx provides two hosting options that share the same adapter core — same pipeline, same mapping, same validation — but differ in how they're triggered and managed.
Adapter Console: Scheduled Execution
The Adapter Console is a command-line application that executes a single adapter run and exits. It's designed for scheduled execution: a Windows Task Scheduler job, a cron entry on Linux, or a CI/CD pipeline step that runs before payroll processing.
A typical deployment looks like this: three days before the monthly payroll deadline, a scheduled task fires the Personio adapter. It authenticates, fetches all changes since the last run, maps and validates the data, imports into PayrollEx, and produces a log file. If the run succeeds, the payroll administrator reviews the import summary and proceeds with the payroll run. If it fails, the error log identifies exactly what went wrong.
The Console model is simple to operate. No background process to monitor. No service to keep alive. No memory leaks accumulating over weeks. Each run is isolated: fresh authentication, fresh state, fresh log file. For providers who run payroll on a fixed monthly schedule, this is often sufficient.
Adapter Service Host: Continuous Operation
The Adapter Service Host is a long-running process — a Windows Service or a Linux systemd unit — that executes adapter runs on a configurable schedule or in response to events. It stays resident, maintains connection pools, and can react to webhooks from source systems.
This model suits providers who need more frequent synchronization. A company that processes weekly payroll, or one that needs near-real-time visibility into headcount changes, benefits from a service that runs every hour or responds to Personio webhook notifications when an employee record changes.
The Service Host supports multiple adapter instances in a single process. A provider serving clients across Personio, AFAS, and Sage can run all three adapters from one service, each on its own schedule, with shared logging and monitoring infrastructure.
| Aspect | Adapter Console | Adapter Service Host |
|---|---|---|
| Execution model | Single run, then exit | Continuous, periodic or event-driven |
| Scheduling | External (cron, Task Scheduler) | Internal (configurable intervals, webhooks) |
| Resource usage | Zero between runs | Resident process, minimal idle footprint |
| Multiple adapters | Separate scheduled tasks | Single process, multiple adapter instances |
| Best for | Monthly payroll, fixed schedules | Weekly/daily sync, event-driven updates |
Both options produce the same result: validated, mapped case values written into PayrollEx. The choice between them is operational, not functional. Providers with simple scheduling needs start with the Console. Providers with complex multi-client environments or frequent sync requirements move to the Service Host. Migration between the two requires no changes to mapping configuration or adapter logic — only the hosting wrapper changes.
Why Results Flow Out, Master Data Doesn't
Adapters are bidirectional — but the directionality follows strict domain ownership. Master data flows inbound: employee records, tax classes, salaries, and employment contracts are imported from the HR system into Payroll Engine. Computed results flow outbound: payslips, tax withholdings, employer costs, and net pay can be exported to ERP and finance systems as journal entries or result files. What never happens is the reverse of master data flow. Payroll Engine does not write employee records back into the HR system.
Two Systems, Two Authorities
The HR or ERP platform is the single source of truth for employee master data. It owns the employee record — name, address, tax class, salary, contract start date. It is the system where HR administrators make changes, where employment contracts are stored, where organizational structures are managed.
Payroll Engine is the single source of truth for payroll results. It owns the calculation — gross pay, statutory deductions, employer contributions, net pay for each period. It is the system that applies country regulations, runs statutory algorithms, and produces legally required output.
These are two different domains with two different authorities. Keeping them separated eliminates an entire class of architectural problems.
Outbound: Results to Finance, Not Back to HR
When selected connectors (Odoo, Microsoft Dynamics 365, AFAS, Sage, CSV) export computed results outbound, the target is always the finance or accounting side of the ERP — not the HR master data side. Payroll Engine posts computed results as journal entries or ledger postings so that payroll costs appear in accounting. It does not update employee salary records, tax class fields, or any master data in the source HR system.
This distinction is what makes outbound safe. The payroll result (e.g., "gross pay EUR 5,200 for employee X in May 2026") is a new fact produced by Payroll Engine — it belongs to the finance domain. The salary (e.g., "employee X earns EUR 5,000/month") is a fact owned by HR. These are different facts, different domains, and different system-of-record responsibilities. Outbound export puts results where they belong without overwriting what HR owns.
Why Master-Data Write-Back Creates Conflicts
If Payroll Engine could update employee salary data in Personio after a retro correction, an immediate question arises: which value is authoritative — Personio's original entry or Payroll Engine's updated figure? If both systems can write the same field, you need conflict resolution — timestamps, version vectors, manual review queues. Every conflict resolution strategy has failure modes. Every failure mode produces incorrect payroll.
The outbound path sidesteps this entirely. Payroll Engine computes results and posts them to a finance module that has no master-data interest in the HR fields. There is no conflict because the two systems write to entirely different domains.
- HR staff see payroll results in their finance module — in the appropriate context, not mixed into employee records
- Retro corrections in Payroll Engine update the payroll result domain only — the HR system is not affected
- Audit trails remain clean — each system records changes in its own domain, making compliance verification straightforward
- Data retention policies stay independent — HR and payroll have different statutory retention periods and can be managed separately
The Error Model
Inbound imports have a simple error model. If an import fails, no data reached Payroll Engine — the previous state is intact. If an import partially succeeds, you know exactly which records were imported and which weren't. There's no half-written state in the source system.
Outbound exports have an equally clean model. If a journal entry export fails, the computed results in Payroll Engine are unaffected — the export can be retried without re-running the payroll. The two directions are independent operations with independent failure modes, neither of which corrupts the other system's state.
Accessing Payroll Results
Computed payroll results flow outward through multiple channels, each suited to different consumers:
- REST API — Full programmatic access to payroll results, payslips, employer cost breakdowns, and historical data. Any system that needs payroll outputs can query the API directly.
- MCP Server — AI-native access to payroll data for LLM-powered tools and integrations. Query employee costs, compare periods, generate summaries.
- Reports — Structured PDF/Excel output for finance teams, auditors, and regulatory submissions. Consolidation reports aggregate across countries and currencies.
- Consolidation queries — Cross-country, cross-currency aggregation for global employer cost views.
- Outbound adapters — Direct export to ERP/finance systems as journal entries (Odoo, Dynamics 365, AFAS, Sage) or structured result files (CSV).
Principle: Master data flows into Payroll Engine from HR systems via inbound adapters. Computed results flow out via REST API, MCP, reports, and — for selected connectors — directly to ERP/finance systems as journal entries or result files. The HR system remains the single writer of master data. Payroll Engine remains the single producer of payroll results. Each system is authoritative for its own domain. Results flow onward to finance without creating master-data conflicts.
This architecture keeps the system predictable, auditable, and operationally simple. For providers running multi-country payroll at scale, a clear domain boundary — one source of truth per system — is worth more than any shortcut that blurs it.
See it in practice
Explore the adapter framework, provider coverage, and deployment options — or get in touch to discuss how PayrollEx adapters integrate with your HR platform stack.
Get in Touch →