Time Entries (Field Time)

The Field Time REST API exposes a clean, versioned surface for reading and writing time entries. It supports fetching a single entry, searching with filter criteria, upserting (combined add or update) one or more entries, and reading supporting lookup values. All endpoints live under /api/ft/public/v1/ and are protected by OAuth bearer tokens. (ex. https://{JonasCustomerSubDomain}.jonasapp.com:8091/api/ft/public/v1/time-entries)

Base path /api/ft/public/v1/
OAuth 2.0 Bearer · jpt + API license validated per request

Authentication

Every /api/ft/public/ endpoint requires a valid bearer token. The controller validates the token's jpt (principal-type) claim and the caller's API license on every request. See Authentication to obtain a token, then send it as:

Authorization: Bearer <access_token>

Get a time entry

GET /api/ft/public/v1/time-entries/{entryId}
200 OK / 404 Not Found

Retrieve a single time entry by its ID. Returns 200 OK with the time entry as a plain JSON object (not wrapped in an outer property), or 404 Not Found when the entry does not exist or is not visible to the caller.

GET /api/ft/public/v1/time-entries/4821
{
  "entryId": 4821,
  "userId": "JSMITH",
  "employee": "Jane Smith",
  "entryDate": "2026-06-05",
  "payroll": "REG",
  "payrollCompany": "01",
  "allocationType": "J",
  "field1": "J-1001",
  "field2": "100",
  "field3": "",
  "earningCode": "REG",
  "earningValue": 8.0,
  "occupation": "CARP",
  "union": "",
  "entryTypeId": 1,
  "status": 1,
  "inputMode": 1,
  "startTime": "07:00",
  "endTime": "15:00",
  "notes": "",
  "crewId": null,
  "reasonId": 0,
  "changeComments": null,
  "timeEntryCustomFields": [],
  "entryChangeHistories": []
}

The response omits SPA-internal fields (grid IDs, approver IDs, attachments, timer/clock metadata, computed hours/qty/dollars, and display-only names) that have no meaning to external integrators.

POST /api/ft/public/v1/time-entries/search
Filter criteria in JSON body

Search accepts its filter criteria as a JSON body — for example a user and a date range. Pagination is supplied with the page and pageSize query parameters.

POST /api/ft/public/v1/time-entries/search?page=1&pageSize=50
{
  "userId": "JSMITH",
  "startDate": "2026-06-01",
  "endDate": "2026-06-07",
  "companyCode": "01",
  "payrollCode": "REG",
  "status": [1, 2]
}
FieldDescription
userIdRequired. Filter entries belonging to a specific user.
startDateOptional. Start of the date range (inclusive). Omit for no lower bound.
endDateOptional. End of the date range (inclusive). Omit for no upper bound.
companyCodeOptional. Restrict results to a specific payroll company.
payrollCodeOptional. Restrict results to a specific payroll/earning code.
statusOptional. Array of entry-status codes to include. See entry statuses.
Query parameterDescription
page1-based page number. Defaults to 1.
pageSizeResults per page. Defaults to 50, maximum 500.

The response is a pagination envelope. entries uses the same field set as Get a time entry.

{
  "page": 1,
  "pageSize": 50,
  "totalCount": 2,
  "entries": [
    { "entryId": 4821, "userId": "JSMITH", "entryDate": "2026-06-05", "...": "..." },
    { "entryId": 4830, "userId": "JSMITH", "entryDate": "2026-06-06", "...": "..." }
  ]
}

Upsert time entries

PUT /api/ft/public/v1/time-entries
Add or update, batched in a single call

The upsert endpoint accepts a batch of one or more entries in a single request — it handles both creating and updating. Entries without an entryId (or with entryId: 0) are created; entries with an existing entryId are updated.

The request below is an example only. Any entry may also include the optional fields (such as occupation, startTime/endTime, notes, crewId, and entryId for updates) in addition to the fields shown.

{
  "userId": "JSMITH",
  "entries": [
    {
      "userId": "JSMITH",
      "employee": "00001",
      "payrollCompany": "LC",
      "clientCompany": "LC",
      "country": "CA",
      "union": "UN01",
      "payroll": "WK.5",
      "earningCode": "H01",
      "earningValue": 8.25,
      "entryDate": "2026-08-24",
      "allocationType": "J/C",
      "field1": "*LYD1",
      "field2": "00000",
      "field3": "1A",
      "ppEnd": "2026/08/28",
      "inputMode": 1
    }
  ]
}

The request body has two batch-level fields:

FieldDescription
userIdRequired. The user the batch is submitted for.
entriesRequired, non-empty array of entry objects. Each entry accepts the required and optional fields listed below.

Required entry fields. For a web-import create request, each entry requires the fields below. Codes must match values configured for the target Jonas customer.

Entry fieldRequirement and format
userIdRequired. User ID as a string; normally matches the batch-level userId.
employeeRequired. Employee code as a string.
payrollCompanyRequired. Payroll company code as a string.
clientCompanyRequired. Client company code as a string.
countryRequired. Two-character country code, such as CA.
payrollRequired. Payroll code as a string.
earningCodeRequired. Earning code as a string.
earningValueRequired. Hours or quantity as a JSON number.
entryDateRequired. Entry date in YYYY-MM-DD format.
allocationTypeRequired. Allocation type code as a string, such as J/C.
field1, field2, field3Required. Allocation segment values as strings.
ppEndRequired. Pay-period end date in YYYY/MM/DD format.
inputModeRequired. Integer input-mode code. See input modes.

Optional entry fields. These fields may be included on any entry, for both creates and updates unless noted otherwise.

Entry fieldDescription
entryIdSend an existing entry ID to update that entry; omit it (or send 0) to create a new entry.
idA client-generated local/offline identifier (e.g. a UUID) for correlating batched or offline-queued entries with their result. Echoed back as localId in the corresponding upsert response entry. If omitted on a create, the server assigns a UUID.
countryOptional on updates only; on web-import creates it is required (see required fields above). Two-character country code, such as CA.
unionUnion code as a string. Send an empty string when the employee is non-union.
jobUnionJob-level union code override, when the job's union differs from the employee's.
occupationOccupation (trade) code as a string.
jobOccupationJob-level occupation code override, when the job's occupation differs from the employee's.
entryTypeIdInteger entry-type code. See entry types.
startTime, endTimeStart and end times in HH:mm 24-hour format, for example 07:00.
breakDetails, breakTimeBreak detail and break time values, for entries that record unpaid breaks.
notesFree-text note attached to the entry.
field1descr, field2descr, field3descrDescriptions for the allocation segment values in field1–field3.
equipmentEquipment code, for entries allocated to equipment.
eqpCostItemEquipment cost-item code, used together with equipment.
ppStart, ppEndPay-period start and end dates in YYYY/MM/DD format. On updates, both must be sent and must together match a web-enabled pay period for the entry's payroll. (ppEnd is also required on creates; see required fields above.)
certifiedPayrollCertified payroll code, for jobs subject to certified payroll reporting.
crewIdInteger crew ID. See crews.
reasonIdInteger change-reason code, used when updating an existing entry. See change reasons.
changeCommentsFree-text comment recorded in the entry's change history when updating.

Fields not listed above — status, display-only names and descriptions (employeeName, earningCodeDesc, entryTypeName, statusName, crewName, crewDescription), computed values (hours, qty, dollars, breakTotal, breakHour, timeEntriesRoundedTime), SPA-internal identifiers (gridId, parentId, approverId, clockTimerEntryId, timerColor, changeDetails), and entryChangeHistories — are server-computed or SPA-internal and are ignored when sent on upsert.

The response returns one result per submitted entry, in the same order, rather than the saved entry objects themselves:

{
  "entries": [
    {
      "entryId": 4821,
      "localId": "f163b4d2-5ccb-4e4b-9c3b-34cddd96aa3b",
      "success": true,
      "message": null,
      "entryChangeHistories": []
    }
  ]
}
FieldDescription
entryIdThe created or updated entry's ID.
localIdEchoes the client-supplied id, if one was sent; otherwise contains the server-generated UUID assigned to a create that omitted id.
successDerived: true when entryId > 0 and message is empty.
messageFailure detail for this entry, if any.
entryChangeHistoriesChange history entries recorded as a result of this save.

Attachments and custom fields are not returned by upsert (v1 scope). Use Get a time entry to read back full entry details after an upsert. There is no plain unfiltered listing — use search to retrieve sets of entries.

Lookups

Supporting reference data used to populate pickers and validate the fields above. All lookup endpoints require the same bearer token as the time entry endpoints.

Entry statuses

GET /api/ft/public/v1/lookups/entry-statuses
Returns 200 OK

Returns all time entry lifecycle status codes, for use with the search status filter.

Entry types

GET /api/ft/public/v1/lookups/entry-types
Returns 200 OK

Returns configured entry types, for use with the entryTypeId field.

Input modes

GET /api/ft/public/v1/lookups/input-modes
Returns 200 OK

Returns valid time entry input modes, for use with the inputMode field.

The three lookups above share the same code/name shape:

[
  { "code": 1, "name": "Approved" },
  { "code": 2, "name": "Pending" }
]

Change reasons

GET /api/ft/public/v1/lookups/change-reasons
Returns 200 OK

Returns valid change reason codes, for use with the reasonId field.

Crews

GET /api/ft/public/v1/lookups/crews
200 OK / 400 Bad Request

Returns crew IDs and names visible to a user within a company, for use with the crewId field.

GET /api/ft/public/v1/lookups/crews?company=01&user=JSMITH&search=framing
Query parameterDescription
companyRequired. Payroll company code to scope crews to.
userRequired. User whose visible crews are returned.
searchOptional. Filters crews by name or description (contains, case-insensitive).
[
  { "crewId": 12, "name": "Framing Crew A", "companyCode": "01" }
]

Custom fields

GET /api/ft/public/v1/lookups/custom-fields
200 OK / 400 Bad Request

Returns custom field definitions configured for a company.

GET /api/ft/public/v1/lookups/custom-fields?company=01
Query parameterDescription
companyRequired. Payroll company code to scope custom fields to.
[
  {
    "fieldId": 3,
    "name": "PO Number",
    "fieldType": 1,
    "active": true,
    "allowedValues": [
      { "id": 1, "shortForm": "PO1", "description": "Purchase Order 1", "isDefault": false }
    ]
  }
]

Errors

Errors are returned as RFC 7807 problem detail documents. See REST API Overview → Error responses for the common status codes and shape.