> ## Documentation Index
> Fetch the complete documentation index at: https://help.gorelo.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Partially updates a time entry. Send only the fields you want to change.

> Every field is optional and a field left out keeps its current value, so a body of `{}`
is accepted and returns the entry unchanged. Re-supplying a value the entry already holds is a
no-op: nothing is written, the entry's UpdatedOn does not move and no contract hours move.
The response is the updated entry, in the same shape `GET /v1/time-entries/{timeEntryId}` returns.


StartedOn, EndedOn and ActualHours are merged with the entry's current values before the same
any-two-of-three rule the create applies. Send two and the third is derived from those two;
send all three and they must agree. Send StartedOn or EndedOn alone and the other instant is
kept, so the duration changes with it; send ActualHours alone and StartedOn is kept and
EndedOn moves, so the duration is exactly what was sent.


Whenever the duration, work type, billing role, billable status or service line changes, the
entry is re-priced exactly as a new entry with the merged values would be: the work type's
minimum and increment rounding, the role's rate and the contract's terms are applied afresh,
and the hours the entry consumes from a BlockHours or LimitedHours contract are returned to
the old contract and drawn from the new. UserId, BillingRoleId, WorkTypeId, ServiceLineId
and BillableStatusId are validated the same way the create validates them.


The ticket an entry is logged against cannot be changed, and neither can its attachments: a
body carrying `TicketId` or `Attachments` is rejected with 400, as any unknown field
is. To move time to another ticket, delete the entry and log it against the other ticket.


Only a time entry that is still open can be changed. One that has been approved, completed or
invoiced is rejected with 409, as is one that is void. The ticket's own status does not matter:
time on a closed ticket that has not yet been billed can still be corrected here, exactly as the
Approve & Invoice flow corrects it, so there is nothing to reopen first. Reverse the approval
and the same request succeeds. Note that deleting such an entry is still refused while its
ticket is closed - correcting time and removing it are not the same act.




## OpenAPI

````yaml /api-reference/openapi.json patch /v1/time-entries/{timeEntryId}
openapi: 3.0.4
info:
  title: Public API
  description: Gorelo Public API documentation.
  version: 1.0.0
servers:
  - url: https://api.usw.gorelo.io
    description: US
  - url: https://api.aue.gorelo.io
    description: Australia
security:
  - ApiKeyAuth: []
tags:
  - name: Attachments
    description: "Uploading a file, for anything that can carry one. One endpoint rather than one per record\r\ntype, because the upload itself is identical - only the record it is checked against differs."
  - name: Alerts
    description: Alerts management operations
  - name: Assets
    description: Assets management operations
  - name: Billing
    description: Billing management operations
  - name: Tickets
    description: Tickets management operations
  - name: Clients
    description: Clients management operations
  - name: Contacts
    description: Contacts management operations
  - name: Deletions
    description: Deletions management operations
  - name: Documents
    description: Documents management operations
  - name: Forms
    description: Forms management operations
  - name: Organization
    description: Organization management operations
  - name: Projects
    description: Projects management operations
