Timesheet

Timesheet Reconciliation Corrections

The Timesheet Reconciliation API lets authorised users create or correct time entries in bulk. You can submit a single correction or a batch of corrections in one request.

This article explains who can use the API, what information each correction needs, the rules that are applied, and how to interpret the results.


Who can use it

To submit timesheet reconciliation corrections you must:

  • Be a Financial Administrator, and

  • Be a Timesheet Administrator, and

  • Have the feature Timesheet Corrections enabled for your organisation, and

  • Have the feature Timesheet Reconciliation Correction enabled for your organisation.

If any of these conditions is not met, the request is rejected with 403 Forbidden response and no corrections are saved.


What you can do

  • Submit a single correction – send one correction entry and receive the outcome for that entry.

  • Submit a batch of corrections – send many correction entries at once and receive a per-row outcome plus an overall summary.

Each entry either creates a new correction or updates an existing one. To update an existing correction, include its Record Id. To create a new correction, leave the Record Id empty (or set it to 0).


Information needed for each correction

Each correction entry is made up of the following fields.

Required

Every entry must include all of the following:

  • Date – the date the time relates to.

  • Hours – the number of hours being recorded for the correction.

  • A way to identify the resource – provide either a Resource Ref or an External Ref.

  • A way to identify the work – provide at least one of Project Ref, Alternate Project Ref, or Project (name) for the non-project related work such as 'Leave' etc.

Optional

These fields can be supplied to add more detail or to support specific scenarios:

  • Record Id – the identifier of an existing correction you want to update. To get the Record Id for existing corrections, use the 'Find' end-point to fetch corrections for a date-range. Omit the Record Id to create a new correction.

  • Resource – the resource (person) name.

  • Financial Ref – a financial reference associated with the work.

  • Task – the flex-task the time relates to.

  • Sub Activity – a more granular activity under the task.

  • Local Value – a monetary value for the entry, in local currency.

  • Local Currency Code – the currency for the Local Value.

  • Notes – free-text notes about the correction.


Validation rules

Corrections are checked in two stages.

1. Basic (entry) checks

These are applied first to every entry:

  • A Date must be supplied.

  • Hours must be supplied.

  • The resource must be identifiable by a Resource Ref or an External Ref.

  • The work must be identifiable by a Project Ref, an Alternate Project Ref, or a Project name.

  • RecordId must be greater than or equal to 0.

An entry that fails any of these checks is marked invalid and is not processed further. The reason is returned against that entry.

2. Business checks

Entries that pass the basic checks are then validated against your organisation's data and rules, for example:

  • The RecordId must exist in the system.

  • The resource referenced must exist.

  • The project or work item referenced must exist and be valid for timesheet corrections.

  • The date must fall within a period that is open for correction (periods that have been locked or prior to Capitalization Lock Date cannot be corrected).

  • The Project must be of a type or status that the Timesheet Configuration marks as excluded from corrections.

  • Any supplied currency must be valid.

Entries that fail a business check are marked invalid and the specific reason is returned against the corresponding row. Entries that pass all checks are submitted.

Partial success is supported. When you submit a batch, valid entries are applied even if other entries in the same batch are invalid. You do not need to resubmit the whole batch to fix a single bad row.


Understanding the results

Single correction

The response tells you whether the entry was valid or invalid, and includes any validation messages explaining why an entry was rejected.

Batch of corrections

The response includes an overall summary and a per-row breakdown:

  • Status – the overall outcome of the batch:

    • success – every entry was valid and applied.

    • partial – some entries were valid and applied; others were invalid.

    • failed – no entries were valid; nothing was applied.

  • Total count – the number of entries submitted.

  • Valid count – the number of entries that passed validation and were applied.

  • Invalid count – the number of entries that failed validation.

  • Per-row results – for each entry, whether it was valid, and any validation messages.

  • Correlation Id – a reference for the batch, useful when reviewing or following up on a submission.


Tips for a clean submission

  • Always include Date and Hours on every entry.

  • Provide a Resource Ref or External Ref so the resource can be matched unambiguously.

  • Provide a Project Ref, Alternate Project Ref, or Project name so the work can be matched.

  • Make sure the Date falls within a period that is still open for reconciliation.

  • To update an existing correction rather than create a new one, include its Record Id.

  • When submitting a batch, review the per-row results so you can correct and resubmit only the rows that failed.

Base URL

/rest/api/timesheetreconciliation

Rate Limits

Endpoint

Limit

GET Find

100 requests per 10 seconds

POST single

20 requests per 5 seconds

POST bulk

10 requests per 2 seconds

Rate limit status is returned in response headers:

Header

Description

RateLimit-Limit

Maximum allowed requests in the window

RateLimit-Remaining

Remaining capacity

RateLimit-Reset

