Skip to content

HRIS · Beta

HR WORKS
API integration

Ship HRIS features without building the integration. Full HR WORKS API access via Proxy, normalized data through Unified APIs, and 140+ MCP-ready tools for AI agents — all extensible to your exact use case.

Built for specific customer use cases. Issues are resolved quickly.

Talk to us
HR WORKS

Use Cases

Why integrate with HR WORKS

Common scenarios for SaaS companies building HR WORKS integrations for their customers.

01

Localize spend management for DACH customers

Corporate card and expense platforms can sync HR WORKS cost centers, cost objectives, and organization units to categorize spend, then push transactions back as expense reports so German finance teams keep a single compliant record.

02

Automate IT provisioning from HR events

IAM and provisioning tools can subscribe to HR WORKS person webhooks to create accounts for new hires on their join date and revoke access when employees become inactive — no manual IT tickets required.

03

Power workforce and shift planning with real absence data

Scheduling SaaS can pull absences, sick leaves, remote work, holidays, and available working hours to avoid double-booking employees who are off, and push planned shifts back as working times.

04

Bridge ATS to HRIS for hired candidates

Applicant tracking systems can push hired candidates into HR WORKS as onboarding documents along with their contract and resume files, eliminating the manual handoff between recruiting and HR admin.

05

Sync billable project hours into payroll-ready records

Project management and agency tools can mirror time tracking projects and project customers, then push logged working times into HR WORKS so hours flow directly into DACH-compliant payroll.

What You Can Build

Ship these features with Truto + HR WORKS

Concrete product features your team can ship faster by leveraging Truto’s HR WORKS integration instead of building from scratch.

01

Two-way employee master data sync

Read and write HR WORKS persons, person master data, and employments to keep employee records aligned with your product in both directions.

02

Unified time-off module

Surface vacation absences, sick leaves, and remote work as distinct primitives — or use the unified Timeoff Requests/Balances/Types/Policies endpoints for a single consistent model.

03

Expense and travel report ingestion

List and create expense reports, attach receipts by receipt type, and track travel requests scoped to the correct personnel number.

04

Org hierarchy mirroring

Map HR WORKS permanent establishments, organization units, cost centers, and cost objectives into your product's groups, departments, and locations.

05

Personnel file and onboarding document uploads

Push contracts, policies, and onboarding paperwork directly into personnel file entries or onboarding documents, with file attachments included.

06

Real-time HR event listeners

Create HR WORKS webhooks for resources like person, applicant, absence, and sick leave to trigger downstream workflows the moment they change.

SuperAI

HR WORKS AI agent tools

Comprehensive AI agent toolset with fine-grained control. Integrates with MCP clients like Cursor and Claude, or frameworks like LangChain.

get_single_hr_works_absence_job_by_id

Get the status and result of an asynchronous absence write. id is the jobId returned by absences.create, absences.bulk_update, absences.bulk_delete, absences.update, absences.delete. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

hr_works_absence_jobs_get_or_error

Get the status of an absences write job, like get (GET /v2/absences/jobs/{jobId}), except that an HTTP error from HR WORKS (e.g. 429 rate limit, 5xx) is returned as a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified timeoff_requests create/update/delete mappings, which poll the job after HR WORKS accepted the write and must still report the job id when a poll fails.

get_single_hr_works_absence_type_job_by_id

Get the status and result of an asynchronous absence type write. id is the jobId returned by absence_types.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_absence_types

List the absence types configured in HR WORKS with their key, name and rules (holiday entitlement, time account, substitution, payroll use). Use the keys as the types filter of absences.list; onlyActive=true returns only active types. Not paginated: everything comes back in one response.

create_a_hr_works_absence_type

Create absence types in bulk: body {data: [...]} with 1-1000 types (name, key and rule flags such as reducesHolidayEntitlement or isSubstitutionMandatory). Asynchronous: returns a jobId; poll absence_type_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

list_all_hr_works_absences

List absences per person in a date range (type, dates, half-days, status, working days); filter by persons, types or statusFilter. interval splits the range (days, weeks, months); count=true returns per-type day totals instead. Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_absence_by_id

Get one absence; id is the absence number (the person's license number, an underscore, then the person's running absence number), as returned by absences.list.

create_a_hr_works_absence

Create absences in bulk: body {data: [...]} with 1-100 absences. Each item needs type, beginDate, endDate, status, personnelNumber. Asynchronous: returns a jobId; poll absence_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

update_a_hr_works_absence_by_id

Edit one absence; id is its absence number. Send only what changes (type, dates, half-day flags, status, substitutes, remark); attributes you leave out stay unchanged. Asynchronous: returns a jobId; poll absence_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

delete_a_hr_works_absence_by_id

