The Embedded Payroll Question

Payroll Engine’s premise is clear: an embeddable calculation engine, not a monolithic product. The REST API handles payroll processing. Country regulations handle compliance. The provider’s own platform handles everything else — branding, workflows, user management.

But the first question every provider asks is: “How do users enter data?”

Payroll needs input. Employee master data. Tax class changes. Salary updates. Address mutations. Bank account details. In a traditional payroll system, these are forms — screens with fields, dropdowns, date pickers, validation messages. In an embedded scenario, the natural assumption is that the engine should ship embeddable form components: a salary input widget, an onboarding wizard, a case change dialog that drops into any frontend.

Payroll Engine ships a Blazor WebApp that does exactly this — as a standalone application. But it’s not embeddable. And the reason it’s not embeddable isn’t a missing feature. It’s a deliberate architectural decision. Embedded UI widgets are the wrong answer to the right question.

Why Embedded UI Widgets Don’t Work

The idea is appealing: ship a <payroll-case-input> web component that renders a dynamic form for any case type. Drop it into any React, Vue, or Angular application. Done.

In practice, this fails for five reasons:

Partners build their own UIs. EOR providers, payroll bureaus, and HCM vendors — the organizations that embed Payroll Engine — have their own design systems, their own UX patterns, their own frontend frameworks. They don’t want a foreign widget in their application. They want the data, and they want to present it their way.

Blazor doesn’t embed. The existing WebApp is built with Blazor Server. Blazor components cannot be consumed as Web Components in non-Blazor applications without massive wrapper infrastructure. A React developer cannot npm install a Blazor component.

UI is opinionated. Form layout, field ordering, validation message placement, error state styling, accessibility patterns, localization, responsive behavior — every design decision in a form widget is an opinion. What works for a German payroll bureau with 20 fields per screen doesn’t work for a mobile-first EOR platform showing three fields at a time.

Maintenance is exponential. Supporting a UI component library across frameworks (React, Vue, Angular, Web Components), across versions, across browsers, across design system integrations — this is a product in itself. The payroll engine’s competitive advantage is compliance logic, not form rendering.

Case forms are dynamic. This is the decisive point. Which fields appear on a case input form depends on the regulation. A German employee case has Steuerklasse, Konfession, Sozialversicherungsnummer. A Dutch employee case has BSN, Loonheffingskorting, Pensioenregeling. A Spanish employee case has Grupo de Cotización, Tipo de Contrato, IRPF. The fields change when regulations update. A hardcoded widget is a contradiction — the form must be as dynamic as the regulation it represents.

The API Already Has Everything

The premise of an embedded widget is that the frontend needs help generating forms. But the Payroll Engine API already delivers everything a frontend needs to build its own case input UI.

The Case Model is fully available through the REST API:

Endpoint Returns
GET .../cases/available Cases the user can interact with (availability scripts evaluated per user context)
POST .../cases/build/{name} Full CaseSet: fields with types, mandatory flags, defaults, current values, related sub-cases
GET .../casefields Field definitions merged across regulation layers
GET .../caserelations Parent/child case relationships
POST .../cases Submit a case change (create or update)

The Blazor WebApp builds its forms from exactly these endpoints. It calls cases/available to populate the menu, cases/build to render the form fields, and cases to submit the result. Any frontend framework can do the same — the API is the schema, not a separate metadata layer on top of it.

A partner building a React onboarding flow calls the same endpoints, maps valueType: Money to their currency input component, maps valueType: String to their text field, maps valueMandatory: true to their required-field styling — and the form is dynamic, regulation-aware, and visually consistent with their own design system.

No schema gap: There is no missing “form schema API” that a widget would bridge. The Case Model API is the form schema. The question is not “how do we expose the schema?” but “what consumes it?”

Chat as the Case Input Interface

This is where the paradigm shifts. If the API delivers the schema, and a frontend renders it as a form, then an AI agent can render it as a conversation. The form fields become questions. The validation rules become constraints. The case submission becomes the final tool call. The chat is the form.

Three MCP tools make this work:

Tool Server Purpose
get_available_cases OSS “What cases can I offer?” — lists available cases per user, employee, and regulation
build_case Pro “What do I need to ask?” — returns all fields, types, mandatory flags, defaults, related sub-cases
create_employee_case_change Pro “Submit the result” — writes the completed case change to the payroll engine

A concrete example

A payroll operator says: “Add a new employee to the German payroll.”