paths:
  /v1/time-entries/{timeEntryId}:
    patch:
      tags:
        - Billing
        - Tickets
      summary: Partially updates a time entry. Send only the fields you want to change.
      description: "Every field is optional and a field left out keeps its current value, so a body of `{}`\r\nis accepted and returns the entry unchanged. Re-supplying a value the entry already holds is a\r\nno-op: nothing is written, the entry's UpdatedOn does not move and no contract hours move.\r\nThe response is the updated entry, in the same shape `GET /v1/time-entries/{timeEntryId}` returns.\r\n\n\r\nStartedOn, EndedOn and ActualHours are merged with the entry's current values before the same\r\nany-two-of-three rule the create applies. Send two and the third is derived from those two;\r\nsend all three and they must agree. Send StartedOn or EndedOn alone and the other instant is\r\nkept, so the duration changes with it; send ActualHours alone and StartedOn is kept and\r\nEndedOn moves, so the duration is exactly what was sent.\r\n\n\r\nWhenever the duration, work type, billing role, billable status or service line changes, the\r\nentry is re-priced exactly as a new entry with the merged values would be: the work type's\r\nminimum and increment rounding, the role's rate and the contract's terms are applied afresh,\r\nand the hours the entry consumes from a BlockHours or LimitedHours contract are returned to\r\nthe old contract and drawn from the new. UserId, BillingRoleId, WorkTypeId, ServiceLineId\r\nand BillableStatusId are validated the same way the create validates them.\r\n\n\r\nThe ticket an entry is logged against cannot be changed, and neither can its attachments: a\r\nbody carrying `TicketId` or `Attachments` is rejected with 400, as any unknown field\r\nis. To move time to another ticket, delete the entry and log it against the other ticket.\r\n\n\r\nOnly a time entry that is still open can be changed. One that has been approved, completed or\r\ninvoiced is rejected with 409, as is one that is void. The ticket's own status does not matter:\r\ntime on a closed ticket that has not yet been billed can still be corrected here, exactly as the\r\nApprove & Invoice flow corrects it, so there is nothing to reopen first. Reverse the approval\r\nand the same request succeeds. Note that deleting such an entry is still refused while its\r\nticket is closed - correcting time and removing it are not the same act.\r\n"
      operationId: patch_v1_time-entries_timeEntryId
      parameters:
        - name: timeEntryId
          in: path
          description: Time entry to update.
          required: true
          schema:
            type: integer
            format: int64
      requestBody:
        description: Fields to change.
        content:
          application/json-patch+json:
            schema:
              $ref: '#/components/schemas/UpdateTimeEntryCommand'
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateTimeEntryCommand'
          text/json:
            schema:
              $ref: '#/components/schemas/UpdateTimeEntryCommand'
          application/*+json:
            schema:
              $ref: '#/components/schemas/UpdateTimeEntryCommand'
      responses:
        '200':
          description: OK
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/TimeEntryModelBaseResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/TimeEntryModelBaseResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/TimeEntryModelBaseResponse'
        '400':
          description: Bad Request
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
        '401':
          description: Unauthorized
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
        '404':
          description: Not Found
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
        '409':
          description: Conflict
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
        '500':
          description: Internal Server Error
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/ObjectBaseResponse'
components:
  schemas:
    UpdateTimeEntryCommand:
      type: object
      properties:
        StartedOn:
          type: string
          description: "When the work started, as a single UTC instant.\r\n            \r\nThe three time fields are merged with the entry's current values before the same\r\nany-two-of-three rule the create applies. Send two of them and the third is derived from\r\nthose two alone; send all three and they must agree. Send StartedOn or EndedOn alone and the\r\nother instant is kept, so the duration changes with it. Send ActualHours alone and StartedOn\r\nis kept and EndedOn moves, so the duration is exactly what was sent. Sending null is the same\r\nas omitting it - neither instant is clearable, since an entry always has an interval."
          format: date-time
          nullable: true
          example: '2026-04-28T09:00:00.0000000+00:00'
        EndedOn:
          type: string
          description: "When the work finished, as a single UTC instant. Must be later than the StartedOn in force\r\nafter the merge. See `StartedOn` for how the three time fields combine. Sending null is\r\nthe same as omitting it."
          format: date-time
          nullable: true
          example: '2026-04-28T10:30:00.0000000+00:00'
        ActualHours:
          type: number
          description: "Duration worked, in decimal hours. Must be greater than 0. The work type's minimum and\r\nincrement rounding is applied on top of it, as on create. See `StartedOn` for how the\r\nthree time fields combine."
          format: double
          nullable: true
          example: 1.5
        BillableStatusId:
          type: integer
          description: "Billable status for this entry: 1 = Billable, 2 = No charge, 3 = Non-billable; any other\r\nvalue is rejected."
          format: int32
          nullable: true
          example: 1
        UserId:
          type: integer
          description: "User whose time this is. Validated against the service provider. Changing it re-stamps the\r\nentry's cost rate from the new user's own rate, as the app does."
          format: int64
          nullable: true
          example: 8842
        BillingRoleId:
          type: integer
          description: "Billing role to charge at. Validated against the service provider's active roles. Get valid\r\nids from GET /v1/billing-roles."
          format: int64
          nullable: true
          example: 1742
        WorkTypeId:
          type: integer
          description: "Work type to apply. Determines the rate multiplier and the minimum/increment rounding applied\r\nto the logged duration. Validated against the service provider's active work types. Get\r\nvalid ids from GET /v1/work-types."
          format: int64
          nullable: true
          example: 2432
        ServiceLineId:
          type: integer
          description: "Service line to bill this time against.\r\n            \r\nOmit it and the entry keeps the service line it has. Send it as null to take the entry off\r\nits contract outright, which also suppresses the automatic contract assignment that would\r\notherwise happen when the time is approved. When supplied, the service line must belong to\r\nthe entry's client, and its contract must be active today and cover labour. Moving an entry\r\nbetween contracts returns the hours it consumed to the old one and draws them from the new."
          format: int64
          nullable: true
          example: 4720
        Comment:
          type: string
          description: >-
            Free-text note describing the work. Send an empty string to clear
            it.
          nullable: true
          example: Replaced faulty NIC
        Distance:
          type: number
          description: >-
            Distance travelled for this work, when the service provider bills
            mileage.
          format: double
          nullable: true
          example: 12.5
      additionalProperties: false
      description: "Partial update of a time entry. Every field is optional and only the fields present in the body\r\nare applied; a field left out keeps its current value. The ticket an entry is logged against and\r\nits attachments cannot be changed here, so neither is a member: a body carrying either is\r\nrejected as malformed, the same way every other endpoint treats a field it does not know."
    TimeEntryModelBaseResponse:
      type: object
      properties:
        StatusCode:
          $ref: '#/components/schemas/HttpStatusCode'
        IsSuccess:
          type: boolean
        Data:
          $ref: '#/components/schemas/TimeEntryModel'
        DataContext:
          type: object
          additionalProperties: false
          nullable: true
          example: null
        Notifications:
          type: array
          items:
            $ref: '#/components/schemas/Error'
      additionalProperties: false
    ObjectBaseResponse:
      type: object
      properties:
        StatusCode:
          $ref: '#/components/schemas/HttpStatusCode'
        IsSuccess:
          type: boolean
        Data:
          nullable: true
        DataContext:
          $ref: '#/components/schemas/ErrorDataContext'
        Notifications:
          type: array
          items:
            $ref: '#/components/schemas/Error'
      additionalProperties: false
    HttpStatusCode:
      enum:
        - 100
        - 101
        - 102
        - 103
        - 200
        - 201
        - 202
        - 203
        - 204
        - 205
        - 206
        - 207
        - 208
        - 226
        - 300
        - 301
        - 302
        - 303
        - 304
        - 305
        - 306
        - 307
        - 308
        - 400
        - 401
        - 402
        - 403
        - 404
        - 405
        - 406
        - 407
        - 408
        - 409
        - 410
        - 411
        - 412
        - 413
        - 414
        - 415
        - 416
        - 417
        - 421
        - 422
        - 423
        - 424
        - 426
        - 428
        - 429
        - 431
        - 451
        - 500
        - 501
        - 502
        - 503
        - 504
        - 505
        - 506
        - 507
        - 508
        - 510
        - 511
      type: integer
      format: int32
    TimeEntryModel:
      type: object
      properties:
        Id:
          type: integer
          description: Identifier of the time entry.
          format: int64
          example: 88214
        Ticket:
          $ref: '#/components/schemas/NumberedReferenceModel'
        Task:
          $ref: '#/components/schemas/NumberedReferenceModel'
        User:
          $ref: '#/components/schemas/CodeModel'
        StartedOn:
          type: string
          description: When the work started, in UTC.
          format: date-time
          nullable: true
          example: '2026-04-28T09:00:00.0000000+00:00'
        EndedOn:
          type: string
          description: When the work finished, in UTC.
          format: date-time
          nullable: true
          example: '2026-04-28T10:30:00.0000000+00:00'
        ActualHours:
          type: number
          description: >-
            Duration actually logged, in decimal hours — simply the span between
            StartedOn and EndedOn.
          format: double
          nullable: true
          example: 1.5
        AdjustedHours:
          type: number
          description: "Billed duration, in decimal hours: the logged duration after the work type's minimum and\r\nincrement rounding.\r\n            \r\nWhen no rounding applied — the logged duration already met the minimum and landed on a\r\nwhole increment — this equals `ActualHours`, so it is always the figure the\r\nentry is billed at. Null only when the entry has no StartedOn and EndedOn to bill from."
          format: double
          nullable: true
          example: 1.75
        BillableStatus:
          $ref: '#/components/schemas/CodeModel'
        BillingRole:
          $ref: '#/components/schemas/CodeModel'
        WorkType:
          $ref: '#/components/schemas/CodeModel'
        ServiceLine:
          $ref: '#/components/schemas/CodeModel'
        Comment:
          type: string
          description: Free-text note describing the work.
          nullable: true
          example: Replaced faulty NIC
        Distance:
          type: number
          description: >-
            Distance travelled for this work, when the service provider bills
            mileage.
          format: double
          nullable: true
          example: 12.5
        Attachments:
          type: array
          items:
            $ref: '#/components/schemas/TimeEntryAttachment'
          description: Files attached to this entry.
        CreatedOn:
          type: string
          description: When the entry was created, in UTC.
          format: date-time
          nullable: true
        UpdatedOn:
          type: string
          description: >-
            When the entry was last changed, in UTC. Null if it has never been
            changed.
          format: date-time
          nullable: true
      additionalProperties: false
      description: Time logged against a ticket.
    Error:
      type: object
      properties:
        Code:
          pattern: ^[0-9]{6}$
          type: string
          description: 6-digit MMTTNN error code. See the Public API error-code registry.
          example: '070101'
        Message:
          type: string
        PropertyName:
          type: string
          nullable: true
        ActionHint:
          type: string
          nullable: true
        DocUrl:
          type: string
          nullable: true
      additionalProperties: false
    ErrorDataContext:
      type: object
      properties:
        TraceId:
          type: string
          description: Request correlation id. Quote this when reporting a problem.
          nullable: true
      additionalProperties: false
      description: "The `DataContext` of an error response: carries the correlation id for the failed\r\nrequest."
    NumberedReferenceModel:
      type: object
      properties:
        Id:
          type: string
          description: Identifier of the referenced ticket or task.
          format: uuid
          example: e5fb2939-3bd7-4893-8b47-a287003d5b8a
        Number:
          type: string
          description: "Number as shown in the app, including any prefix. A string rather than an integer — task\r\nnumbers are not purely numeric."
          example: T-1042
        Title:
          type: string
          description: Title of the referenced ticket or task.
          example: Laptop will not boot after update
      additionalProperties: false
      description: "A pointer to the ticket or task a record hangs off: the id to fetch it by, plus the number and\r\ntitle a person recognises it by. Kept separate from PublicCodeModel because a ticket has both a\r\nnumber and a title, and neither one alone identifies it to a reader."
    CodeModel:
      type: object
      properties:
        Id:
          type: integer
          description: >-
            Numeric identifier of the coded value (e.g. status, priority, type,
            or source id).
          format: int64
          example: 1
        Name:
          type: string
          description: Display name of the coded value.
      additionalProperties: false
      description: >-
        A coded value, such as a status, priority, type or source, as an id and
        a display name.
    TimeEntryAttachment:
      type: object
      properties:
        Name:
          type: string
          description: File name as it should be displayed.
          example: switch-photo.jpg
        Url:
          type: string
          description: Location the file can be fetched from.
          example: https://files.example.com/switch-photo.jpg
      additionalProperties: false
      description: A file attached to a time entry. Same shape as comment attachments.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      description: >-
        Your Gorelo API key. See [Generate API key](/generate-api-key). If your
        data is hosted in Australia, click the base URL at the top of this page
        and select `https://api.aue.gorelo.io`.
      name: X-API-Key
      in: header

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.