Delete one absence; id is its absence number (the person's license number, an underscore, then the running absence number). Asynchronous: returns a jobId; poll absence_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_absences_bulk_update

Edit absences in bulk: body {data: [...]} with 1-100 items, each identified by its absence number; attributes you leave out stay unchanged (mandatory ones excepted). Each item needs number. Asynchronous: returns a jobId; poll absence_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_absences_bulk_delete

Delete several absences at once: numbers lists 1-100 absence numbers (license number, underscore, running number). Required: numbers. Asynchronous: returns a jobId; poll absence_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_absences_list_or_error

List absences like list (GET /v2/absences, one page, the same required beginDate/endDate parameters), but return HR WORKS's whole response (the map of person identifier to date intervals) and, when HR WORKS answers an HTTP error (e.g. 429 rate limit, 5xx), a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified timeoff_requests create mapping to read the new absence back after its write job finished, so a failing read-back can still report the job id.

list_all_hr_works_accumulated_absences

Get per-person totals of absence working days by absence type for a date range; filter by persons, types or statusFilter, and split the range with interval (days, weeks or months). Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

list_all_hr_works_accumulated_remote_work

Get per-person totals of remote-work working days for a date range; filter by persons or statusFilter, and split the range with interval (days, weeks or months). Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

list_all_hr_works_accumulated_sick_leaves

Get per-person totals of sick-leave working days by sick leave type for a date range; filter by persons, types or statusFilter, and split the range with interval (days, weeks or months). Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

list_all_hr_works_applicants

List applicants (only applicants that have job applications): name, contact data, address, birthday and more. Filter by applicant uuids (applicants) or by the status of their job applications (statusFilter). Paginated by HR WORKS at 50 per page (limit is ignored); pass next_cursor with the same filters for the next page.

get_single_hr_works_applicant_by_id

Get one applicant; id is the applicant's uuid, which stays the same when the applicant later becomes an employee. Returns: address, birthday, earliestPossibleJoinDate, email, firstName, gender, uuid, hasNoticePeriod and more.

list_all_hr_works_available_working_hours

Get each person's available working hours (working hours, regular working hours, remarks, related events) for a date range, optionally split by interval into days, weeks or months; choose persons with personIdentifierType. Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 150 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_cost_center_assignment_job_by_id

Get the status and result of an asynchronous cost center assignment write. id is the jobId returned by cost_center_assignments.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

create_a_hr_works_cost_center_assignment

Assign cost centers to persons and/or organization units in bulk: body {data: [...]} with 1-2000 assignments; personIdentifierType sets the type of each personIdentifier (uuid by default). Asynchronous: returns a jobId; poll cost_center_assignment_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_cost_center_job_by_id

Get the status and result of an asynchronous cost center write. id is the jobId returned by cost_centers.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

hr_works_cost_center_jobs_get_or_error

Get the status of a cost centers write job, like get (GET /v2/cost-objects/cost-centers/jobs/{jobId}), except that an HTTP error from HR WORKS (e.g. 429 rate limit, 5xx) is returned as a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified groups create/update mappings, which poll the job after HR WORKS accepted the write and must still report the job id when a poll fails.

list_all_hr_works_cost_centers

List all cost centers of the company (number and name). Paginated by HR WORKS at 10000 per page (limit is ignored); pass next_cursor with the same filters for the next page.

create_a_hr_works_cost_center

Create cost centers in bulk: body {data: [...]} with 1-1000 items (number, name); overwriteNames=true renames an existing cost center that has the same number. Asynchronous: returns a jobId; poll cost_center_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_cost_centers_list_or_error

List cost centers like list (GET /v2/cost-objects/cost-centers, first page only, up to 10000), but return HR WORKS's whole response ({"costCenters": [...]}) and, when HR WORKS answers an HTTP error (e.g. 429 rate limit, 5xx), a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified groups create/update mappings to read the cost center back after its write job finished, so a failing read-back can still report the job id.

get_single_hr_works_cost_objective_assignment_job_by_id

Get the status and result of an asynchronous cost objective assignment write. id is the jobId returned by cost_objective_assignments.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

create_a_hr_works_cost_objective_assignment

Assign cost objectives to persons and/or organization units in bulk: body {data: [...]} with 1-2000 assignments; personIdentifierType sets the type of each personIdentifier (uuid by default). Asynchronous: returns a jobId; poll cost_objective_assignment_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_cost_objective_job_by_id

Get the status and result of an asynchronous cost objective write. id is the jobId returned by cost_objectives.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_cost_objectives

List all cost objectives of the company (number and name). Paginated by HR WORKS at 10000 per page (limit is ignored); pass next_cursor with the same filters for the next page.

create_a_hr_works_cost_objective

Create cost objectives in bulk: body {data: [...]} with 1-1000 items (number, name); overwriteNames=true renames an existing cost objective that has the same number. Asynchronous: returns a jobId; poll cost_objective_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

create_a_hr_works_document_pool_file

Upload a file to the HR WORKS document pool. Required: fileName. Give the file as url (HR WORKS downloads it) or send its bytes (up to 15 MB): call the Truto proxy with truto_body_passthrough=true and the file's MIME type as Content-Type (MCP tools can only pass url). Asynchronous: returns a jobId; poll person_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_expense_report_job_by_id

Get the status and result of an asynchronous travel expense report write. id is the jobId returned by expense_reports.create, expense_reports.update. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_expense_reports

List travel expense reports per person in a date range; choose persons with personIdentifierType, filter with statusFilter, and add receipt collections without a trip with includeExpenseReportsWithoutTrip=true. Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 20 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_expense_report_by_id

Get one travel expense report with its days, receipts, advances and totals; id is its number (the person's personnel number, a dash, then the trip number).

create_a_hr_works_expense_report

Create travel expense reports in bulk: body {data: [...]} (HR WORKS documents no maximum); personIdentifierType sets the type of the personIdentifier values (personnel number by default). Each item needs personIdentifier, beginDate, endDate. Asynchronous: returns a jobId; poll expense_report_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

update_a_hr_works_expense_report_by_id

Change the status of one travel expense report (statusIdentifier is the only editable property); id is its number (the person's license number, a hyphen, then the running expense report number). Asynchronous: returns a jobId; poll expense_report_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

list_all_hr_works_health_check

Check that the HR WORKS API is up and accepts this connection's access token; Truto uses it to validate new connections. HR WORKS documents no response body: a successful call means the API is reachable.

get_single_hr_works_holiday_job_by_id

Get the status and result of an asynchronous holiday write. id is the jobId returned by holidays.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_holidays

List the holidays of one year for the company, its countries and permanent establishments; filter with countryCodes (ISO 3166-1 alpha-3) or permanentEstablishments (general holidays of the country stay included). Required: year. Not paginated: the result is ONE object keyed by ISO 3166-1 alpha-3 country code; `` in the response schema is a placeholder, not a field.

create_a_hr_works_holiday

Create company holidays in bulk: body {data: [...]} with 1-1000 holidays (date, name, half-day flag). Scope each one with countryCode, state (together with countryCode) or permanentEstablishmentId; at least one of them is required. Asynchronous: returns a jobId; poll holiday_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

create_a_hr_works_job_application_file

Attach a file to a job application; job_application_id is the application's id. Required: fileName, job_application_id. Give the file as url (HR WORKS downloads it) or send its bytes (up to 15 MB): call the Truto proxy with truto_body_passthrough=true and the file's MIME type as Content-Type (MCP tools can only pass url). Asynchronous: returns a jobId; poll job_application_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_job_application_job_by_id

Get the status and result of an asynchronous job application write. id is the jobId returned by job_applications.create, job_applications.update, job_applications.delete, job_application_files.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_job_applications

List job applications with their applicant, documents, post and status; filter by post ids (posts) and status identifiers (statusFilter). Paginated by HR WORKS at 50 per page (limit is ignored); pass next_cursor with the same filters for the next page.

get_single_hr_works_job_application_by_id

Get one job application (HR WORKS API v3); id is the job application's uuid. Truto returns the data object of the v3 response. Returns: id, statusIdentifier, postUuid, applicant, applicationDocuments, creationDateAndTime, desiredSalary, expectedSalary and more.

create_a_hr_works_job_application

Create job applications in bulk: body {data: [...]} with 1-100 applications, each holding applicant and application details. Each item needs firstName, lastName, postId. Asynchronous: returns a jobId; poll job_application_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

update_a_hr_works_job_application_by_id

Edit one job application; id is its uuid. Only the attributes you send change (post, post offer, desired salary, remark, privacy flags, statusIdentifier). Asynchronous: returns a jobId; poll job_application_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

delete_a_hr_works_job_application_by_id

Delete one job application; id is its uuid, as returned by job_applications.list. Asynchronous: returns a jobId; poll job_application_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

list_all_hr_works_leave_accounts

Get leave-account figures per person (entitlement, requested, approved, planned, expiring and more) for the period from January 1 of referenceDate's year up to referenceDate; filter by persons or add leavers with onlyActive=false. Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_leave_account_by_id

Get one person's leave account (entitlement, requested, approved, planned, expiring and more); id is the person's HR WORKS personnel number. referenceDate sets the end of the period, which starts on January 1 of that year.

create_a_hr_works_onboarding_document_file

Attach a file to an onboarding document; onboarding_document_id is the document id. Required: fileName, onboarding_document_id. Give the file as url (HR WORKS downloads it) or send its bytes (up to 15 MB): call the Truto proxy with truto_body_passthrough=true and the file's MIME type as Content-Type (MCP tools can only pass url). Asynchronous: returns a jobId; poll onboarding_document_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_onboarding_document_job_by_id

Get the status and result of an asynchronous onboarding document write. id is the jobId returned by onboarding_documents.create, onboarding_documents.update, onboarding_document_files.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_onboarding_documents

List the onboarding documents of the company (the data collected from new hires before they become persons); filter by statusFilter or organizationUnits. Not paginated: everything comes back in one response.

get_single_hr_works_onboarding_document_by_id

Get one onboarding document with the new hire's personal, address, bank and tax data; id is its id, as returned by onboarding_documents.list. Returns: address, bankAccount, birthday, birthName, confession, countryOfBirth, childAllowanceCategory, emergencyContactDegreeOfKinship and more.

create_a_hr_works_onboarding_document

Create onboarding documents for new hires in bulk: body {data: [...]} with 1-100 documents. Each item needs privateEmail, firstName, lastName, organizationUnitNumber, joinDate. Asynchronous: returns a jobId; poll onboarding_document_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

update_a_hr_works_onboarding_document_by_id

Edit one onboarding document; id is its id. HR WORKS ignores attributes you leave out, but its schema marks firstName, lastName, privateEmail, organizationUnitNumber and joinDate as required. Asynchronous: returns a jobId; poll onboarding_document_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_organization_unit_job_by_id

Get the status and result of an asynchronous organization unit write. id is the jobId returned by organization_units.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

hr_works_organization_unit_jobs_get_or_error

Get the status of an organization units write job, like get (GET /v2/organization-units/jobs/{jobId}), except that an HTTP error from HR WORKS (e.g. 429 rate limit, 5xx) is returned as a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified groups create mapping, which polls the job after HR WORKS accepted the write and must still report the job id when a poll fails.

list_all_hr_works_organization_unit_present_persons

List the persons of one organization unit who are in the office right now: HR WORKS leaves out vacations, trips, sick leaves and other absences, counted by forenoon and afternoon. Required: organization_unit_number. Not paginated: everything comes back in one response.

list_all_hr_works_organization_units

List all active organization units of the company (number, name, parent unit, SAP codes, country and more). Not paginated: everything comes back in one response.

get_single_hr_works_organization_unit_by_id

Get one organization unit; id is its uuid (passing the unit number instead is deprecated by HR WORKS). Returns: parentOrganizationUnit, number, name, additionalName, sapCodeType, sapUnitCode, countryCode, uuid.

create_a_hr_works_organization_unit

Create organization units in bulk: body {data: [...]} with 1-500 units; parent and child units can be created in the same call. Each item needs number, name, parentOrganizationUnitNumber, countryCode. Asynchronous: returns a jobId; poll organization_unit_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_organization_units_list_or_error

List the active organization units like list (GET /v2/organization-units, one call, no paging), but return HR WORKS's whole response ({"organizationUnits": [...]}) and, when HR WORKS answers an HTTP error (e.g. 429 rate limit, 5xx), a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified groups create mapping to read the new unit back after its write job finished, so a failing read-back can still report the job id.

get_single_hr_works_payroll_file_job_by_id

Get the status and result of an asynchronous payroll file write. id is the jobId returned by payroll_files.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

create_a_hr_works_payroll_file

Upload a payroll file for a person and a year (optionally a month). Choose the person with personIdentifier and personIdentifierType (uuid by default; personnelNumber is deprecated). Required: fileName, year. Give the file as url (HR WORKS downloads it) or send its bytes (up to 15 MB): call the Truto proxy with truto_body_passthrough=true and the file's MIME type as Content-Type (MCP tools can only pass url). Asynchronous: returns a jobId; poll payroll_file_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_permanent_establishment_job_by_id

Get the status and result of an asynchronous permanent establishment write. id is the jobId returned by permanent_establishments.create, permanent_establishments.bulk_update. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_permanent_establishments

List all active permanent establishments (business locations) of the company: id, number, name and address. Not paginated: everything comes back in one response.

get_single_hr_works_permanent_establishment_by_id

Get one permanent establishment (business location): id, number, name and address; id is its id, as returned by permanent_establishments.list. Returns: id, name, address, number.

create_a_hr_works_permanent_establishment

Create permanent establishments (business locations) in bulk: body {data: [...]} with 1-1000 items (id, number, name, address). Asynchronous: returns a jobId; poll permanent_establishment_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_permanent_establishments_bulk_update

Edit permanent establishments in bulk: body {data: [...]} with 1-1000 items (id, number, name, address). Asynchronous: returns a jobId; poll permanent_establishment_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

create_a_hr_works_person_document

Upload a file to one person's document pool; person_identifier selects the person (personIdentifierType sets its type, personId by default). Required: fileName, person_identifier. Give the file as url (HR WORKS downloads it) or send its bytes (up to 15 MB): call the Truto proxy with truto_body_passthrough=true and the file's MIME type as Content-Type (MCP tools can only pass url). Asynchronous: returns a jobId; poll person_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_person_job_by_id

Get the status and result of an asynchronous person write. id is the jobId returned by persons.create, persons.bulk_update, document_pool_files.create, person_documents.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

hr_works_person_jobs_get_or_error

Get the status of a persons write job, like get (GET /v2/persons/jobs/{jobId}), except that an HTTP error from HR WORKS (e.g. 429 rate limit, 5xx) is returned as a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified employees create/update mappings, which poll the job after HR WORKS accepted the write and must still report the job id when a poll fails.

list_all_hr_works_person_master_data

Get full master data (contact, address, bank account, employment, organization and more) for many persons; choose persons with persons and personIdentifierType, or add leavers with onlyActive=false. Paginated by HR WORKS at 50 per page (limit is ignored); pass next_cursor with the same filters for the next page.

get_single_hr_works_person_master_datum_by_id

Get one person's full master data; id is the person identifier, a personnel number unless personIdentifierType says otherwise. Custom master-data fields defined in HR WORKS are returned as extra properties.

list_all_hr_works_person_wage_payments

List one person's wage payments (HR WORKS API v3): month, gross amount, wage type and more; person_uuid is the person's uuid. Required: person_uuid. Paginated (page size not documented; limit is ignored); pass next_cursor with the same filters for the next page. HR WORKS documents page links only for v2, so paging may stop after page 1.

get_single_hr_works_person_working_time_status_by_id

Get whether a person is clocked in right now and, if so, the current working time; id is the person identifier (a personnel number by default). Returns: clockedIn, workingTime.

create_a_hr_works_person_working_time

Clock a person in or out now; person_identifier is a personnel number by default. action is clockIn or clockOut, and a clockIn needs type. beginDateAndTime or endDateAndTime (ISO 8601 UTC) may backdate it by up to 24 hours. Required: action, person_identifier. Synchronous: returns the working time and any warnings directly.

list_all_hr_works_personnel_file_categories

List the personnel file categories of the company (key and name). Use a key as the category filter of personnel_file_entries.list or as the category of a new entry. Not paginated: everything comes back in one response.

list_all_hr_works_personnel_file_entries

List one person's personnel file entries (category, creation date, documents, name, notes and more); person_identifier selects the person (a personnel number unless personIdentifierType says otherwise). Required: person_identifier. Not paginated: everything comes back in one response.

get_single_hr_works_personnel_file_entry_by_id

Get one personnel file entry; id is the entry id and person_identifier the person (a personnel number unless personIdentifierType says otherwise). Required: person_identifier.

create_a_hr_works_personnel_file_entry

Create personnel file entries for many persons: body {data: [...]} with 1-1000 entries; personIdentifierType sets the type of the personIdentifier values in the items. Each item needs name, creationDate, category, personnelNumber. Asynchronous: returns a jobId; poll personnel_file_entry_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

create_a_hr_works_personnel_file_entry_file

Attach a file to one of a person's personnel file entries (personIdentifierType sets the type of person_identifier, personnel number by default). Required: fileName, person_identifier, personnel_file_entry_id. Give the file as url (HR WORKS downloads it) or send its bytes (up to 15 MB): call the Truto proxy with truto_body_passthrough=true and the file's MIME type as Content-Type (MCP tools can only pass url). Asynchronous: returns a jobId; poll personnel_file_entry_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_personnel_file_entry_job_by_id

Get the status and result of an asynchronous personnel file entry write. id is the jobId returned by personnel_file_entries.create, personnel_file_entry_files.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_persons

List persons (base data: names and identifiers such as personId, personnelNumber and uuid) grouped by organization unit; filter with organizationUnits. Only active persons unless onlyActive=false. Not paginated: the result is ONE object keyed by organization-unit number, or uuid when identifierType=uuid; `` in the response schema is a placeholder, not a field.

get_single_hr_works_person_by_id

Get one person's base data (names and identifiers); id is the person identifier, a personnel number unless personIdentifierType says otherwise (uuid, personId, personLicenseNumber, personIdentifierForKiosk).

create_a_hr_works_person

Create employees in bulk: body {data: [...]} with 1-100 persons. HR WORKS notes that creating employees causes license costs. Each item needs personId, personnelNumber. Asynchronous: returns a jobId; poll person_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_persons_bulk_update

Edit employees in bulk: body {data: [...]} with 1-100 persons; attributes you leave out stay unchanged (mandatory ones excepted). Each item needs personId, personnelNumber. Asynchronous: returns a jobId; poll person_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_persons_list_or_error

List the company's persons, like list (GET /v2/persons), except that an HTTP error from HR WORKS (e.g. a 403 because the key pair has no permission for /persons, a 429 rate limit or a 5xx) is returned as a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified timeoff_requests, timeoff_balances and timesheet_entries mappings, which resolve the person of every record through this endpoint and must still return the records when it is not readable.

hr_works_persons_get_or_error

Get a single person, like get (GET /v2/persons/{personIdentifier}), except that an HTTP error from HR WORKS (e.g. a 403 because the key pair has no permission for /persons, a 429 rate limit or a 5xx) is returned as a 200 result {"truto_error": {"status": }} instead of failing. Used by the unified timeoff_requests.get mapping, which resolves the absence's employee from the licence number in the absence number and must still return the absence when that look-up is not permitted.

list_all_hr_works_persons_today

Get today's day data per person: target working time and holidays always, plus the sources listed in includeData (such as working times, absences, sick leaves and travel requests). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

list_all_hr_works_posts

List the job posts of the company (display name, name, key, uuid, organization unit and more); onlyActive=false also returns inactive posts. Not paginated: everything comes back in one response.

get_single_hr_works_post_by_id

Get one job post; id is the post's uuid (passing the older post id is deprecated by HR WORKS). Returns: displayName, name, key, uuid, organizationUnitNumber, organizationUnitUuid, contactInformation, scopeOfActivities and more.

get_single_hr_works_project_customer_job_by_id

Get the status and result of an asynchronous project customer write. id is the jobId returned by project_customers.create, project_customers.bulk_update. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_project_customers

List the time-tracking project customers (id, number, name, active flag, project counts); onlyActive=false also returns inactive customers. Not paginated: everything comes back in one response.

get_single_hr_works_project_customer_by_id

Get one time-tracking project customer (id, number, name, active flag, project counts); id is the customer number (the number field of project_customers.list, not its id field).

create_a_hr_works_project_customer

Create project customers in bulk: body {data: [...]} with 1-1000 customers (name, number). Asynchronous: returns a jobId; poll project_customer_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_project_customers_bulk_update

Edit project customers in bulk: body {data: [...]} with 1-1000 items (id, number, name, isActive). Asynchronous: returns a jobId; poll project_customer_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

list_all_hr_works_receipt_types

List the travel-expense receipt types per country (name, category, key, deduction, flags and accounts); filter with countryCodes (ISO 3166-1 alpha-3), categories or onlyActive=false. Not paginated: Truto returns the receiptTypes object, keyed by 3-letter country code (`` in the response schema is a placeholder) with the receipt types of each country.

list_all_hr_works_remote_work

List remote-work entries per person in a date range (dates, half-days, number, status, working days); filter by persons or statusFilter, and split the range with interval (days, weeks or months). Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_remote_work_by_id

Get one remote-work entry; id is its number (the person's license number, an underscore, then the running remote-work number), as returned by remote_work.list.

create_a_hr_works_remote_work

Create remote-work entries in bulk: body {data: [...]} with 1-100 entries. Each item needs beginDate, endDate, personnelNumber, statusIdentifier. Asynchronous: returns a jobId; poll remote_work_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

update_a_hr_works_remote_work_by_id

Edit one remote-work entry; id is its number. Send only what changes (beginDate, endDate, half-day flags, statusIdentifier). Asynchronous: returns a jobId; poll remote_work_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

delete_a_hr_works_remote_work_by_id

Delete one remote-work entry; id is its number (the person's license number, an underscore, then the running remote-work number). Asynchronous: returns a jobId; poll remote_work_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_remote_work_bulk_update

Edit remote-work entries in bulk: body {data: [...]} with 1-100 items, each identified by its remote-work number; attributes you leave out stay unchanged (mandatory ones excepted). Each item needs number. Asynchronous: returns a jobId; poll remote_work_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_remote_work_job_by_id

Get the status and result of an asynchronous remote work write. id is the jobId returned by remote_work.create, remote_work.bulk_update, remote_work.update, remote_work.delete. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

get_single_hr_works_sick_leave_job_by_id

Get the status and result of an asynchronous sick leave write. id is the jobId returned by sick_leaves.create, sick_leaves.bulk_update, sick_leaves.bulk_delete, sick_leaves.delete. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

get_single_hr_works_sick_leave_type_job_by_id

Get the status and result of an asynchronous sick leave type write. id is the jobId returned by sick_leave_types.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_sick_leave_types

List the sick leave types configured in HR WORKS (key, name, color and visibility flags). Use the keys as the types filter of sick_leaves.list; onlyActive=false also returns deactivated types. Not paginated: everything comes back in one response.

create_a_hr_works_sick_leave_type

Create sick leave types in bulk: body {data: [...]} with 1-1000 types (name, key and flags such as isSicknessOfChild or useInMonthPayroll). Asynchronous: returns a jobId; poll sick_leave_type_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

list_all_hr_works_sick_leaves

List sick leaves per person in a date range (type, dates, half-days, status, working days); filter by persons, types or statusFilter. interval splits the range (days, weeks, months); count=true returns per-type day totals instead. Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_sick_leaf_by_id

Get one sick leave; id is its number (the person's license number, an underscore, then the running sick leave number), as returned by sick_leaves.list.

create_a_hr_works_sick_leaf

Create sick leaves in bulk: body {data: [...]} with 1-100 sick leaves. Each item needs type, beginDate, endDate, status, personnelNumber. Asynchronous: returns a jobId; poll sick_leave_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

delete_a_hr_works_sick_leaf_by_id

Delete one sick leave; id is its number (the person's license number, an underscore, then the running sick leave number). Asynchronous: returns a jobId; poll sick_leave_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_sick_leaves_bulk_update

Edit sick leaves in bulk: body {data: [...]} with 1-100 items, each identified by its sick leave number; attributes you leave out stay unchanged (mandatory ones excepted). Each item needs number. Asynchronous: returns a jobId; poll sick_leave_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_sick_leaves_bulk_delete

Delete several sick leaves at once: numbers lists 1-100 sick leave numbers (license number, underscore, running number). Required: numbers. Asynchronous: returns a jobId; poll sick_leave_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

list_all_hr_works_time_accounts

Get monthly time accounts per person (working hours, target hours, time credit, carry-over and totals) for the months in monthYears (YYYY-MM, the current month by default); filter by persons. Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

list_all_hr_works_time_recording_regulations

List the time recording regulations of the company (title, key, online and kiosk use, working time types, kiosks and more); their working time types are the types filter of working_times.list. Not paginated: everything comes back in one response.

get_single_hr_works_time_tracking_project_assignment_job_by_id

Get the status and result of an asynchronous project team assignment write. id is the jobId returned by time_tracking_project_assignments.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

create_a_hr_works_time_tracking_project_assignment

Add persons to project teams in bulk: body {data: [...]} with 1-500 assignments. Each item needs personIdentifier, projectNumber. Asynchronous: returns a jobId; poll time_tracking_project_assignment_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_time_tracking_project_job_by_id

Get the status and result of an asynchronous time-tracking project write. id is the jobId returned by time_tracking_projects.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_time_tracking_projects

List time-tracking projects, optionally within a beginDate/endDate range; filter by numbers, projectIds, statusFilter, projectType, projectManagerPersonnelNumber or projectCustomerId. Paginated by HR WORKS at 50 per page (limit is ignored); pass next_cursor with the same filters for the next page.

get_single_hr_works_time_tracking_project_by_id

Get one time-tracking project (number, name, dates, status, budget, manager, customer and more); id is the project's id field (not its number). Returns: id, number, name, beginDate, endDate, description, parentProject, statusIdentifier and more.

create_a_hr_works_time_tracking_project

Create time-tracking projects in bulk: body {data: [...]} with 1-500 projects. Each item needs id, name, beginDate, status, projectManagerPersonnelNumber, customerId. Asynchronous: returns a jobId; poll time_tracking_project_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

list_all_hr_works_travel_requests

List travel requests per person in a date range; choose persons with personIdentifierType and filter with statusFilter. Required: beginDate and endDate (at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_travel_request_by_id

Get one travel request (destination, dates, cost center and objective, person and more); id is its number (the person's personnel number, a dash, then the trip request number).

list_all_hr_works_vacation_types

List the vacation types of the company per country (key, name, country, active, assigned and default flags). Filter with countryCodes (ISO 3166-1 alpha-3); onlyAssigned=false and onlyActive=false widen the result. Not paginated: everything comes back in one response.

list_all_hr_works_wage_and_salary_types

List the wage types of the company (number, name, type, export key, country, active flag); onlyActive decides whether only active wage types are returned. Not paginated: everything comes back in one response.

get_single_hr_works_wage_payment_job_by_id

Get the status and result of an asynchronous wage payment write. id is the jobId returned by wage_payments.create. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_wage_payments

List all wage payments (HR WORKS API v3): person uuid, month, gross amount, wage type and more; filter by wageTypeNumber. Paginated (page size not documented; limit is ignored); pass next_cursor with the same filters for the next page. HR WORKS documents page links only for v2, so paging may stop after page 1.

get_single_hr_works_wage_payment_by_id

Get one wage payment (HR WORKS API v3); id is the wage payment's uuid. Truto returns the data object of the v3 response. Returns: personUuid, monthYear, grossAmount, wageTypeNumber, remark, endMonthYear, interval.

create_a_hr_works_wage_payment

Create wage payments in bulk: body {data: [...]} (HR WORKS documents no maximum). Each item needs personUuid, monthYear, grossAmount, wageTypeNumber. Asynchronous: returns a jobId; poll wage_payment_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_webhook_job_by_id

Get the status and result of an asynchronous webhook write. id is the jobId returned by webhooks.create, webhooks.bulk_update, webhooks.bulk_delete. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_webhooks

List the server-event webhooks of the company (id, name, resource, action, url, active flag); filter by resource (person, applicant, absence, sickLeave, remoteWork), action, or add inactive ones with onlyActive=false. Not paginated: everything comes back in one response.

create_a_hr_works_webhook

Create server-event webhooks in bulk: body {data: [...]} with 1-500 webhooks (HTTPS url, resource, action). Each item needs action, name, resource, url. Asynchronous: returns a jobId; poll webhook_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_webhooks_bulk_update

Edit webhooks in bulk: body {data: [...]} with 1-500 items, each identified by its webhook id. Each item needs action, name, resource, url, id. Asynchronous: returns a jobId; poll webhook_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_webhooks_bulk_delete

Delete webhooks by id: numbers lists 1-500 webhook ids, as returned by webhooks.list. Required: numbers. Asynchronous: returns a jobId; poll webhook_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

get_single_hr_works_working_time_job_by_id

Get the status and result of an asynchronous working time write. id is the jobId returned by working_times.create, working_times.bulk_update, working_times.bulk_delete. The job is pending until HR WORKS has processed the write, then finished with the written records or the errors (write errors are reported here inside an HTTP 200). Kept for two weeks.

list_all_hr_works_working_time_kiosks

List the time-tracking kiosks (terminals) of the company: name, id and whether they use QR codes or PINs. Not paginated: everything comes back in one response.

list_all_hr_works_working_times

List working times per person in a date range, with worked, target and break minutes per interval; filter by persons or types. Only persons with a time recording regulation in the range are returned. Required: beginDate and endDate (YYYY-MM-DD, at most one year apart; at most 31 days with interval=days). Each page is ONE object keyed by person identifier (`` in the response schema is a placeholder, not a field) holding up to 50 persons; HR WORKS fixes the page size, so limit is ignored. Pass next_cursor with the same filters for the next page.

get_single_hr_works_working_time_by_id

Get one working time (HR WORKS API v3); id is the working time's uuid. Truto returns the data object of the v3 response. Returns: beginDateAndTime, endDateAndTime, beginDateTime, endDateTime, clockInKioskId, clockOutKioskId, comment, workingTimeType and more.

create_a_hr_works_working_time

Create working times in bulk: body {data: [...]} with 1-1000 entries; deleteOverlappingWorkingTimes=true deletes existing working times that the new ones overlap. Each item needs beginDateAndTime. Asynchronous: returns a jobId; poll working_time_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_working_times_bulk_update

Edit existing working times in bulk: body {data: [...]} with 1-1000 entries; deleteOverlappingWorkingTimes=true deletes other working times that the edited ones overlap. Each item needs beginDateAndTime. Asynchronous: returns a jobId; poll working_time_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

hr_works_working_times_bulk_delete

Delete working times: numbers lists 1-1000 working time numbers (personnel number, an underscore, then the begin time in Unix seconds, e.g. 1_1683093600). Required: numbers. Asynchronous: returns a jobId; poll working_time_jobs.get with it until the job is finished (write errors are reported there inside an HTTP 200).

Why Truto

Why use Truto’s MCP server for HR WORKS

Other MCP servers give you a static tool list for one app. Truto gives you a managed, multi-tenant MCP infrastructure across 800+ integrations.

01

Auto-generated, always up to date

Tools are dynamically generated from curated documentation — not hand-coded. As integrations evolve, tools stay current without manual maintenance.

02

Fine-grained access control

Scope each MCP server to read-only, write-only, specific methods, or tagged tool groups. Expose only what your AI agent needs — nothing more.

03

Multi-tenant by design

Each MCP server is scoped to a single connected account with its own credentials. The URL itself is the auth token — no shared secrets, no credential leaking across tenants.

04

Works with every MCP client

Standard JSON-RPC 2.0 protocol. Paste the URL into Claude, ChatGPT, Cursor, or any MCP-compatible agent framework — tools are discovered automatically.

05

Built-in auth, rate limits, and error handling

Tool calls execute through Truto’s proxy layer with automatic OAuth refresh, rate-limit handling, and normalized error responses. No raw API plumbing in your agent.

06

Expiring and auditable servers

Create time-limited MCP servers for contractors or automated workflows. Optional dual-auth requires both the URL and a Truto API token for high-security environments.

Unified APIs

Unified APIs for HR WORKS

Skip writing code for every integration. Use Truto’s category-specific Unified APIs out of the box or customize the mappings with AI.

Unified HRIS API

Bank Info

Bank info represent the Bank Account information for an Employee

View Docs

Employee Compensations

Represent the compensation configuration for an Employee

View Docs

Employees

Represents an employee in HRIS

View Docs

Employments

Employments represent a job position at a company.

View Docs

Groups

Groups represent the groups for an Employee

View Docs

Locations

Locations represent the locations in HRIS

View Docs

Timeoff Balances

Represent the time off balances for an Employee

View Docs

Timeoff Policies

Represent the time off policies in a company

View Docs

Timeoff Requests

Represent the time off requests for an Employee

View Docs

Timeoff Types

Represent the time off types in a company

View Docs

Timesheet Entries

Represents a block of time an employee worked or reported, such as a shift, a punch or a timesheet line, attributed to a single work date

View Docs

How It Works

From zero to integrated

Go live with HR WORKS in under an hour. No boilerplate, no maintenance burden.

01

Link your customer’s HR WORKS account

Use Truto’s frontend SDK to connect your customer’s HR WORKS account. We handle all OAuth and API key flows — you don’t need to create the OAuth app.

02

We handle authentication

Don’t spend time refreshing access tokens or figuring out secure storage. We handle it and inject credentials into every API request.

03

Call our API, we call HR WORKS

Truto’s Proxy API is a 1-to-1 mapping of the HR WORKS API. You call us, we call HR WORKS, and pass the response back in the same cycle.

04

Unified response format

Every response follows a single format across all integrations. We translate HR WORKS’s pagination into unified cursor-based pagination. Data is always in the result attribute.

FAQs

Common questions about HR WORKS on Truto

Authentication, rate limits, data freshness, and everything else you need to know before you integrate.

How does authentication work for HR WORKS?

End users connect their HR WORKS account through Truto's hosted connect flow. Truto handles credential storage, token refresh, and any API-key or OAuth handshake so you never touch raw secrets.

Why do some writes not appear immediately after a create or update call?

HR WORKS processes most mutations asynchronously — the API returns a job ID that must be polled until it resolves. Truto exposes the underlying job endpoints (e.g. person jobs, absence jobs, working time jobs) so you can confirm final status before updating your UI.

Do you support real-time events or only polling?

Both. You can use HR WORKS native webhooks for events like person, applicant, absence, and sick leave changes, or fall back to scheduled list calls. Truto normalizes delivery so your handlers look the same across tenants.

Can I use a unified HRIS schema instead of HR WORKS–specific fields?

Yes. Truto's Unified HRIS API covers Employees, Employments, Employee Compensations, Bank Info, Groups, Locations, Timeoff Balances, Timeoff Policies, Timeoff Requests, Timeoff Types, and Timesheet Entries. You can also drop down to the raw HR WORKS tools when you need DACH-specific fields like tax class or cost objective.

How are vacation, sick leave, and remote work represented?

HR WORKS keeps them as separate resources — absences, sick leaves, and remote work — each with its own list, create, update, delete, and bulk operations. The unified Timeoff endpoints consolidate them into a common model when you want a simpler surface.

Can I upload files like contracts or receipts through the integration?

Yes. You can create personnel file entry files, onboarding document files, document pool files, payroll files, person documents, and job application files directly through the HR WORKS tools, keeping employee paperwork in sync with your product.

HR WORKS

Get HR WORKS integrated into your app

Our team understands what it takes to make a HR WORKS integration successful. A short, crisp 30 minute call with folks who understand the problem.