DevelopersAPI reference

Payroll API

REST endpoints for payroll in ServFlo: 9 operations with parameters, request and response fields, permissions and curl examples.

Base URL: https://app.servflo.ai/api. Every call needs Authorization: Bearer sfk_… (Authentication); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id.

OperationEndpointRisk
Payroll settingsGET /organizations/{organizationId}/payroll/settingsread
Change payroll settingsPATCH /organizations/{organizationId}/payroll/settingswrite
People on payrollGET /organizations/{organizationId}/payroll/peopleread
Set a person's payPATCH /organizations/{organizationId}/payroll/peoplewrite
Pay periodsGET /organizations/{organizationId}/payroll/periodsread
Payroll run for a pay periodGET /organizations/{organizationId}/payroll/runread
Close a pay periodPOST /organizations/{organizationId}/payroll/run/closesensitive
Reopen a pay periodPOST /organizations/{organizationId}/payroll/run/reopensensitive
Download a payroll fileGET /organizations/{organizationId}/payroll/run/exportread

Payroll settings

GET /organizations/{organizationId}/payroll/settings

The pay schedule (WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY and the anchor date weekly/bi-weekly periods repeat from), the overtime rules (weekly threshold, optional daily overtime and double time, California's seventh-day rule, multipliers, workweek start), non-job time types that are unpaid, the default payroll service and its company codes. saved is false while the defaults (bi-weekly, 40 h weekly overtime at 1.5×) apply. Read-only.

Permissionpayroll.read (Owner, Admin)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolpayroll_getSettings (toolset team)

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/settings" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
scheduleenumOne of WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY.
anchorDatestringA day a pay period starts on (weekly and bi-weekly repeat from it).
workweekStartDayinteger0 = Sunday … 6 = Saturday.
weeklyOvertimeMinutesinteger or null
dailyOvertimeMinutesinteger or null
dailyDoubleTimeMinutesinteger or null
seventhDayRuleboolean
overtimeMultipliernumber
doubleTimeMultipliernumber
unpaidActivitiesarray of enum
defaultProviderenumOne of GENERIC, GUSTO, ADP, PAYCHEX, QUICKBOOKS.
adpCompanyCodestring or null
paychexClientIdstring or null
savedbooleanFalse while the defaults apply.

Change payroll settings

PATCH /organizations/{organizationId}/payroll/settings

Changes the fields sent: pay schedule, anchor date, overtime rules (California: dailyOvertimeMinutes 480, dailyDoubleTimeMinutes 720, seventhDayRule true), multipliers, unpaid non-job time types, default payroll service, ADP company code, Paychex client id. Affects every open period's run (closed periods keep their snapshot). Returns the settings.

Permissionpayroll.manage (Owner, Admin)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolpayroll_updateSettings (toolset team)

Request body

FieldTypeRequiredDescription
scheduleenumNoOne of WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY.
anchorDatestringNoA first day of a pay period.
workweekStartDayintegerNo0–6.
weeklyOvertimeMinutesinteger or nullNoWeekly overtime threshold in minutes; null = none. 0–10080.
dailyOvertimeMinutesinteger or nullNoDaily overtime threshold in minutes; null = none. 0–10080.
dailyDoubleTimeMinutesinteger or nullNoDaily double-time threshold in minutes; null = none. 0–10080.
seventhDayRulebooleanNo
overtimeMultipliernumberNo1–5.
doubleTimeMultipliernumberNo1–5.
unpaidActivitiesarray of enumNoUp to 20 items.
defaultProviderenumNoOne of GENERIC, GUSTO, ADP, PAYCHEX, QUICKBOOKS.
adpCompanyCodestring or nullNoADP "Co Code" for the paydata file. Up to 100 characters.
paychexClientIdstring or nullNoPaychex Flex client id for the import file. Up to 100 characters.

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/settings" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"schedule":"WEEKLY","anchorDate":"2026-11-03","workweekStartDay":1}'

Response

200: the answer's fields
FieldTypeDescription
scheduleenumOne of WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY.
anchorDatestringA day a pay period starts on (weekly and bi-weekly repeat from it).
workweekStartDayinteger0 = Sunday … 6 = Saturday.
weeklyOvertimeMinutesinteger or null
dailyOvertimeMinutesinteger or null
dailyDoubleTimeMinutesinteger or null
seventhDayRuleboolean
overtimeMultipliernumber
doubleTimeMultipliernumber
unpaidActivitiesarray of enum
defaultProviderenumOne of GENERIC, GUSTO, ADP, PAYCHEX, QUICKBOOKS.
adpCompanyCodestring or null
paychexClientIdstring or null
savedbooleanFalse while the defaults apply.

People on payroll

GET /organizations/{organizationId}/payroll/people

Every technician and everyone who has tracked time here or has a payroll profile: pay type, hourly rate (a technician's is their technician pay rate), annual salary, overtime multiplier override and exemption, and their ids in each payroll service. Read-only.

Permissionpayroll.read (Owner, Admin)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolpayroll_listPeople (toolset team)

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/people" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields

An array of objects:

FieldTypeDescription
personKeystringThe person's user id, or technician:<id> for a technician without an account.
userIdstring or null
technicianIdstring or null
namestring
activeboolean
payTypeenumOne of HOURLY, SALARY.
hourlyPayCentsinteger or null
annualSalaryCentsinteger or null
overtimeMultipliernumber or nullOverrides the business's; null = theirs.
overtimeExemptboolean
employeeNumberstring or null
gustoEmployeeIdstring or null
adpFileNumberstring or null
paychexWorkerIdstring or null
quickbooksEmployeeIdstring or null

Set a person's pay

PATCH /organizations/{organizationId}/payroll/people

Sets how one person is paid and who they are in the payroll services (fields sent only). Name them by technicianId (from GET /technicians) or userId (someone who tracked time; from GET /payroll/people). A technician's hourly rate is their technician pay rate, also used by job costing and the payroll hours report. Returns the person.

Permissionpayroll.manage (Owner, Admin)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolpayroll_updatePerson (toolset team)

Request body

FieldTypeRequiredDescription
technicianIdstring or nullNoThe technician (preferred when they have one).
userIdstring or nullNoA ServFlo account that tracked time here.
payTypeenumNoOne of HOURLY, SALARY.
hourlyPayCentsinteger or nullNo0–100000000.
annualSalaryCentsinteger or nullNo0–10000000000.
overtimeMultipliernumber or nullNoNull = the business's multiplier. 1–5.
overtimeExemptbooleanNo
employeeNumberstring or nullNoUp to 100 characters.
gustoEmployeeIdstring or nullNoUp to 100 characters.
adpFileNumberstring or nullNoADP "File #". Up to 100 characters.
paychexWorkerIdstring or nullNoUp to 100 characters.
quickbooksEmployeeIdstring or nullNoUp to 100 characters.

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/people" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"technicianId":"technician_id","userId":"user_id","payType":"HOURLY"}'

Response

200: the answer's fields
FieldTypeDescription
personKeystringThe person's user id, or technician:<id> for a technician without an account.
userIdstring or null
technicianIdstring or null
namestring
activeboolean
payTypeenumOne of HOURLY, SALARY.
hourlyPayCentsinteger or null
annualSalaryCentsinteger or null
overtimeMultipliernumber or nullOverrides the business's; null = theirs.
overtimeExemptboolean
employeeNumberstring or null
gustoEmployeeIdstring or null
adpFileNumberstring or null
paychexWorkerIdstring or null
quickbooksEmployeeIdstring or null

Pay periods

GET /organizations/{organizationId}/payroll/periods

The current pay period and the ones before it (newest first, count 1–52, default 12), each OPEN, CLOSED or REOPENED, from the pay schedule. Read-only. Next: GET /payroll/run?date=<start>.

Permissionpayroll.read (Owner, Admin)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolpayroll_listPeriods (toolset team)

Parameters

NameInTypeRequiredDescription
countqueryintegerNo1–52. Default 12.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/periods" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
todaystring
scheduleenumOne of WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY.
itemsarray of object
items[].startstring
items[].endstring
items[].stateenumOne of OPEN, CLOSED, REOPENED.
items[].currentboolean
items[].closedAtstring (date-time) or null

Payroll run for a pay period

GET /organizations/{organizationId}/payroll/run

Per person for the pay period holding date: regular, overtime and double-time hours (by the overtime rules, per workweek and day), paid and unpaid non-job time, unapproved and running entries, wages (hourly × multipliers, or salary ÷ periods per year), approved commissions earned in the period, tips on payments in the period (to the job's technician), reimbursable expenses, gross and total, with warnings. Each person carries their home business unit and byBusinessUnit subtotals the run per unit; businessUnitId keeps one unit's people. Live while open; the snapshot taken at closing once closed (changedSinceClose counts entries changed later). Read-only.

Permissionpayroll.read (Owner, Admin)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolpayroll_getRun (toolset team)

Parameters

NameInTypeRequiredDescription
datequerystringYesAny day in the pay period (its first day is usual); the period comes from the pay schedule.
businessUnitIdquerystringNoOnly the people whose home business unit is this one (ids from GET /business-units), or "none" for people without a unit (office staff, generalists). Totals are then that unit's. Omit for everyone (a business without units never needs it). Up to 100 characters.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/run?date=2026-11-03" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
periodobject
period.startstring
period.endstring
scheduleenumOne of WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY.
timezonestring
rulesobject
rules.workweekStartDayinteger
rules.weeklyOvertimeMinutesinteger or null
rules.dailyOvertimeMinutesinteger or null
rules.dailyDoubleTimeMinutesinteger or null
rules.seventhDayRuleboolean
rules.overtimeMultipliernumber
rules.doubleTimeMultipliernumber
rules.unpaidActivitiesarray of enum
peoplearray of object
people[].personKeystring
people[].userIdstring or null
people[].technicianIdstring or null
people[].namestring
people[].businessUnitIdstring or nullThe person's home business unit (their technician's; GET /business-units); null = none, also for people who are not technicians.
people[].businessUnitNamestring or null
people[].payTypeenumOne of HOURLY, SALARY.
people[].hourlyPayCentsinteger or null
people[].annualSalaryCentsinteger or null
people[].overtimeMultipliernumber
people[].overtimeExemptboolean
people[].paidMinutesinteger
people[].regularMinutesinteger
people[].overtimeMinutesinteger
people[].doubleTimeMinutesinteger
people[].paidNonJobMinutesintegerPaid non-job time (shop, training…), inside the hours.
people[].unpaidNonJobMinutesintegerNon-job time of unpaid types, left out of the hours.
people[].unapprovedEntriesinteger
people[].unapprovedMinutesinteger
people[].runningEntriesinteger
people[].wagesCentsinteger or nullNull when no rate or salary is set.
people[].commissionCentsintegerApproved and paid commission lines earned in the period.
people[].pendingCommissionCentsintegerNot in the gross until approved.
people[].tipsCentsinteger
people[].reimbursementCentsinteger
people[].grossCentsintegerWages + commissions + tips, before taxes and deductions.
people[].totalCentsintegerGross + reimbursements.
people[].employeeNumberstring or null
people[].gustoEmployeeIdstring or null
people[].adpFileNumberstring or null
people[].paychexWorkerIdstring or null
people[].quickbooksEmployeeIdstring or null
people[].warningsarray of string
totalsobject
totals.peopleinteger
totals.paidMinutesinteger
totals.regularMinutesinteger
totals.overtimeMinutesinteger
totals.doubleTimeMinutesinteger
totals.paidNonJobMinutesinteger
totals.wagesCentsinteger
totals.commissionCentsinteger
totals.tipsCentsinteger
totals.reimbursementCentsinteger
totals.grossCentsinteger
totals.totalCentsinteger
totals.unapprovedEntriesinteger
totals.runningEntriesinteger
unattributedTipsCentsintegerTips on payments with no technician on the job and not taken by a technician (0 when filtered by unit).
businessUnitIdstring or nullThe unit filter applied (an id or "none"); null = everyone.
usesBusinessUnitsbooleanThe business has business units.
byBusinessUnitarray of objectThe run grouped by home business unit: a subtotal per unit with people in it (by name), then "No unit". Empty for a business without units.
byBusinessUnit[].peopleinteger
byBusinessUnit[].paidMinutesinteger
byBusinessUnit[].regularMinutesinteger
byBusinessUnit[].overtimeMinutesinteger
byBusinessUnit[].doubleTimeMinutesinteger
byBusinessUnit[].wagesCentsinteger
byBusinessUnit[].commissionCentsinteger
byBusinessUnit[].tipsCentsinteger
byBusinessUnit[].reimbursementCentsinteger
byBusinessUnit[].grossCentsinteger
byBusinessUnit[].totalCentsinteger
byBusinessUnit[].idstring or nullThe unit; null = "No unit".
byBusinessUnit[].namestring
computedAtstring
stateenumOPEN (never closed), CLOSED (locked), REOPENED. One of OPEN, CLOSED, REOPENED.
sourceenumSnapshot: the figures frozen when it was closed. One of LIVE, SNAPSHOT.
endedbooleanThe period is over (it can be closed).
closedAtstring (date-time) or null
closedByUserIdstring or null
reopenedAtstring (date-time) or null
reopenedByUserIdstring or null
reopenReasonstring or null
changedSinceCloseintegerTime entries in a closed period changed after closing (e.g. synced from a phone).

Close a pay period

POST /organizations/{organizationId}/payroll/run/close

Locks timesheets: closes the pay period holding date once it has ended. Its time entries can no longer be added, edited, deleted, submitted, approved or rejected until it is reopened (409). Refused while a clock runs in it; with unapproved entries it is refused unless acknowledgeUnapproved is true. The run is frozen as the period's snapshot, which the exports then use. Emits payroll.closed. Returns the run.

Permissionpayroll.manage (Owner, Admin)
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolpayroll_close (toolset team)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Request body

FieldTypeRequiredDescription
datestringYesAny day in the pay period (its first day is usual); the period comes from the pay schedule.
acknowledgeUnapprovedbooleanNoClose even though some entries are not approved.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/run/close" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"date":"2026-11-03"}'

Response

200: the answer's fields
FieldTypeDescription
periodobject
period.startstring
period.endstring
scheduleenumOne of WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY.
timezonestring
rulesobject
rules.workweekStartDayinteger
rules.weeklyOvertimeMinutesinteger or null
rules.dailyOvertimeMinutesinteger or null
rules.dailyDoubleTimeMinutesinteger or null
rules.seventhDayRuleboolean
rules.overtimeMultipliernumber
rules.doubleTimeMultipliernumber
rules.unpaidActivitiesarray of enum
peoplearray of object
people[].personKeystring
people[].userIdstring or null
people[].technicianIdstring or null
people[].namestring
people[].businessUnitIdstring or nullThe person's home business unit (their technician's; GET /business-units); null = none, also for people who are not technicians.
people[].businessUnitNamestring or null
people[].payTypeenumOne of HOURLY, SALARY.
people[].hourlyPayCentsinteger or null
people[].annualSalaryCentsinteger or null
people[].overtimeMultipliernumber
people[].overtimeExemptboolean
people[].paidMinutesinteger
people[].regularMinutesinteger
people[].overtimeMinutesinteger
people[].doubleTimeMinutesinteger
people[].paidNonJobMinutesintegerPaid non-job time (shop, training…), inside the hours.
people[].unpaidNonJobMinutesintegerNon-job time of unpaid types, left out of the hours.
people[].unapprovedEntriesinteger
people[].unapprovedMinutesinteger
people[].runningEntriesinteger
people[].wagesCentsinteger or nullNull when no rate or salary is set.
people[].commissionCentsintegerApproved and paid commission lines earned in the period.
people[].pendingCommissionCentsintegerNot in the gross until approved.
people[].tipsCentsinteger
people[].reimbursementCentsinteger
people[].grossCentsintegerWages + commissions + tips, before taxes and deductions.
people[].totalCentsintegerGross + reimbursements.
people[].employeeNumberstring or null
people[].gustoEmployeeIdstring or null
people[].adpFileNumberstring or null
people[].paychexWorkerIdstring or null
people[].quickbooksEmployeeIdstring or null
people[].warningsarray of string
totalsobject
totals.peopleinteger
totals.paidMinutesinteger
totals.regularMinutesinteger
totals.overtimeMinutesinteger
totals.doubleTimeMinutesinteger
totals.paidNonJobMinutesinteger
totals.wagesCentsinteger
totals.commissionCentsinteger
totals.tipsCentsinteger
totals.reimbursementCentsinteger
totals.grossCentsinteger
totals.totalCentsinteger
totals.unapprovedEntriesinteger
totals.runningEntriesinteger
unattributedTipsCentsintegerTips on payments with no technician on the job and not taken by a technician (0 when filtered by unit).
businessUnitIdstring or nullThe unit filter applied (an id or "none"); null = everyone.
usesBusinessUnitsbooleanThe business has business units.
byBusinessUnitarray of objectThe run grouped by home business unit: a subtotal per unit with people in it (by name), then "No unit". Empty for a business without units.
byBusinessUnit[].peopleinteger
byBusinessUnit[].paidMinutesinteger
byBusinessUnit[].regularMinutesinteger
byBusinessUnit[].overtimeMinutesinteger
byBusinessUnit[].doubleTimeMinutesinteger
byBusinessUnit[].wagesCentsinteger
byBusinessUnit[].commissionCentsinteger
byBusinessUnit[].tipsCentsinteger
byBusinessUnit[].reimbursementCentsinteger
byBusinessUnit[].grossCentsinteger
byBusinessUnit[].totalCentsinteger
byBusinessUnit[].idstring or nullThe unit; null = "No unit".
byBusinessUnit[].namestring
computedAtstring
stateenumOPEN (never closed), CLOSED (locked), REOPENED. One of OPEN, CLOSED, REOPENED.
sourceenumSnapshot: the figures frozen when it was closed. One of LIVE, SNAPSHOT.
endedbooleanThe period is over (it can be closed).
closedAtstring (date-time) or null
closedByUserIdstring or null
reopenedAtstring (date-time) or null
reopenedByUserIdstring or null
reopenReasonstring or null
changedSinceCloseintegerTime entries in a closed period changed after closing (e.g. synced from a phone).

Reopen a pay period

POST /organizations/{organizationId}/payroll/run/reopen

Unlocks a closed pay period so its time can change again; the reason, who and when are kept (and in the activity log). The run is live again until it is closed again. Emits payroll.reopened. Returns the run.

Permissionpayroll.manage (Owner, Admin)
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolpayroll_reopen (toolset team)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Request body

FieldTypeRequiredDescription
datestringYesAny day in the pay period (its first day is usual); the period comes from the pay schedule.
reasonstringYesWhy it is reopened. Up to 2000 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/run/reopen" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"date":"2026-11-03","reason":"string"}'

Response

200: the answer's fields
FieldTypeDescription
periodobject
period.startstring
period.endstring
scheduleenumOne of WEEKLY, BIWEEKLY, SEMIMONTHLY, MONTHLY.
timezonestring
rulesobject
rules.workweekStartDayinteger
rules.weeklyOvertimeMinutesinteger or null
rules.dailyOvertimeMinutesinteger or null
rules.dailyDoubleTimeMinutesinteger or null
rules.seventhDayRuleboolean
rules.overtimeMultipliernumber
rules.doubleTimeMultipliernumber
rules.unpaidActivitiesarray of enum
peoplearray of object
people[].personKeystring
people[].userIdstring or null
people[].technicianIdstring or null
people[].namestring
people[].businessUnitIdstring or nullThe person's home business unit (their technician's; GET /business-units); null = none, also for people who are not technicians.
people[].businessUnitNamestring or null
people[].payTypeenumOne of HOURLY, SALARY.
people[].hourlyPayCentsinteger or null
people[].annualSalaryCentsinteger or null
people[].overtimeMultipliernumber
people[].overtimeExemptboolean
people[].paidMinutesinteger
people[].regularMinutesinteger
people[].overtimeMinutesinteger
people[].doubleTimeMinutesinteger
people[].paidNonJobMinutesintegerPaid non-job time (shop, training…), inside the hours.
people[].unpaidNonJobMinutesintegerNon-job time of unpaid types, left out of the hours.
people[].unapprovedEntriesinteger
people[].unapprovedMinutesinteger
people[].runningEntriesinteger
people[].wagesCentsinteger or nullNull when no rate or salary is set.
people[].commissionCentsintegerApproved and paid commission lines earned in the period.
people[].pendingCommissionCentsintegerNot in the gross until approved.
people[].tipsCentsinteger
people[].reimbursementCentsinteger
people[].grossCentsintegerWages + commissions + tips, before taxes and deductions.
people[].totalCentsintegerGross + reimbursements.
people[].employeeNumberstring or null
people[].gustoEmployeeIdstring or null
people[].adpFileNumberstring or null
people[].paychexWorkerIdstring or null
people[].quickbooksEmployeeIdstring or null
people[].warningsarray of string
totalsobject
totals.peopleinteger
totals.paidMinutesinteger
totals.regularMinutesinteger
totals.overtimeMinutesinteger
totals.doubleTimeMinutesinteger
totals.paidNonJobMinutesinteger
totals.wagesCentsinteger
totals.commissionCentsinteger
totals.tipsCentsinteger
totals.reimbursementCentsinteger
totals.grossCentsinteger
totals.totalCentsinteger
totals.unapprovedEntriesinteger
totals.runningEntriesinteger
unattributedTipsCentsintegerTips on payments with no technician on the job and not taken by a technician (0 when filtered by unit).
businessUnitIdstring or nullThe unit filter applied (an id or "none"); null = everyone.
usesBusinessUnitsbooleanThe business has business units.
byBusinessUnitarray of objectThe run grouped by home business unit: a subtotal per unit with people in it (by name), then "No unit". Empty for a business without units.
byBusinessUnit[].peopleinteger
byBusinessUnit[].paidMinutesinteger
byBusinessUnit[].regularMinutesinteger
byBusinessUnit[].overtimeMinutesinteger
byBusinessUnit[].doubleTimeMinutesinteger
byBusinessUnit[].wagesCentsinteger
byBusinessUnit[].commissionCentsinteger
byBusinessUnit[].tipsCentsinteger
byBusinessUnit[].reimbursementCentsinteger
byBusinessUnit[].grossCentsinteger
byBusinessUnit[].totalCentsinteger
byBusinessUnit[].idstring or nullThe unit; null = "No unit".
byBusinessUnit[].namestring
computedAtstring
stateenumOPEN (never closed), CLOSED (locked), REOPENED. One of OPEN, CLOSED, REOPENED.
sourceenumSnapshot: the figures frozen when it was closed. One of LIVE, SNAPSHOT.
endedbooleanThe period is over (it can be closed).
closedAtstring (date-time) or null
closedByUserIdstring or null
reopenedAtstring (date-time) or null
reopenedByUserIdstring or null
reopenReasonstring or null
changedSinceCloseintegerTime entries in a closed period changed after closing (e.g. synced from a phone).