Seconds until the window resets


Endpoints

1. Find Correction Records by Date Range{#find}

Retrieves timesheet reconciliation correction records for the specified date range. Both startDate and endDate are mandatory.

HTTP Example

Endpoint: GET /rest/api/timesheetreconciliation/find

cURL Example

curl -X GET "https://organisation.fluid.work/rest/api/timesheetreconciliation/find?startDate=2026-07-01&endDate=2026-07-31&version=3.0" \
  -H "Authorization: Bearer {your_api_token}" \
  -H "Accept: application/json"

Query Parameters:

Parameter

Type

Required

Description

startDate

DateTime

Yes

Start of the date range (inclusive). ISO 8601 or yyyy-MM-dd format.

endDate

DateTime

Yes

End of the date range (inclusive). ISO 8601 or yyyy-MM-dd format.

version

string

No

API version (e.g. "3" or "3.0"). Defaults to latest stable.

fields

string

No

Comma-separated list of fields to include in the response.

expand

string

No

Comma-separated list of custom property expansions.

skip

integer

No

Number of records to skip for pagination. Defaults to 0.

take

integer

No

Number of records to return. Defaults to 50 (max 50).

Response Headers:

Header

Description

Item-Count

Number of records returned in this response

Total-Count

Total number of matching records

Response:

[
  {
    "fields": {
      "recordId": 1042,
      "date": "2026-07-01T00:00:00",
      "resource": "Henry Rogers",
      "resourceRef": "Henry.rogers",
      "externalRef": "EMP-001",
      "project": "Project Solara",
      "projectRef": "ED-EN-1000",
      "alternateProjectRef": null,
      "financialRef": "FR-JC-01",
      "task": null,
      "subActivity": null,
      "hours": 8.0,
      "localValue": 0.0,
      "localCurrencyCode": null,
      "notes": null
    }
  }
]

Response (400 Bad Request) — missing or invalid date parameters:

{
  "message": "startDate and endDate are required and must be valid dates."
}

Response (403 Forbidden):

{
  "message": "You do not have the required permission to fetch timesheet reconciliation corrections."
}

2. Create Single Correction Entry

Creates or updates a single timesheet reconciliation correction entry.

HTTP Example

Endpoint: POST /rest/api/timesheetreconciliation

Request Body:

{
  "fields": {
    "recordId": 0,
    "date": "2026-07-01T00:00:00Z",
    "resourceRef": "Henry.rogers",
    "projectRef": "ED-EN-1000",
    "financialRef": "FR-JC-01",
    "subActivity": "Application Support",
    "hours": 8.0,
    "localValue": 350.00,
    "localCurrencyCode": "GBP",
    "notes": "July correction"
  }
}

cURL Example

curl -X POST "https://organisation.fluid.work/rest/api/timesheetreconciliation?version=3.0" \
  -H "Authorization: Bearer {your_api_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "fields": {
      "recordId": 0,
      "date": "2026-07-01T00:00:00Z",
      "resourceRef": "Henry.rogers",
      "projectRef": "ED-EN-1000",
      "financialRef": "FR-JC-01",
      "subActivity": "Application Support",
      "hours": 8.0,
      "localValue": 350.00,
      "localCurrencyCode": "GBP",
      "notes": "July correction"
    }
  }'

Response — Valid entry:

{
    "fields": {
        "rowIndex": 1,
        "isValid": true,
        "status": "valid",
        "validationMessages": []
    }
}

Response — Validation error:

{
    "fields": {
        "rowIndex": 1,
        "isValid": false,
        "status": "invalid",
        "validationMessages": [
            "This row was skipped as Project 'Vacations' is not a valid Miscellaneous Task as configured in Timesheet Configuration."
        ]
    }
}

Response (403 Forbidden):

{
  "message": "You do not have the required permission to create timesheet reconciliation corrections."
}

3. Create Bulk Correction Entries

Submits multiple timesheet reconciliation correction entries in a single request. Each entry is independently validated and processed. Invalid rows do not prevent valid rows from being saved.

HTTP Example
Endpoint: POST /rest/api/timesheetreconciliation/bulk

Request Body:

[
  {
    "fields": {
      "recordId": 0,
      "date": "2026-07-01T00:00:00Z",
      "resourceRef": "Henry.rogers",
      "projectRef": "ED-EN-1000",
      "hours": 8.0
    }
  },
  {
    "fields": {
      "recordId": 1042,
      "date": "2026-07-02T00:00:00Z",
      "resourceRef": "Henry.rogers",
      "projectRef": "ED-EN-1000",
      "financialRef": "FR-JC-01",
      "hours": 10.5
    }
  },
  {
    "fields": {
      "recordId": 0,
      "date": "2026-07-03T00:00:00Z",
      "resourceRef": "unknown.user",
      "projectRef": "ED-EN-1000",
      "hours": 8.0
    }
  }
]

