Working with Entry Sheets
Entry Sheets are one of the most important resources in the Journyx API, representing the containers for time, expense, and custom (a.k.a. mileage) data entry. Whether you're building an integration to import timesheets, creating a mobile time-tracking app, or generating custom reports, understanding how to work with Entry Sheets is essential.
This guide will walk you through the Entry Sheets API, explaining the hierarchical data structure, how to find and retrieve sheets, and how to modify them. We'll focus on practical concepts and common patterns while linking to the comprehensive reference documentation where appropriate.
Before diving into Entry Sheets, you should be familiar with:
- Core Concepts - REST API basics, HTTP methods, ETags
- Collections - Filtering, sorting, pagination
- Authentication - How to authenticate your requests
- Creating and Modifying Resources - Using POST and PUT
The Entry Sheet REST API was introduced in Journyx version 13.5.0 (25H2). Ensure your Journyx instance is running this version or later to access these endpoints.
Entry Sheet Types
The Journyx system supports three distinct types of entry sheets, each accessed through its own endpoint:
- Time Sheets (
/api/v1/entry_sheets/time) - For tracking hours worked on projects, with optional punch-in/out times and leave/PTO accruals - Expense Sheets (
/api/v1/entry_sheets/expense) - For tracking monetary expenses and mileage reimbursement - Custom Sheets (
/api/v1/entry_sheets/custom) - For tracking distance traveled or other custom metrics (also called "mileage" or "travel" sheets). Note that thetypefield in sheets (orentry_type) will normally always usemileageto indicate the "Custom Entry" screen type; the terminology around this sheet type has evolved over time.
While each type has some unique fields (for example, only time sheets include accruals and punch times), they all share the same fundamental structure and API patterns. This means once you understand how to work with one type, you can easily work with the others.
The Entry Sheets API (/api/v1/entry_sheets/*) provides full read-write access
for creating and modifying entries. There's also a separate
Entry Records API
(/api/v1/entry_recs/*) that provides read-only access to individual entry
records (time_recs, expense_recs, travel_recs) and is primarily useful for
reporting and analysis.
See the Entry Records tutorial section below.
Listing Entry Sheets
To get started, let's retrieve a list of entry sheets. Like other collections in the Journyx API, entry sheets are accessed via a collection endpoint that returns paginated results.
GET https://example.apps.journyx.com/api/v1/entry_sheets/time
Accept: application/json
Authorization: Basic [credentials]
The response follows the standard collection response structure:
{
"$count": 156,
"$skip": 0,
"$top": 1000,
"response_code": 200,
"success": true,
"uri": "/api/v1/entry_sheets/time",
"results": [
{
"id_sheet": "5583FA6DF48A4B42A470D12CD7984386",
"id_user": "alice",
"type": "time",
"state": "open",
"start_date": "20251103",
"end_date": "20251109",
"total": 40.0,
"pname": "11/03/2025 - 11/09/2025",
"uri": "/api/v1/entry_sheets/time/5583FA6DF48A4B42A470D12CD7984386"
}
// ... more sheets
]
}
It's important to note that when retrieving sheets in a collection, the details
of the rows and cells are not included by default for performance reasons.
The rows array, which contains all the actual entry data (rows/combos and
cells), is only returned when you request it with the $keys parameter set to
$extended or $keys=rows.
Filtering Entry Sheets
You'll often want to filter entry sheets to find specific ones. Here are some common filtering scenarios:
Find all sheets for a specific user:
GET /api/v1/entry_sheets/time?$filter=id_user eq "alice"
Find only open (unsubmitted) sheets:
GET /api/v1/entry_sheets/time?$filter=state eq "open"
Find sheets for a specific date range:
GET /api/v1/entry_sheets/time?$filter=start_date ge "20250101" AND end_date le "20250331"
Combine multiple filters:
GET /api/v1/entry_sheets/time?$filter=id_user eq "alice" AND state eq "open"&$orderBy=start_date desc
All the standard collection operations are available, including $orderBy for
sorting, $skip and $top for pagination, and $keys for controlling the
detail level. See the Collections guide for complete
details on these parameters.
Understanding Entry Sheet Structure
The key to working effectively with Entry Sheets is understanding their hierarchical structure. Entry Sheets organize data in a three-level hierarchy:
Entry Sheet (e.g., week of Nov 3-9, 2025)
└── Rows (unique project + code combinations)
└── Cells (intersection of row and date)
└── Entry Records (individual time_recs)
Let's break down each level:
The Sheet Level
An Entry Sheet represents a time period (typically a week or two weeks) for a specific user. Key properties at the sheet level include:
id_sheet- Unique identifier for the sheetid_user- The user who owns this sheettype- Either "time", "expense", or "mileage"state- Workflow state: "open", "submitted", "approved", etc.start_dateandend_date- The period covered (in YYYYMMDD format)dates- Array of all dates in the periodtotal- Sum of all amounts across all rows and cellsrows- Array of row objects (explained below)columns- Metadata describing available entry fields
For time sheets specifically, there are also:
accruals- Leave bank balances (vacation, sick time, etc.)punchlists- Allocation punch times at the sheet level (one per date)
As mentioned earlier, the rows array is not included by default when
retrieving sheets in a collection. You must request it explicitly using the
$keys parameter. However, it is included when using the individual sheet
(item) endpoint.
The Row Level
A row represents a unique combination of entry column values: project, activity codes, and comment. Each distinct combination creates a separate row. For example:
- Row 1: Project A + Meetings + Regular Pay = 8 hours total across the week
- Row 2: Project A + Development + Regular Pay = 32 hours total across the week
- Row 3: Project B + Meetings + Regular Pay = 5 hours total across the week
Even though Rows 1 and 2 are both for Project A, they're separate rows because they have different activity codes.
Key properties at the row level:
{
"project": "B4DDBCAB098C4F5A8D2458F95F69205F",
"project_name": "T1-00135-0015 Requirements Gathering",
"code0": "2E6B170B797343999D8CE2546880D6A1",
"code0_name": "Development",
"code1": "Regular",
"code1_name": "Regular Time",
"code2": "Billable",
"code2_name": "Billable",
"comment": "Feature implementation",
"total": 40.0,
"read_only": false,
"interim_submitted": false,
"interim_approved": false,
"cells": [
// ... array of cells
],
"punchlists": []
}
The total at the row level is the sum of amounts across all cells in that row.
The read_only flag indicates whether this row can be modified (submitted or
approved entries are typically read-only).
For time entries with job start/stop tracking enabled, rows can also have
punchlists containing punch-in/out times associated with that specific job.
The Cell Level
A cell represents the intersection of a row and a specific date. This is where the actual entry data lives. Cells contain:
date- The specific date (YYYYMMDD format)amount- The hours/amount/distance for that dateids- Array of entry record IDs that contribute to this cell's totalattachments- File attachments (receipts, documents)notes- Diary notes for that date/row combinationlocations- GPS coordinates if location tracking is enabled
Important: Cells can be empty objects {} when there's no data entered for
that specific date. This is completely normal:
{
"cells": [
{}, // Monday - no entry
{
"date": "20251104",
"amount": 8.0,
"ids": ["41BF68FEBAA9420CAF22E5F53516435F"]
}, // Tuesday - 8 hours
{}, // Wednesday - no entry
{
"date": "20251106",
"amount": 8.0,
"ids": ["CCC73AD7102B47D7863098624130F955"]
} // Thursday - 8 hours
]
}
Because the indexes in this array correspond to the dates in the sheet's dates
array, you must maintain the correct order when modifying cells. You should use
an empty object {} for dates with no entries. The length of a cells array
and the dates array should always be the same.
Example: Complete Structure
Here's a simplified example showing the complete hierarchy:
{
"id_sheet": "5583FA6DF48A4B42A470D12CD7984386",
"id_user": "alice",
"type": "time",
"state": "open",
"start_date": "20251103",
"end_date": "20251109",
"dates": [
"20251103",
"20251104",
"20251105",
"20251106",
"20251107",
"20251108",
"20251109"
],
"total": 16.0,
"rows": [
{
"project": "B4DDBCAB098C4F5A8D2458F95F69205F",
"project_name": "Requirements Gathering",
"code0": "Development",
"code1": "Regular",
"code2": "Billable",
"comment": "API implementation",
"total": 16.0,
"cells": [
{}, // Mon
{"date": "20251104", "amount": 8.0, "ids": ["rec1"]}, // Tue
{}, // Wed
{"date": "20251106", "amount": 8.0, "ids": ["rec2"]}, // Thu
{}, // Fri
{}, // Sat
{} // Sun
]
}
]
}
This sheet covers a week (7 dates), has one row (one project/code combination), and the user worked 8 hours on Tuesday and 8 hours on Thursday for a total of 16 hours.
Retrieving a Single Entry Sheet
To get the full details of a specific entry sheet, make a GET request to its item endpoint:
GET https://example.apps.journyx.com/api/v1/entry_sheets/time/5583FA6DF48A4B42A470D12CD7984386
Accept: application/json
Authorization: Basic [credentials]
Single-item responses include additional data not present in collection listings:
rows- The complete array of rows with all cellscolumns- Metadata about available entry fields (project, codes, comment, etc.)gui_options- UI configuration settings (marked as internal-use in the API, detailed reference available separately)approval_plan_info- Information about approval workflowsaccruals- (Time sheets only) Leave bank balancespunchlists- (Time sheets only) Allocation punch times
The response also includes an ETag header that you'll need when modifying the
sheet (explained below).
Creating and Modifying Entry Sheets
Modifying entry sheets follows the standard
PUT pattern used throughout
the Journyx API, but with some important considerations specific to the
hierarchical structure.
Creating and modifying sheets are virtually identical operations (in terms of
the sheet format) but differ in that you use the
POST pattern instead. The
main difference is that if you POST a sheet for a certain user and date range,
and a sheet already exists for them, the server will return a 409 Conflict
error. Also, if you PUT a sheet that doesn't already exist, it will be
accepted and created anyway. So PUT allows both creating and modifying sheets.
Required Headers
All PUT and POST requests must include these headers:
PUT /api/v1/entry_sheets/time/5583FA6DF48A4B42A470D12CD7984386
Content-Type: application/json
Accept: application/json
X-Requested-With: XMLHttpRequest
Origin: https://example.apps.journyx.com
If-Match: "abc123etag456"
Authorization: Basic [credentials]
The If-Match header with the current
ETag is required to
prevent conflicts. You must retrieve the current ETag via a GET request first.
See also the tutorial section on modifying items.
The Modification Payload
When modifying a sheet, you send the complete sheet representation with your changes. The server will process your changes and update the underlying entry records accordingly.
When creating or modifying a sheet, you do not need to send the entire
schema back to the server. Only include the fields you are changing (usually,
rows) along with the required identifiers (id_sheet, etc.). The server will
merge your changes with the existing data.
Example: Adding hours to an existing row
Let's say you have a row for "Project A + Development + Regular" and you want to add 8 hours to Monday:
- GET the current sheet to obtain the ETag and current data
- Locate the row you want to modify in the
rowsarray - Find the cell for Monday (or add one if it's currently an empty
{}) - Update the
amountin that cell - Send the entire modified sheet back via PUT
{
"id_sheet": "5583FA6DF48A4B42A470D12CD7984386",
"rows": [
{
"project": "B4DDBCAB098C4F5A8D2458F95F69205F",
"code0": "Development",
"code1": "Regular",
"code2": "Billable",
"comment": "API work",
"cells": [
{"date": "20251103", "amount": 8.0}, // Added Monday entry
{"date": "20251104", "amount": 8.0, "ids": ["rec1"]},
{},
{"date": "20251106", "amount": 8.0, "ids": ["rec2"]},
{},
{},
{}
]
}
]
}
Example: Creating a new row with entries
To create a completely new row (a new project/code combination), add a new
object to the rows array:
{
"id_sheet": "5583FA6DF48A4B42A470D12CD7984386",
"rows": [
// ... existing rows ...
{
"project": "NewProjectID123",
"code0": "Meetings",
"code1": "Regular",
"code2": "Billable",
"comment": "Client meeting",
"cells": [
{},
{"date": "20251104", "amount": 2.0}, // Tuesday meeting
{},
{},
{},
{},
{}
]
}
]
}
Submitting for Approval
Sheets can also be submitted for approval, if the target user is configured for
an approval workflow, which is indicated in the sheet data with the
can_be_submitted flag.
When saving changes to a sheet with the PUT method, you can simply set the
submit: true field in the main sheet object (sibling to the rows key). The
state field is read-only.
Note that the Data Validation Tool (DVT) settings may prevent submitting a sheet
for approval if there are validation warnings present. Depending on DVT
settings, it may be possible to override this and submit a sheet for approval
anyway despite validation warnings. If DVT settings allow for this, put an
extra_data object in the main sheet object with the following key:
{
... // other sheet keys
"submit": true,
"extra_data": {
// Allow submit despite DVT validation warnings
"submit_with_warnings": true
}
}
Validations and warnings
Several Journyx product features involve applying rules and validations to Entry Sheets, such as the Data Validation Tool (DVT). This can also include data transformation rules applied by custom tools (Professional Services tools). These validations and rules are also applied when saving changes through the Entry Sheet REST API.
These validation warnings are passed back to you via the warnings key in the
API response. Fatal errors are indicated in the errors key.
As noted above, depending on DVT settings it may be possible to allow submitting a sheet for approval despite the presence of validation warnings.
Modifiable vs Read-Only Fields
Not all fields can be modified via the API. Generally:
Modifiable:
- Cell amounts (adding/changing hours, expenses, distances)
- Project and code selections for rows
- Comments
- Notes and locations in cells
- Note: attachments are currently read-only in this API: see attachment details below.
Read-Only:
id_sheet,id_user,typecreate_timestamp,creator,modify_timestamp,modifierstate(can only be changed via specific submission/approval endpoints)total(computed from cell amounts)- The
attachmentsfield of cells is currently read-only in this API but see the note below. - Most fields in
gui_options,approval_plan_info, etc.
Success Response
A successful PUT returns 200 OK with the updated sheet in the response body.
The response includes a new ETag header:
HTTP/1.1 200 OK
ETag: "new-etag-value-xyz789"
If there's a validation error, you'll receive a 400 Bad Request with details
about what went wrong.
Special Considerations
Time Sheets: Punch Times
Time sheets can include punch-in/out times at two different levels:
Sheet-level punchlists (Allocation): One PunchList per date in the period,
containing punch times that aren't tied to a specific job/project. This is for
"allocation" style time entry.
Row-level punchlists (Job Start/Stop): Punch times tied to a specific project/code combination. This is for "job-based" time tracking.
{
"punchlists": [
// Sheet level - one per date
{"punches": []}, // Monday - no punches
{
"punches": [
{
"time_in": "08:00",
"time_in_ISO_8601": "2025-11-04T08:00:00-06:00",
"time_out": "12:00",
"time_out_ISO_8601": "2025-11-04T12:00:00-06:00"
}
]
}
]
}
Read-Only Cells and Rows
When entries have been submitted or approved, they often become read-only. Check
the read_only flag on rows:
{
"read_only": true,
"interim_submitted": true,
"cells": [
/* cells can't be modified */
]
}
Attempting to modify read-only entries will result in a validation error from the server.
Performance: Using $keys
When listing many sheets, use the $keys parameter to control how much data is
returned:
GET /api/v1/entry_sheets/time?$keys=$base
This returns only essential fields without the expensive-to-compute nested
rows, columns, and gui_options data. This is much faster when you only
need to list sheets without their detailed entry data.
For a single sheet where you need all the entry data:
GET /api/v1/entry_sheets/time/12345?$keys=$extended
See the Collections guide for more details
on the $keys parameter.
Deleting sheets
The Entry Sheets REST API does not respond to the DELETE method - it is not
possible to fully delete an entry sheet via the API. You can zero out the
entries in a sheet to effectively clear its contents, but the sheet object
itself remains for auditing and compliance reasons.
Entry Records API
While the Entry Sheets API provides read-write access to the structured sheet/row/cell hierarchy, sometimes you need direct access to the individual entry records. That's where the Entry Records API comes in.
The Entry Records API (/api/v1/entry_recs/time, /api/v1/entry_recs/expense,
/api/v1/entry_recs/custom) is read-only and provides access to the
underlying time_recs, expense_recs, and travel_recs database records.
Use the Entry Records API when you need to:
- Query all time entries for a specific project across all users
- Generate reports that aggregate entry data
- Export entry data for external systems
- Analyze entry patterns and trends
Important: To create or modify entry records, you must use the Entry Sheets API. The Entry Records API is for querying only.
Common Patterns and Examples
Here are some common patterns you'll encounter when working with Entry Sheets:
Find all open sheets for the current week
GET /api/v1/entry_sheets/time?$filter=state eq "open" AND start_date eq "20251103"
Bulk update multiple dates on a row
When modifying your sheet, you can update multiple cells at once in a single PUT
request. Just modify all the relevant cells in the row's cells array.
Add an attachment to a specific cell
It's important to note that currently the attachments array is read-only and
does not contain the actual file itself. In other words, you must use a separate
API call to upload or download attachments. The objects in the attachments
array only contain IDs that you can use to make separate API calls.
We plan to improve the developer experience around dealing with file attachments in a future software release.
Currently, in order to upload an attachment (e.g. a receipt image) to a sheet, or to download the actual file, you must call methods in the legacy SOAP API:
- Use
addAttachmentif you have the actual Entry Record ID (e.g., anid_time_rec). The Entry Record IDs are given in thecellsobjects in theidskey. - Use
addAttachmentToCellto add an attachment by specifying the "combo" (the unique combination of project, codes and comment for an entry row) instead of the Entry Record ID. - Use
getAttachmentsto identify or download attachment files (and/or their metadata) using their Entry Record IDs to identify them. - Use
getAttachmentByEncodedIdto download attachment files or their metadata using the unique identifier assigned to the actual attachment; these are included in theattachmentskey of the sheet'scellarrays.
Query all entries for a project using Entry Records
GET /api/v1/entry_recs/time?$filter=id_project eq "ProjectID123"
This bypasses the sheet structure and gives you direct access to all time records for that project.
Next Steps
Now that you understand how to work with Entry Sheets, explore these related topics:
- Entry Sheets API Reference (Time)
- Complete reference for time sheets
- Entry Sheets API Reference (Expense)
- Complete reference for expense sheets
- Entry Sheets API Reference (Custom)
- Complete reference for custom/mileage sheets
- Entry Records API Reference - Read-only access to entry records
- Collections - Deep dive into filtering, sorting, and pagination
- Core Concepts - Understanding PUT, ETags, and error handling
- Authentication - Managing API authentication
- Programming Examples - Sample code in Python, JavaScript, and C#