Download a payroll file

GET /organizations/{organizationId}/payroll/run/export

The pay period's payroll as a CSV for a payroll service: GUSTO (Import hours: hours and earnings per employee), ADP (Workforce Now / RUN paydata import), PAYCHEX (Paychex Flex import, one row per pay component), QUICKBOOKS, or GENERIC (every figure). Default: the business's default service. Uses the closed snapshot when the period is closed. missingIds names people without that service's id (their id cell is blank). Everyone unless businessUnitId limits it to one unit's people. Read-only; contains pay data.

Permissionpayroll.read (Owner, Admin)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolpayroll_export (toolset team)

Parameters

NameInTypeRequiredDescription
datequerystringYesAny day in the pay period (its first day is usual); the period comes from the pay schedule.
providerqueryenumNoOne of GENERIC, GUSTO, ADP, PAYCHEX, QUICKBOOKS.
businessUnitIdquerystringNoOnly the people whose home business unit is this one (ids from GET /business-units), or "none" for people without a unit (office staff, generalists). Totals are then that unit's. Omit for everyone (a business without units never needs it). Up to 100 characters.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/payroll/run/export?date=2026-11-03" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
fileNamestring
csvstring
countinteger
missingIdsarray of string
providerenumOne of GENERIC, GUSTO, ADP, PAYCHEX, QUICKBOOKS.
stateenumOne of OPEN, CLOSED, REOPENED.

On this page