Job Costing

This guide explains how to use the Friday API to track labor time by customer and project, then report on that time for job costing.

Prerequisites

  • Plan: Timetracking or higher.
  • Auth: An API key for the company, sent as X-API-Key on every request.
  • Setup: Customers and projects should already exist in Friday. The API can list, fetch, and update them, but new customer/project setup happens in the Friday product.

How Job Costing Works

Job costing in Friday is built from three pieces:

PiecePurpose
CustomerThe job, customer, or job site you want to track labor against.
ProjectA more specific subdivision under a customer, such as a phase, location, or workstream.
Time recordThe employee's clock-in/clock-out span, optionally tagged with customer_id and project_id.

When employees clock in against customers and projects, Friday can later group time by customer or project in timetrack reports.


Typical Flow

1. Sync customers and projects          → GET /customers and GET /projects
2. Clock employees into a job           → POST /time-records/clock-in
3. Clock employees out                  → POST /time-records/clock-out
4. Request job-costing report details   → GET /timetrack/company
5. Optionally download reports          → GET /timetrack/company/download

Step 1 — Sync Customers and Projects

Use the customer and project endpoints to keep your system aligned with the jobs configured in Friday.

ActionEndpoint
List customersGET /customers
Get customerGET /customers/:customer_id
Update customerPATCH /customers/:customer_id
List projectsGET /projects
Get projectGET /projects/:project_id
Update projectPATCH /projects/:project_id

Projects belong to customers. If your app lets users choose a job before clocking in, show the customer first, then the available projects under that customer.

See the Customers and Projects guide for the full customer/project model.


Step 2 — Clock Into a Customer or Project

When creating a time record, include customer_id, project_id, or both.

POST /time-records/clock-in

{
  "employee_id": 25,
  "type": "work",
  "customer_id": 3,
  "project_id": 7,
  "note": "Starting framing work"
}

The resulting time record stores the job information:

{
  "data": {
    "time_record_id": 1042,
    "type": "work",
    "start": "2026-03-05T09:00:00.000Z",
    "end": null,
    "employee_id": 25,
    "customer_id": 3,
    "project_id": 7,
    "customers": {
      "customer_id": 3,
      "name": "ABC Construction"
    },
    "projects": {
      "project_id": 7,
      "name": "Office Renovation"
    }
  }
}

Some companies require a customer or project for every clock-in. If a required job is missing, the API returns a 400 error.

Disabled customers and projects cannot be used for new time records.


Step 3 — Switch Jobs During the Day

Use type: "switch" when an employee stays clocked in but moves to a different customer or project.

{
  "employee_id": 25,
  "type": "switch",
  "customer_id": 9,
  "project_id": 12,
  "note": "Moving to afternoon job"
}

A switch:

  1. Clocks out the current active work record.
  2. Creates a new work record with the new customer/project details.
  3. Returns the newly created time record.

switch is only a clock-in action. Stored records still have type: "work" or type: "break".


Step 4 — Get Job-Costing Breakdowns

Use timetrack report endpoints when you need totals grouped by job.

Company report

GET /timetrack/company?start=2026-03-01T00:00:00.000Z&end=2026-03-07T23:59:59.999Z&include_customer_reports=true&include_project_reports=true

Employee report

GET /timetrack/employee/25?start=2026-03-01T00:00:00.000Z&end=2026-03-07T23:59:59.999Z&include_customer_reports=true&include_project_reports=true
Query parameterPurpose
startReport start datetime, inclusive.
endReport end datetime, inclusive.
timezoneOptional IANA timezone or numeric UTC offset. Defaults to America/New_York.
include_customer_reports=trueInclude time broken down by customer.
include_project_reports=trueInclude time broken down by project.
include_days=trueInclude per-day breakdowns.
include_time_records=trueInclude raw time records behind the totals.

Example customer breakdown:

{
  "customer_reports": [
    {
      "customer": {
        "customer_id": 3,
        "name": "ABC Construction"
      },
      "report": {
        "regular_hours": 40,
        "overtime_hours": 3,
        "total_hours": 43
      }
    },
    {
      "customer": {
        "customer_id": 9,
        "name": "XYZ Plumbing"
      },
      "report": {
        "regular_hours": 12,
        "overtime_hours": 0,
        "total_hours": 12
      }
    }
  ]
}

Step 5 — Download Job-Costing Reports

Use the company download endpoint when you need a PDF or CSV export.

GET /timetrack/company/download

Required query parameters:

ParameterPurpose
startReport start datetime, inclusive.
endReport end datetime, inclusive.
typeOutput format: pdf, periodTotalsCsv, dailyTotalsCsv, or timeRecordsCsv.

Useful optional parameters:

ParameterPurpose
timezoneTimezone for report grouping.
timetypeFormat time values as decimal or minutes.
employeeIdsComma-separated employee IDs to include.
pay_schedule_group_idRestrict the report to one pay schedule group.

Job Geofencing

If the company uses job geofencing, clock-in and clock-out may require coordinates. The applicable fence comes from the customer/project on clock-in, or from the active time record on clock-out.

Send coordinates as:

latitude,longitude

Example:

{
  "employee_id": 25,
  "customer_id": 3,
  "project_id": 7,
  "start_coordinates": "40.7128,-74.0060"
}

If coordinates are missing or outside the allowed perimeter, the API rejects the request with a 400 error.


Try It Out

Use the API Reference to test a job-costing flow in sandbox:


Common Pitfalls

  • Using disabled jobs — Disabled customers and projects cannot be used for new clock-ins.
  • Forgetting to include customer/project report flags — Job-costing breakdowns only appear when include_customer_reports=true or include_project_reports=true.
  • Expecting switch to appear in reports — Switches create normal work records; they do not create a separate stored record type.
  • Missing geofence coordinates — If a job requires geofencing, include coordinates on clock-in and clock-out.
  • Mixing environments — Sandbox and production have separate customers, projects, time records, and API keys.

Quick Reference

ActionMethodPath
List customersGET/customers
List projectsGET/projects
Clock into a jobPOST/time-records/clock-in
Switch jobsPOST/time-records/clock-in with type: "switch"
Clock outPOST/time-records/clock-out
Company job-costing reportGET/timetrack/company
Employee job-costing reportGET/timetrack/employee/:employee_id
Download reportGET/timetrack/company/download