cURL Example

curl -X POST "https://organisation.fluid.work/rest/api/timesheetreconciliation/bulk?version=3.0" \
  -H "Authorization: Bearer {your_api_token}" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "fields": {
        "recordId": 0,
        "date": "2026-07-01T00:00:00Z",
        "resourceRef": "Henry.rogers",
        "projectRef": "ED-EN-1000",
        "hours": 8.0
      }
    },
    {
      "fields": {
        "recordId": 1042,
        "date": "2026-07-02T00:00:00Z",
        "resourceRef": "Henry.rogers",
        "projectRef": "ED-EN-1000",
        "financialRef": "FR-JC-01",
        "hours": 10.5
      }
    }
  ]'

Response — Partial success:

{
  "status": "partial",
  "totalCount": 3,
  "validCount": 2,
  "invalidCount": 1,
  "correlationId": "12345678-1234-1234-1234-123456789012",
  "results": [
    {
      "fields": {
        "rowIndex": 1,
        "isValid": true
        "status": "valid",
        "validationMessages": [],
      }
    },
    {
      "fields": {
        "rowIndex": 2,
        "isValid": true
        "status": "valid",
        "validationMessages": [],
      }
    },
    {
      "fields": {
        "rowIndex": 3,
        "isValid": false
        "status": "valid",
        "validationMessages": [
          "Row 3: no resource found matching the Resource Ref 'unknown.user'"
        ]
      }
    }
  ]
}

Request Model

The top-level request object wraps a fields object.

{
  "fields": { ... }
}

Field

Type

Required

Description

recordId

integer

No

Id of an existing correction entry to update. Use 0 (or omit) to create a new entry. Must not be negative.

date

DateTime

Yes

Date of the correction entry. Must be after the Capitalization Lock Date.

resourceRef

string

Yes*

Resource reference ID (e.g. "Henry.rogers"). Required unless externalRef is provided.

externalRef

string

Yes*

Resource employee ID. Alternative to resourceRef.

project

string

No

Project display name. Used to resolve miscellaneous task entries when projectRef is absent.

projectRef

string

Yes**

Project External Reference (e.g. "ED-EN-1000").

alternateProjectRef

string

Yes**

Project Alternate Reference.

financialRef

string

No

Financial reference for the time entry (e.g. a cost code or WBS element).

task

string

No

Flex board task title. Used when the correction targets a flex board task entry. Cannot be combined with financialRef.

subActivity

string

No

Sub-activity type (e.g. "Application Support"). Required for non-project entries; must match configured sub-activity values in MetaData.

hours

decimal

Yes

Hours for the correction. Must be greater than zero for new records (recordId = 0).

localValue

decimal

No

Override monetary value in local currency. When supplied alongside localCurrencyCode, bypasses rate-card conversion.

localCurrencyCode

string

No

Currency code for localValue (e.g. "GBP", "USD"). Must be a valid currency code configured in the system.

notes

string

No

Free-text notes attached to the correction entry.

* Either resourceRef or externalRef is required.
** Either projectRef or alternateProjectRef is required (or project for miscellaneous task entries).


Response Models

Returned by both the single create and bulk create endpoints.

Field

Type

Description

status

string

Overall batch status: "success", "partial", "failed", or "error".

totalCount

integer

Total number of records submitted.

validCount

integer

Number of records that passed validation and were saved.

invalidCount

integer

Number of records that failed validation.

correlationId

string

Unique GUID assigned to this request for tracking and log correlation.

results

array

List of Fields, one per submitted row.

status values

Value

Meaning

"success"

All submitted records were valid and saved.

"partial"

Some records were valid and saved; others failed validation.

"failed"

No records were valid; nothing was saved.

"error"

An unexpected processing error occurred.

Per-row result

Field

Type

Description

rowIndex

integer

1-based position of this row within the submitted list.

isValid

boolean

true if the record passed all validations and was saved.

status

string

"valid" or "invalid".

validationMessages

string[]

Array of validation error messages. Empty when valid.

Response for 'Find' endpoint

Returned per record by the GET /find endpoint.

Field

Type

Description

recordId

integer

TimeActual.Id — use this value as recordId when submitting an update for this record.

date

DateTime

Timesheet start date for this correction entry.

resource

string

Resource full name.

resourceRef

string

Resource reference ID.

externalRef

string

Resource employee ID.

project

string

Project or task display name.

projectRef

string

Project external reference.

alternateProjectRef

string

Project secondary external reference.

financialRef

string

Financial reference for the time entry.

task

string

Flex board task title, if applicable.

subActivity

string

Sub-activity type.

hours

decimal

Recorded hours.

localValue

decimal