The AI agent calls get_available_cases with caseType: Employee. The API returns the available cases for the German regulation: Employment, Salary, TaxData, SocialSecurity, BankAccount, Address. The agent presents these as options.

The operator selects Employment. The agent calls build_case for the Employment case. The API returns the field definitions: FirstName (String, mandatory), LastName (String, mandatory), EntryDate (Date, mandatory), Steuerklasse (Integer, mandatory, lookup: TaxClasses), Konfession (String, optional), Sozialversicherungsnummer (String), Krankenkasse (String, mandatory), and twelve more fields defined by the German regulation.

The agent doesn’t dump all fields at once. It groups them logically, asks for the mandatory fields first, explains what Konfession affects (church tax), offers the valid Steuerklasse values from the lookup, and validates formats as the operator provides them. When all required fields are collected, it calls create_employee_case_change with the assembled payload. Server-side validateActions run. If validation passes, the case is persisted. If it fails, the agent reports the specific validation error and asks for correction.

The entire interaction is a conversation. No form was rendered. No frontend code was written. No widget was embedded.

Dynamic by Default

The deepest advantage of the chat-as-interface model is that it’s dynamic by definition. The agent reads the schema at runtime — it doesn’t render a compiled form.

New mandatory field. The 2027 German regulation adds a new case field: DigitaleRentenUebersicht (Boolean, mandatory). No code change. The next time an agent calls build_case for Employment, the field is in the response. The agent asks the question. The operator answers. Done.

Conditional visibility. The Dutch regulation has a field PensionOptOut that is only available when PensionScheme is “BPV”. This is controlled by availableActions in the Case Model. When the agent calls build_case, the field is included or excluded based on current data. The agent never asks questions that don’t apply.

New case type. A provider adds a company-level case for collective agreements. It appears in get_available_cases with caseType: Company. The agent can offer it immediately — no deployment, no release, no form update.

Related sub-cases. The Employment case in Spain includes related cases for Contrato and Convenio. The build_case response returns these as RelatedCases[]. The agent walks through the parent case and its sub-cases in sequence, collecting all required data in one conversation.

Compare this to a hardcoded form: every regulation change requires a frontend update. Every new field requires a new component. Every conditional visibility rule requires frontend logic that mirrors the backend. The form is a copy of the regulation — and copies drift.

The regulation is the form definition. Cases and CaseFields in the Payroll Engine are not just data containers — they are a complete form schema with types, validations, conditions, and relationships. Any consumer that reads this schema at runtime inherits every regulation update automatically.

When You Still Need a Form

Chat is not a universal replacement for forms. It’s important to be honest about where it works and where it doesn’t.

Bulk operations. Onboarding 50 employees from a CSV export is not a chat conversation. It’s a batch import. Payroll Engine’s adapter framework handles this — Personio, AFAS, Sage, Silae, and CSV connectors that map external data to case changes in bulk. Chat is for individual case changes, not mass data loads.

Compliance workflows. A payrun approval with four-eyes principle — where a second user must review and confirm before results become legally binding — requires a structured workflow UI. The WebApp handles this with explicit approval screens, audit trails, and role-based access. Chat can trigger a payrun, but the approval workflow belongs in a dedicated interface.

Dashboards and reporting. Visualizing payrun results, comparing periods, drilling into wage type breakdowns — these are visual tasks. The MCP server provides the data, but rendering charts, pivot tables, and comparison views requires a visual UI. The WebApp or a custom dashboard built against the API serves this need.

Where chat excels. Individual case changes: salary adjustments, address updates, tax class changes, bank account mutations. Ad-hoc queries during onboarding. Quick data entry by payroll operators who know what they want to do and don’t need a navigation structure to find the right screen. These are the interactions where a form is overhead and a conversation is faster.

Three channels, one API

Channel Consumer Best for
REST API Partner’s own frontend Full custom UI, bulk operations, complex workflows
MCP Server AI agent (Cursor, Claude, custom) Individual case changes, ad-hoc queries, conversational data entry
WebApp Standalone Blazor application Full payroll management, approval workflows, reporting

All three channels consume the same API. All three read the same Case Model. All three submit case changes through the same validation pipeline. The difference is the interface — not the engine.

The embedded payroll question — “how do users enter data?” — has three answers, not one. And the newest answer — a conversation driven by the regulation schema itself — is the one that requires zero frontend code from the provider.

See it in action

The MCP Server Pro with case input dialog is available for evaluation. Connect it to your AI agent and run a case change conversation against the German, Dutch, or Spanish regulation.

Get in Touch →
← Back
All Articles