How to Export Timesheets to CSV for Payroll Processing

Generate weekly timesheet CSV reports from Deputy and deliver them via FTP or email.

What This Integration Does

Payroll systems almost always want a flat CSV file: one row per employee per pay period, columns for hours, rate codes, locations, and any allowances. Deputy holds the source data but exporting it manually every week is repetitive and error-prone. This workflow runs on a schedule, pulls the previous week's timesheets, reshapes them into your payroll system's column order, writes a CSV, and drops it on an FTP server (or attaches it to an email) ready for the payroll team to pick up.

It's a small workflow but a high-leverage one. Each run produces a single CSV artifact, named by date, that's easy to audit and rerun. Because the source query is bounded by date, reruns are idempotent - generating last week's report a second time produces the same file.

Prerequisites

  • A deputy connection with read access to timesheets and employees.
  • Either an ftp connection (host, user, password, target directory) or an smtp / resend connection for email delivery.
  • The exact CSV column order your payroll system expects. Pull a sample export from the payroll vendor to compare against.
  • Awareness of your pay-week boundary (Monday-Sunday is most common).

Step 1: Schedule Trigger

Add a Trigger node and set its type to Schedule. A typical cadence is Monday at 06:00 in your operations timezone so the report is waiting for payroll by start of business. If your pay week ends on a different day, shift the schedule one day after pay-week-end so all timesheets are finalised in Deputy by the time the workflow runs.

Step 2: Compute the Pay Week Window

Add a Variable assignment driven by the date connector:

  • windowEnd - last Sunday at 23:59 using end-of with unit "week" and an offset.
  • windowStart - the Monday seven days before windowEnd.
  • fileDate - windowEnd formatted as YYYY-MM-DD using format - this is what goes in the filename.

Pin the timezone explicitly. Payroll boundaries that drift by an hour because of DST cause genuine paycheck errors.

Step 3: Fetch Timesheets from Deputy

Add a Connector node pointing at the deputy connector and pick the list-timesheets tool. Set the date filter to windowStart / windowEnd and request only approved timesheets so you don't ship unapproved data to payroll. In parallel, fetch deputy list-employees and (optionally) list-locations so you can resolve IDs to readable names in the output CSV.

Step 4: Transform - Project the Payroll Schema

Add a Transform node to flatten and reshape each timesheet row to match your payroll system's columns. A typical projection:

{
  "employee_id": "{{ timesheet.employeeId }}",
  "employee_name": "{{ employee.name }}",
  "date": "{{ timesheet.date }}",
  "start_time": "{{ timesheet.startTime }}",
  "end_time": "{{ timesheet.endTime }}",
  "hours_worked": "{{ timesheet.totalHours }}",
  "pay_rate_code": "{{ timesheet.payCode }}",
  "location_code": "{{ location.externalCode }}",
  "notes": "{{ timesheet.notes }}"
}

Use a Loop node if you need to enrich each timesheet row with the employee and location lookup data.

Step 5: Generate the CSV

Add a Connector node pointing at the csv connector and pick the from-json tool. Pass in the transformed array. Configure:

  • Headers: explicit list matching your payroll system's column order (the keys you used in Step 4).
  • Delimiter: , (or \t if the payroll vendor wants TSV).
  • Quote strings: enabled - safer for fields with commas or newlines in notes.

The output is a string ready to write to a file.

Step 6: Deliver via FTP or Email

Pick one delivery path:

  • FTP: a Connector node pointing at the ftp connector with upload-file. Set the path to something like /payroll/timesheets-{{ fileDate }}.csv.
  • Email: a Send Email node with the CSV as an attachment, addressed to the payroll team alias.

Add a final Condition node that checks the upload/send result. On failure, post a slack send-message alert so on-call sees the break before payroll does.

Tips

  • Filter for approved timesheets only. Unapproved entries shouldn't reach payroll. The Deputy filter is cheaper than a downstream Condition node.
  • Stamp the filename with the pay-week-end date, not "today". Re-runs and historical audits both depend on this.
  • Keep a copy. Even with FTP delivery, upload a copy to a long-term archive bucket so finance has a paper trail.
  • Test the column order by feeding your CSV into the payroll system's import preview before flipping the schedule on.

Common Pitfalls

  • Timezone drift. Deputy timesheet boundaries follow location timezones. If your week boundary is computed in UTC, shifts that cross midnight will land in the wrong week.
  • Embedded commas in notes. Without quoting enabled, free-text notes will silently break the CSV column count.
  • Pagination. list-timesheets caps per-page results. Loop until the page is empty rather than relying on a single call.
  • Missing employees. A new hire who started on Monday may not have a payroll ID yet. Add a Condition that routes rows with empty employee_id to a Slack alert instead of into the CSV.

Testing

Run the workflow manually first with last week's window. Open the resulting CSV in a spreadsheet and verify column order, totals, and one or two employees against the Deputy UI. Once a clean file lands, do a dry-run import into the payroll system before turning the weekly schedule on.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.