Data sync resources
Every data sync resource, its scope, opt-in fields, files and response fields
This page lists the 33 resources of the data sync API. Every resource has a changed-since feed (/api/v1/sync/{resource}) and id reconciliation (/api/v1/sync/{resource}/ids). Read the guide first for how passes, deletions, include, files and the payroll rule work.
How to read this page
Each resource lists:
- Scope: which rows a key receives. "Own company" means the row's company is one of the key's companies. "Through the employee" means the row's employee belongs to one of the key's companies. "Group" means every key of the group receives every row.
include: the opt-in names the resource accepts and the fields each adds. A name it does not accept gets400.- Content: whether
/api/v1/sync/{resource}/{id}/contentserves a file. - Run listing: whether
?payroll_run_id=<id>lists every row of one payroll run. - Page size: the largest
limit. It is 1,000 (default 500) unless stated.
Field types:
| Type | Format |
|---|---|
string | Text. Ids are opaque strings |
date | YYYY-MM-DD |
instant | ISO 8601 UTC, such as 2026-11-11T01:58:12.000Z |
time | HH:mm wall-clock time |
money | String with exactly 2 decimals, such as "15000.00" |
decimal(n) | String with n decimals, such as "0.9677" for decimal(4) |
integer, number, boolean | JSON values |
object, array | JSON values, described per field |
Treat every field except id as nullable. An id that points at a row in a company outside the key's companies is null. Enum values are returned as stored; the values listed are the ones Hopshift writes today.
| Area | Resources |
|---|---|
| People | employees, employee-events, salary-adjustments, transfers, employee-locations |
| Structure | companies, locations, departments, positions, level-tiers |
| Attendance | clock-records, clock-record-history, shifts, overtime-requests, attendance-corrections |
| Leave | leave-requests, leave-request-history, leave-types, leave-allocations |
| Payroll | payroll-runs, payroll-items, payroll-documents, payslip-pdfs, pay-items, pay-item-definitions, employee-pay-items, employee-pay-schedules, meal-allowance-policies, meal-allowance-enrollments, intercompany-assignments, intercompany-recharges, tax-declarations |
| History | audit-events |
People
employees
One row per employee record, every status. Moving a person to another company creates a second record with a new id (see people with several employee records).
- Scope: own company.
include:notesaddstermination_reason(string),would_rehire(boolean, HR's judgement),scheduling_notes(string) andexclude_from_payroll_reason(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | Employee record id |
employee_code | string | Staff code assigned by HR |
user_id | string | The Hopshift login linked to this record. Join *_by_user_id fields to it |
company_id | string | Employing company |
first_name | string | Latin script |
last_name | string | Latin script |
first_name_th | string | Thai script |
last_name_th | string | Thai script |
title_th | string | Thai name title |
nickname | string | |
email | string | |
gender | string | As stored |
birthdate | date | |
nationality | string | |
national_id_number | string | |
tax_id | string | |
sso_number | string | Social security number |
bank_name | string | |
bank_account_no | string | |
address | string | Home address |
city | string | |
state | string | |
zip_code | string | |
phone | string | |
home_phone | string | |
emergency_contact_name | string | |
emergency_contact_number | string | |
emergency_contact_relation | string | |
employee_type | string | full_time, part_time, contract, intern |
status | string | ACTIVE, INACTIVE, TERMINATED, ON_LEAVE |
hire_date | date | Day in the company's timezone, like the other dates of this resource |
probation_end_date | date | |
contract_end_date | date | |
termination_date | date | |
department_id | string | Sent only when the department belongs to the employee's company |
position_id | string | Sent only when the position belongs to the employee's company |
manager_id | string | Reporting line: the manager's employee record |
pay_type | string | HOURLY, DAILY, SALARY, COMMISSION, MIXED |
hours_source | string | Where paid hours come from: TIMESHEET, MANUAL, EXEMPT, HYBRID |
base_salary | money | |
hourly_rate | money | |
daily_rate | money | |
wage_effective_date | date | When the current rate took effect |
exclude_from_payroll | boolean | Left out of payroll runs |
transferred_from_employee_id | string | The record this one was transferred from |
created_at | instant | |
updated_at | instant |
employee-events
Employment history: one row per event, such as a salary change, promotion, transfer or termination.
- Scope: own company (the event's company).
include:notesaddsreason(string) and, inside the values of status events,termination_reasonandwould_rehire.- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
event_type | string | HIRED, SALARY_CHANGE, POSITION_CHANGE, DEPARTMENT_CHANGE, LOCATION_CHANGE, PROMOTION, DEMOTION, TRANSFER_OUT, TRANSFER_IN, TRANSFER_REVERSED, PROBATION_PASSED, CONTRACT_RENEWED, STATUS_CHANGE, TERMINATED, RESIGNED, REHIRED |
event_date | date | Day in the company's timezone |
previous_value | object | The values before the event, filtered as below |
new_value | object | The values after the event, filtered as below |
description | string | The system's summary line |
initiated_by_user_id | string | |
approved_by_user_id | string | |
transfer_id | string | The transfer behind a transfer event |
created_at | instant |
previous_value and new_value keep only these keys, renamed to snake_case. They are null when none of the keys is present, and always null for other event types. Values are passed through as recorded, so their JSON types can differ between rows. Employee and company ids inside them are null when outside the key's companies.
| Event type | Keys |
|---|---|
SALARY_CHANGE | pay_type, base_salary, hourly_rate, daily_rate |
POSITION_CHANGE, PROMOTION, DEMOTION | position_id, position_name, title, department, level |
DEPARTMENT_CHANGE | department_id, department_name |
STATUS_CHANGE, TERMINATED, RESIGNED | status, termination_date, scheduled, cancelled; with notes also termination_reason, would_rehire |
TRANSFER_OUT | company_id, target_company_id, target_employee_id |
REHIRED | status, rehired_employee_id, terminated_employee_id |
salary-adjustments
Salary changes with their amounts.
- Scope: through the employee.
include:notesaddsreason(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
previous_amount | money | |
new_amount | money | |
adjustment_type | string | ANNUAL_REVIEW, PROMOTION, MARKET_ADJUSTMENT, CORRECTION, OTHER |
effective_date | date | Day in the employee's company's timezone |
created_at | instant | |
updated_at | instant |
transfers
Moves of an employee from one company to another. A completed transfer links the source employee record to the new record in the target company.
- Scope: either company: the key receives the transfer when the source or the target company is one of its companies.
include:notesaddsreason(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | The source employee record. Null when outside the key's companies |
target_employee_id | string | The new employee record in the target company. Null when outside the key's companies |
source_company_id | string | Always sent |
target_company_id | string | Always sent |
transfer_date | date | Day in the source company's timezone |
transfer_type | string | PERMANENT, SECONDMENT, RETURN |
status | string | DRAFT, PENDING_APPROVAL, APPROVED, COMPLETED, CANCELLED |
retain_history | boolean | |
carry_leave_balance | boolean | |
carry_seniority_date | boolean | |
initiated_by_user_id | string | |
approved_by_user_id | string | |
cancelled_at | instant | |
cancelled_by_user_id | string | |
created_at | instant | |
updated_at | instant |
employee-locations
Outlet assignments: the outlets an employee is assigned to, with dates.
- Scope: through the employee.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
location_id | string | Null when the punch has no outlet recorded, or when the outlet belongs to a company outside the key's companies |
start_date | date | Day in the employee's company's timezone |
end_date | date | Null while the assignment is open |
Structure
companies
The companies of the group the key covers. Reporting lines are employees.manager_id; there is no separate resource for them.
- Scope: the key's companies.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
name | string | |
name_th | string | |
slug | string | URL name in Hopshift |
country | string | Country code, such as TH |
address | string | |
address_th | string | |
pnd1_tax_id | string | The employer's tax id on PND1 filings |
sso_registration_number | string | The employer's SSO registration number |
pay_frequency | string | WEEKLY, BIWEEKLY, SEMI_MONTHLY, MONTHLY |
pay_period_start_day | integer | Day of the month the pay period starts |
pay_period_end_day | integer | Day of the month the pay period ends |
created_at | instant | |
updated_at | instant |
locations
Outlets.
- Scope: own company.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
company_id | string | |
name | string | |
slug | string | |
address | string | |
timezone | string | IANA timezone, such as Asia/Bangkok |
latitude | number | Outlet position |
longitude | number | Outlet position |
created_at | instant | |
updated_at | instant |
departments
- Scope: own company.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
company_id | string | |
name | string | |
order | integer | Display order |
created_at | instant | |
updated_at | instant |
positions
Job positions, such as Barista or Sous Chef.
- Scope: own company.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
company_id | string | |
name | string | |
name_th | string | |
name_zh | string | |
level_tier_id | string | The position's step on the group's level ladder (level-tiers) |
is_active | boolean | |
created_at | instant | |
updated_at | instant |
level-tiers
The group's level ladder that positions belong to.
- Scope: group.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
level | integer | Step number |
label | string | |
label_th | string | |
sort_order | integer | |
created_at | instant | |
updated_at | instant |
Attendance
clock-records
Punches: clock-ins, clock-outs and breaks.
- Scope: own company (the company recorded on the punch). A punch at a host outlet can name an employee, outlet or roster of another company; those ids are null when that company is outside the key's companies.
include:notesaddsnotes(string),manager_notes(string) andapproval_reason(string, the wording behind the approval outcome, such as a reviewer's rejection reason).gpsaddsgps_location(string, the raw position as"lat,lng"),gps_accuracy(number, as reported by the device) andip_address(string).photosaddshas_photo(boolean).- All opt-in fields are null when the punch's employee belongs to a company outside the key's companies.
- Content: the punch's face photo when
has_photois true.404when the punch or its employee is outside the key's companies, or there is no photo. - Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
location_id | string | Outlet |
shift_id | string | The roster the punch belongs to |
type | string | For example clock_in, clock_out, break_start, break_end |
record_time | instant | The punch time |
source | string | How the punch was recorded, as stored |
approval_status | string | For example pending, approved, rejected, correction_pending |
approval_reason_code | string | Machine code of the rule that set the approval reason, such as LONG_SESSION. Null for ordinary reasons |
reviewed_by_user_id | string | |
reviewed_at | instant | |
edited_by_user_id | string | |
edited_at | instant | |
original_time | instant | The punch time before an edit |
is_out_of_schedule | boolean | Punched outside a roster |
is_location_unverified | boolean | The position could not be verified at the outlet |
created_at | instant | |
updated_at | instant |
clock-record-history
Status history of punches: one row per review step.
- Scope: through the punch's company.
include:notesaddsnotes(string, the reviewer's text), null when the punch's employee belongs to a company outside the key's companies.- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
clock_record_id | string | |
previous_status | string | Approval status before the step |
new_status | string | Approval status after the step |
changed_by_user_id | string | |
created_at | instant |
shifts
Rosters, including deleted ones: a deleted shift stays in the feed and in /ids with deleted_at set.
- Scope: own company.
include:notesaddsnotes(string) anddeletion_reason(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
location_id | string | Outlet |
date | date | |
start_local | time | Start, wall-clock time in timezone |
end_local | time | End, wall-clock time in timezone |
timezone | string | The outlet's timezone, else the company's |
break_minutes | integer | |
status | string | DRAFT, SCHEDULED, IN_PROGRESS, COMPLETED, CANCELLED |
position_label | string | Free-text position shown on the roster |
worked_half | boolean | On a half-day leave date: true when the times are only the hours worked, false when it is a full shift the leave splits, null when not answered |
recurrence_id | string | The repeating-shift rule this roster came from |
deleted_at | instant | Set when the shift was deleted |
deleted_by_user_id | string |
Shifts have no created_at or updated_at.
overtime-requests
- Scope: own company.
include:notesaddsreason(string, the employee's) andmanager_notes(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
request_date | date | |
start_time | string | Window start as stored, HH:mm |
end_time | string | Window end as stored, HH:mm |
start_day_offset | integer | Days after request_date that the window starts (0 or 1, for overnight overtime); null on older requests |
requested_hours | decimal(2) | |
actual_hours | decimal(2) | |
ot_type | string | regular, restDay, publicHoliday |
overrides_roster | boolean | The reviewer allowed attended time overlapping the roster to be paid as overtime |
is_from_clock | boolean | Raised from a punch |
clock_record_id | string | The punch it was raised from |
status | string | pending, approved, rejected, cancelled |
reviewed_by_user_id | string | |
reviewed_at | instant | |
created_at | instant | |
updated_at | instant |
attendance-corrections
Requests to correct punches, such as a missed clock-out.
- Scope: own company (the employee's payroll company). The punch, roster and outlet it names can belong to a host company; those ids are null when that company is outside the key's companies.
include:notesaddsreason(string, the employee's) andreview_note(string, the reviewer's).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
clock_record_id | string | The punch being corrected |
shift_id | string | |
location_id | string | |
correction_type | string | LATE_CHECKIN, MISSED_CHECKIN, MISSED_CHECKOUT, WRONG_TIME, ACCIDENTAL, MISSED_SHIFT, UNROSTERED_WORK |
requested_time | instant | |
requested_end_time | instant | |
original_time | instant | |
effective_time | instant | The time applied on approval |
effective_end_time | instant | |
status | string | PENDING, APPROVED, DENIED, CANCELLED |
reviewed_by_user_id | string | |
reviewed_at | instant | |
system_raised | boolean | Raised by Hopshift (a forgotten clock-out) rather than by a person |
system_reason | string | Why Hopshift raised it |
created_at | instant | |
updated_at | instant |
Leave
leave-requests
Medical certificates and other attachments are never served.
- Scope: own company.
include:notesaddsreason(string, the employee's) andmanager_notes(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
leave_type_id | string | |
start_date | date | |
end_date | date | |
day_count | decimal(1) | |
is_half_day | boolean | |
half_day_portion | string | FIRST or SECOND half of the rostered shift |
half_day_period | string | Legacy AM or PM; older rows carry only this |
status | string | pending, approved, rejected (denied on older rows), cancelled, cancellation_pending (still approved leave until the cancellation is accepted) |
reviewed_by_user_id | string | |
reviewed_at | instant | |
first_approved_by_user_id | string | The approver |
first_approved_at | instant | |
approved_via_admin_override | boolean | |
is_backfilled | boolean | Entered by an admin after the fact |
is_retroactive | boolean | |
cancellation_requested_at | instant | |
cancellation_approved_at | instant | |
supersedes_id | string | An edited request replaces the request named here |
created_at | instant | |
updated_at | instant |
leave-request-history
Status history of leave requests.
- Scope: through the request's company.
include:notesaddsnotes(string, the reviewer's text).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
leave_request_id | string | |
previous_status | string | |
new_status | string | |
changed_by_user_id | string | |
created_at | instant |
leave-types
The group's leave types and their rules. Company limits on a leave type are not included.
- Scope: group.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
code | string | Such as ANNUAL or SICK |
name | string | |
name_th | string | |
name_zh | string | |
reset_type | string | ANNUAL_CALENDAR, ANNUAL_ANNIVERSARY, ONE_TIME, ACCUMULATIVE |
reset_month | integer | Month of the yearly reset, for ANNUAL_CALENDAR |
reset_day | integer | Day of the yearly reset, for ANNUAL_CALENDAR |
counting_method | string | WORKING_DAYS, CALENDAR_DAYS |
pay_type | string | PAID, HALF_PAID, UNPAID |
requires_doc_after_days | integer | A document is required after this many consecutive days; null means never |
max_consecutive_days | integer | Longest single request; null means no limit |
gender_restriction | string | M, F, or null for none |
advance_notice_days | integer | |
retroactive_days_allowed | integer | |
can_go_negative | boolean | |
carry_over_merged_into_code | string | Unused days at year end go to this leave type's carry-over instead |
ph_schedule_type | string | Public holiday schedule: FIXED, POOLED |
is_legally_mandated | boolean | |
is_active | boolean | |
sort_order | integer | |
requires_second_approval | boolean | |
created_at | instant | |
updated_at | instant |
leave-allocations
Leave balances per employee, leave type and period. Manual balance adjustments are not part of this resource, so allocated plus carry_over minus taken can differ from the balance Hopshift shows.
- Scope: own company.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
leave_type_id | string | |
period_start | date | |
period_end | date | 9999-12-31 for an ACCUMULATIVE leave type |
allocated | decimal(1) | Days granted for the period |
carry_over | decimal(1) | Days carried from the previous period |
taken | decimal(1) | |
pending | decimal(1) | Days in requests awaiting approval |
carry_over_expires_at | date | |
source | string | POLICY, MANUAL, OPENING_BALANCE |
created_at | instant | |
updated_at | instant |
Payroll
payroll-items, payroll-documents, payslip-pdfs and intercompany-recharges serve rows of FINALIZED runs only. Follow the payroll rule to keep them right when runs are finalised, reopened or republished.
payroll-runs
Payroll runs of every status. A run's status says whether its payslips, documents, PDFs and recharges are served.
- Scope: own company (the run's company).
include:notesaddsnotes(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
company_id | string | |
period_start | date | |
period_end | date | |
pay_date | date | |
status | string | DRAFT, REVIEW, APPROVED, PROCESSING, COMPLETED, FINALIZED |
publication_revision | integer | 0 until the run is first published, then increases each time it is published |
finalized_at | instant | |
total_gross | money | |
total_deductions | money | |
total_net | money | |
total_employer_cost | money | |
created_at | instant | |
updated_at | instant |
payroll-items
Payslips of finalised runs, with their lines nested.
- Scope: the run is
FINALIZEDand its company is one of the key's. include:notesaddsmanual_notes(string) andno_sso_reason(string) to the payslip, andoverride_reason(string) andpayslip_note(string) to each line.- Content: none; the PDF is a
payslip-pdfsrow. Run listing: yes. - Page size: at most 200 (default 200).
| Field | Type | Meaning |
|---|---|---|
id | string | |
payroll_run_id | string | |
employee_id | string | |
company_id | string | The run's company |
cost_bearing_company_id | string | The company that bears the cost |
intercompany_assignment_id | string | |
pay_type | string | Snapshot at calculation: HOURLY, DAILY, SALARY, COMMISSION, MIXED |
hourly_rate | money | |
regular_hours | decimal(2) | |
overtime_hours | decimal(2) | |
holiday_hours | decimal(2) | |
rest_day_hours | decimal(2) | |
unpaid_leave_hours | decimal(2) | |
basic_pay | money | |
overtime_pay | money | |
holiday_pay | money | |
rest_day_pay | money | |
allowances | money | |
commissions | money | |
bonuses | money | |
back_pay | money | |
gross_pay | money | Sum of all positive earnings |
gross_income_adjustments | money | Unpaid leave and overpayment recovery; gross pay after them is gross_pay minus this |
sso_eligible_income | money | Income SSO was assessed on |
sso_contribution_base | money | Base the SSO contribution was calculated from |
taxable_earnings | money | |
net_taxable_income | money | |
employee_sso | money | |
employer_sso | money | |
income_tax | money | Withholding tax |
loan_deduction | money | |
other_deductions | money | |
total_deductions | money | |
net_pay | money | |
total_cost_to_company | money | |
proration_factor | decimal(4) | |
is_manual_override | boolean | HR overrode the calculated figures |
no_sso | boolean | HR waived SSO on this payslip |
calculated_at | instant | |
created_at | instant | |
updated_at | instant | |
lines | array | Payslip lines, ordered by sort_order then id, fields below |
Each line:
| Field | Type | Meaning |
|---|---|---|
id | string | |
pay_item_id | string | The company pay item (pay-items); null for system lines |
name | string | The pay item name when calculated |
type | string | EARNING, DEDUCTION, EMPLOYER_CONTRIBUTION |
amount | money | The amount paid |
computed_amount | money | What the calculation produced; differs from amount when HR overrode the line |
calc_method | string | FIXED, PERCENTAGE or MANUAL, as stored on the line |
sort_order | integer |
payroll-documents
Government filings and the payroll export of finalised runs. Metadata only: the file is the content.
- Scope: the run exists, is
FINALIZEDand its company is one of the key's. TypesPND1,PND1_PDF,SSO_1_10,SSO_1_10_PDF,SSO_SUBMISSIONandPAYROLL_CSV.PAYSLIPS_ZIPis never listed. include: none.- Content: the file; see Files for formats. Run listing: yes.
| Field | Type | Meaning |
|---|---|---|
id | string | |
payroll_run_id | string | |
company_id | string | The run's company |
type | string | One of the six types above |
filename | string | |
content_type | string | The Content-Type the file is served with |
generated_at | instant | Moves when the filing is regenerated |
payslip-pdfs
Password-protected payslip PDFs of finalised runs. Metadata only: the file is the content.
A run has payslip PDFs only once it has been published from Hopshift. Runs finalised without being published, such as payroll imported from an earlier system, still have their payroll items and filings but no payslip PDFs.
- Scope: the run is
FINALIZEDin one of the key's companies, the PDF belongs to the run's active publication, and no later protection of the same payslip exists. A newer protection (for example after a birthday correction) arrives as a new row that replaces the earlier row with the samepayroll_item_id. include: none.- Content: the PDF as stored, AES-256 protected with the employee's birthday as
DDMMYYYY(Gregorian year).404when there is no PDF. Run listing: yes.
| Field | Type | Meaning |
|---|---|---|
id | string | |
payroll_item_id | string | The payslip |
payroll_run_id | string | |
employee_id | string | |
company_id | string | The run's company |
publication_revision | integer | The run publication this PDF belongs to |
protection_revision | integer | Starts at 0 and increases each time the payslip is protected again |
filename | string | |
pdf_available | boolean | False when the employee had no birthday on file, so there is no PDF |
pdf_sha256 | string | SHA-256 (hex) of the PDF file; null without a PDF |
protected_at | instant | |
generated_at | instant |
pay-items
Each company's pay components: what payslip lines, employee pay items and schedules point at.
- Scope: own company.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
company_id | string | |
group_pay_item_definition_id | string | The group component this item replicates (pay-item-definitions) |
code | string | Short code, such as HOUSING_ALLOW |
name | string | |
name_local | string | Local-language name |
type | string | EARNING, DEDUCTION, EMPLOYER_CONTRIBUTION |
category | string | Free grouping, such as Allowances |
calc_method | string | FIXED, PERCENTAGE, HOURS_BASED, FORMULA |
default_amount | money | For FIXED |
percentage | decimal(4) | For PERCENTAGE: "0.0500" is 5% |
percentage_of | string | What the percentage applies to, such as basicPay, grossPay or another pay item code |
is_taxable | boolean | Subject to income tax |
is_ssoable | boolean | Subject to SSO |
is_pvfable | boolean | Subject to provident fund |
reduces_gross_income | boolean | A deduction that reduces reportable income, such as unpaid leave |
should_prorate | boolean | Prorated for joiners and leavers |
projects_for_annualization | boolean | Projected over future months when annualising tax |
is_active | boolean | |
is_system | boolean | |
created_at | instant | |
updated_at | instant |
pay-item-definitions
The group's canonical pay components. Company pay items sharing a definition are the same component across companies.
- Scope: group.
include: none. Content: none. Run listing: no.
Fields: id plus the same fields as pay-items from code to updated_at (no company_id and no group_pay_item_definition_id).
employee-pay-items
Recurring pay components set on an employee.
- Scope: through the employee.
include:notesaddsnotes(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
pay_item_id | string | |
amount | money | Overrides the pay item's amount; null uses the pay item's |
percentage | decimal(4) | Overrides the pay item's percentage; null uses the pay item's |
is_active | boolean | |
effective_from | date | |
effective_to | date | Null while ongoing |
created_at | instant | |
updated_at | instant |
employee-pay-schedules
Planned monthly amounts of a pay component for an employee. Imported incentives live here.
- Scope: through the employee.
include:notesaddsnote(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
pay_item_id | string | |
tax_year | integer | Such as 2026 |
month | integer | Calendar month, 1 to 12 |
amount | money | |
created_at | instant | |
updated_at | instant |
meal-allowance-policies
Meal allowance rules: a group default (company_id null) and company overrides. An enrolled employee's policy is their company's override, else the group default.
- Scope: the group default reaches every key of the group; a company override reaches keys covering that company.
include:notesaddsdescription(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
group_id | string | |
company_id | string | Null for the group default |
name | string | |
amount | money | |
currency | string | Such as THB |
min_hours | decimal(1) | |
max_hours | decimal(1) | |
is_taxable | boolean | |
is_active | boolean | |
effective_from | date | |
deduct_on_no_show | boolean | |
prorate_on_mid_shift | boolean | |
late_deduction_mode | string | NONE, PROPORTIONAL, PER_MINUTE |
late_deduction_rate | decimal(4) | |
grace_minutes | integer | |
deduct_on_early_out | boolean | |
deduct_on_missing_clock_out | boolean | |
created_at | instant | |
updated_at | instant |
meal-allowance-enrollments
Which employees receive meal allowance. The policy is resolved by company, not stored here.
- Scope: own company.
include: none. Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
source | string | POLICY, MANUAL, OPENING_BALANCE |
calculation_mode | string | ATTENDANCE, CLOCKED_DAYS, FIXED_MONTHLY (legacy, calculated like CLOCKED_DAYS) |
monthly_amount | money | |
override_amount | money | Per-employee amount; null uses the policy's |
pay_unrostered_days | boolean | Also pays days clocked without a roster |
is_active | boolean | |
created_at | instant | |
updated_at | instant |
intercompany-assignments
An employee of one company (source) working for another (host), with how the cost is recharged.
- Scope: either company: the key receives the row when the source or the host company is one of its companies.
include:notesaddsnotes(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
transfer_id | string | |
source_company_id | string | Sent when the company is in the key's group |
host_company_id | string | Sent when the company is in the key's group |
host_department_id | string | |
cost_basis_type | string | ACTUAL_COST, FIXED_RATE, COST_PLUS_MARKUP |
fixed_monthly_rate | money | |
markup_percentage | decimal(2) | |
include_basic_pay | boolean | Basic pay is recharged |
include_overtime_pay | boolean | |
include_allowances | boolean | |
include_bonuses | boolean | |
include_employer_contributions | boolean | |
effective_from | date | |
effective_to | date | |
is_active | boolean | |
created_at | instant | |
updated_at | instant |
intercompany-recharges
What a host company owes the source company for one finalised payroll run, with lines per employee.
- Scope: the recharge is in the key's group, the source or the host company is one of the key's companies, the run is
FINALIZED, and the recharge belongs to the run's current publication. A key without the source company getspayroll_run_idnull; see the host-company limitation. include:notesaddsnotes(string),settlement_reference(string) anddispute_reason(string) to the recharge, andnotes(string) to each line.- Content: none. Run listing: yes.
| Field | Type | Meaning |
|---|---|---|
id | string | |
payroll_run_id | string | The source company's run |
publication_revision | integer | The run publication the recharge belongs to |
source_company_id | string | |
host_company_id | string | |
period_start | date | |
period_end | date | |
currency | string | |
total_actual_cost | money | |
total_markup_amount | money | |
total_recharge_amount | money | |
status | string | DRAFT, CONFIRMED, INVOICED, SETTLED, DISPUTED, CANCELLED |
invoice_number | string | |
invoice_due_date | date | |
confirmed_at | instant | |
invoiced_at | instant | |
settled_at | instant | |
disputed_at | instant | |
created_at | instant | |
updated_at | instant | |
lines | array | One line per employee, ordered by id, fields below |
Each line:
| Field | Type | Meaning |
|---|---|---|
id | string | |
assignment_id | string | The intercompany assignment |
employee_id | string | |
basic_pay | money | |
overtime_pay | money | |
allowances | money | |
bonuses | money | |
other_earnings | money | |
employer_contributions | money | |
actual_cost | money | |
cost_basis_type | string | ACTUAL_COST, FIXED_RATE, COST_PLUS_MARKUP |
fixed_monthly_rate | money | |
markup_percentage | decimal(2) | |
markup_amount | money | |
proration_factor | decimal(4) | |
recharge_amount | money | |
created_at | instant | |
updated_at | instant |
tax-declarations
Employees' yearly tax allowance and deduction declarations. Amounts only: no dependant names or ids.
- Scope: own company.
include:notesaddsnotes(string).- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
employee_id | string | |
company_id | string | |
tax_year | integer | |
personal_allowance | money | |
spouse_allowance | money | |
child_allowance | money | |
child_education_allowance | money | |
parent_support_allowance | money | |
disabled_dependent_allowance | money | |
life_insurance_premium | money | |
health_insurance_premium | money | |
parent_health_insurance | money | |
pension_insurance | money | |
provident_fund_contribution | money | |
home_loan_interest | money | |
social_enterprise_donation | money | |
education_donation | money | |
general_donation | money | |
other_deductions | money | |
status | string | DRAFT, SUBMITTED, APPROVED |
submitted_at | instant | |
approved_at | instant | |
approved_by_user_id | string | |
created_at | instant | |
updated_at | instant |
History
audit-events
Hopshift's audit log, redacted. It covers only some changes, much of it imports and repairs; the per-record histories are clock-record-history, leave-request-history, employee-events and salary-adjustments.
- Scope: the audited record's company, resolved when the row is read, for these entity types:
employee/Employee,shift/Shift(deleted shifts too),ClockRecord,ClockSession,AttendanceCorrectionRequest/attendance_correction,EmployeeLeaveAllocation,EmployeeLeaveGrant,UserEmailChangeRequest,PayrollRun,PayrollItem/payroll_item. Rows about the group's API keys, MCP keys, MCP connections and MCP operations (ApiKey,McpKey,McpOAuthGrant,McpOperation) have no company and reach only whole-group keys. Rows of any other type, and rows whose record no longer exists, reach no key. include:notesaddsreason(string, the actor's) and keeps free text insidevalues.valuesaddsvalues(object{ "before": ..., "after": ... }, the recorded values). Withoutnotes, every key that holds free text (notes, reasons, descriptions, comments, remarks,wouldRehire) is removed from them at any depth.valuesis null when the row recorded no values (itschanged_fieldsis then empty) or when personal evidence hides it.- Both are null when the employee the row is about belongs to a company outside the key's companies or can no longer be found, and on payroll run and payslip rows whose run is not currently
FINALIZED.
- Content: none. Run listing: no.
| Field | Type | Meaning |
|---|---|---|
id | string | |
action | string | What happened, as recorded |
entity_type | string | One of the types above |
entity_id | string | The audited record's id |
performed_by_user_id | string | |
company_id | string | The resolved company; null on a group row |
created_at | instant | |
changed_fields | array | Names of the fields the row records as changed, sorted. Names only, never values |