Stored local monetary value.

localCurrencyCode

string

Local currency code.

notes

string

Notes attached to the entry.


Validation Rules

Validation runs in two phases: request-level (pre-processor) and processor-level.

Phase 1 — Request-Level Validation

Applied before the processor runs. A row that fails any of these checks is marked invalid and excluded from processor execution.

Rule

Error message

date is missing

"Date is required"

hours is missing

"Hours is required"

Neither resourceRef nor externalRef is provided

"Resource Ref or External Ref is required"

None of projectRef, alternateProjectRef, or project is provided

"Project Ref, Alternate Project Ref, or Project is required"

Phase 2 — Processor-Level Validation

Applied for each entry that passes Phase 1.

Rule

Error message fragment

recordId is negative

"Record Id is not valid"

date is on or before the Capitalization Lock Date

"must be later than the Capitalization Lock Date"

Resource not found by resourceRef

"no resource found matching the Resource Ref"

Resource not found by externalRef

"no resource found matching the External Ref"

Resource has no resource plan for date

"resource does not have a valid resourceplan for date"

Project not found by projectRef or alternateProjectRef

"no project found matching the Project Ref"

Project type is excluded from timesheet reconciliation

"project type is excluded from corrections"

Project status prevents new entries

"project status"is excluded from corrections

subActivity is blank for entries that require one

"the subactivity value '' is invalid"

subActivity value is not in the configured list

"the subactivity value '...' is invalid"

Miscellaneous task name not found in configuration

"is not a valid Miscellaneous Task as configured in Timesheet Configuration"

task title not found in flex board tasks

task not found message

task and financialRef both supplied

"Task and Financial Ref cannot both be specified"

hours is zero for a new record (recordId = 0)

"Hours must be greater than zero"

localCurrencyCode is not a valid system currency

"is not a valid currency code"

recordId > 0 and the submitted combination does not match the DB record

"No record found matching."

Duplicate row within the batch (same resource + project + date + finRef + subActivity)

Only the most latest record in the duplicated list is considered for submission, rest all duplicate records are skipped with a duplicate-detected message

Combination Matching for Updates (recordId > 0)

When recordId is greater than zero, the processor looks up the existing correction record by that ID and compares the following fields against what was submitted:

field

date

Resource

Project

subActivity

Task GUID

financialRef

If any field does not match, the row is rejected and the existing record is unchanged. Only hours, localValue, localCurrencyCode, and notes may differ from the stored values — all other fields act as an identity key to confirm the correct record is being updated.


Processing Behaviour

  1. Request validation: Each row is checked against the mandatory field rules..

  2. Duplicate removal: Rows sharing the same (resourceRef, projectRef, date, financialRef, subActivity) key within a single batch are deduplicated. The row that is latest in the duplicated combination list is kept; earlier duplicates are rejected without processing.

  3. Processor execution: All valid entries are submitted.


Versioning

The API supports versioning via the version query parameter:

  • version=3 or version=3.0 — V3 API (current)

  • Omitted — resolves to the latest stable version (currently V3)

POST /rest/api/timesheetreconciliation?version=3.0

Field Filtering

Use the fields query parameter to limit which fields appear in the response:

GET rest/api/timesheetreconciliation/find?startDate=2026-08-01&endDate=2026-08-30&fields=resourceRef,projectRef,recordid

Best Practices

  1. Use the Find endpoint before bulk updates: Call GET /rest/api/timesheetreconciliation/find first to retrieve current recordId values and the exact field values stored in the database. This ensures the combination check passes when you submit updates.

  2. Treat recordId as an opaque identifier: Source it from the 'Find' endpoint.

  3. For updates, only change hours, localValue, localCurrencyCode, or notes: Any change to date, resourceRef, projectRef, financialRef, subActivity, or task on a row with recordId > 0 will fail the combination check and the row will be rejected.

  4. Use the bulk endpoint for multi-row submissions: The bulk endpoint processes all rows in a single batch and returns per-row results. Sending rows individually across multiple single POST calls is less efficient.

  5. Check isValid per row: Iterate results and check isValid to identify rejected rows and their validationMessages.

  6. Handle partial success: The status field signals "success", "partial", or "failed".

  7. Respect the Capitalization Lock Date: Entries with a date on or before the Capitalization Lock Date are always rejected. Verify the lock date before submitting historical corrections.

  8. Miscellaneous task entries: For entries targeting a miscellaneous task rather than a standard project, set the project field to the task display name and leave projectRef and alternateProjectRef empty. Do not include subActivity on miscellaneous task rows.

  9. Flex board task entries: Set the task field to the exact flex board task title. Do not combine task with financialRef..

  10. Use field filtering: Use the fields query parameter to reduce response payload size when only specific fields are needed.

Was this article helpful?