Aller au contenu

Changelog

Changes to the API contract that an existing integration may notice. Additive changes (new endpoints, new optional properties) are not listed here, except when they come with a behaviour an integration should know about.

PUT /api/v1/agent-segments/{segmentId}: a later revision renames the session

Section intitulée « PUT /api/v1/agent-segments/{segmentId}: a later revision renames the session »

On a revision that is accepted (greater than the stored one, same session, segment not detached and not locked by a submission), description, taskId and tagIds of the body are now applied to the existing entry instead of being ignored. An integration that resends the same mapping on every revision sees no change. What to know:

  • description absent or null keeps the stored one. taskId absent keeps the task, an explicit null clears it. tagIds absent or null keeps the tags, [] removes them, a list replaces them.
  • projectId is immutable: a value different from the stored one (a missing projectId counts as different from a stored project) answers 422 with an error on projectId and the code immutable, and nothing in the revision is written. Open a new segment to change project.
  • The applied values are validated like at creation: description at most 2000 characters (too_long), a changed taskId must belong to the entry’s project (invalid, or required on an entry without project): both 422. Tag ids that no longer exist are ignored rather than refused (a heartbeat never fails because a tag was deleted); the response lists the tags applied.
  • An entry locked by a timesheet submission still answers 409 entry_locked; a detached segment 409 segment_detached. A finalised segment that is not detached can still be renamed (finalisation is not a validation), but its end cannot move (409 segment_finalized).
  • The 200 response is the up-to-date entry (description, taskId, taskName, tagIds): read it to know that the server applied the rename. Replay and revision rules are unchanged (exact replay is a no-op, a reused number with another content is 409 agent_revision_conflict). A taskId absent (keep) and taskId: null (clear) are different requests: the same revision sent once each is 409 agent_revision_conflict.

Release “agent provenance and entry write coordination”. Everything below is documented in full on Agent provenance and Errors; this entry lists what an existing integration may notice.

New refusals on writes: 503 workspace_busy, 409 entry_id_conflict, 409 submission_scope_changed

Section intitulée « New refusals on writes: 503 workspace_busy, 409 entry_id_conflict, 409 submission_scope_changed »

Every write that touches time entries, timesheet lines or submissions now takes a short per-workspace coordination lock, so that a timesheet submission never sees half of a concurrent edit.

  • 503 service_unavailable with detail: "workspace_busy" and a Retry-After: 1 header when the lock is not obtained within the wait budget (5 s by default). Nothing was written: retry exactly the same request after Retry-After, with exponential back-off and jitter. It applies to the /api/v1 entry and timer routes as well as to the internal ones. If your HTTP client treats every 5xx as a hard failure, make an exception for this code.
  • 409 conflict with detail: "entry_id_conflict" when an entry created with a client-chosen id (bulk upsert) was created concurrently by another request and is visible to you. Re-read the entry; sending the same id again takes the update branch.
  • 409 conflict with submission_scope_changed when the entries selected by a timesheet submission changed while it locked them. Nothing was submitted.

Self-hosted deployments tune the wait budget with ENTRY_LOCK_WAIT_SECONDS (default 5, from 1 to 60).

Entries have an origin; agents can declare themselves

Section intitulée « Entries have an origin; agents can declare themselves »

Every entry now exposes an immutable origin (web, api, agent, agent_session, import_toggl, import_tickr, calendar, and unknown for entries created before the release). With an API key, an entry is api unless you send the Tickr-Origin: agent and Tickr-Agent: <name>/<version> headers (then agent; Tickr-Origin: agent without Tickr-Agent answers 422 agent_required, field Tickr-Agent). Two new routes record token usage and automatic agent sessions: PUT /api/v1/entries/{id}/agent-usage and PUT /api/v1/agent-segments/{id} (the latter answers 404 unless the deployment enables agent sessions). Entries of the agent_session origin may be excluded from work-time totals by the workspace’s counting policy, and can neither be resumed as a timer nor split — see Agent provenance.

Entry create, update, bulk upsert and timer stop now refuse an entry spanning more than 2 147 483 647 seconds (about 68 years) with 422 invalid_value (field stoppedAt, or items[i].stoppedAt for a bulk upsert). Starting a timer, or editing an entry so that it stays running, with a start older than that answers 422 invalid_value on startedAt: such an entry could never be stopped.

A timer running when a member is removed or leaves is stopped by the exit. A write of that member (timer start, entry create or edit, bulk upsert, agent segment) that was queued behind the exit answers 401 and writes nothing.

These routes belong to the web app (session authentication, not available with an API key):

  • PUT /api/workspaces/me/branding takes three new optional properties, phone, registrationNumber and vatNumber (trimmed, 50 characters at most, 422 max_length beyond). The PUT is a full replace: a script that sends only the previous properties clears the new ones.
  • POST /api/tags answers 403 tags_creation_disabled to a plain member when the workspace setting “members can create tags” is off.
  • GET /api/auth/me returns agentSegmentsEnabled, the deployment flag the web app reads to show or hide the agent-session interface.
  • The dashboard, the agent-activity report and the entry exports accept origins (agentSessionId is accepted only by the agent-activity report and the entry exports; the dashboard ignores it); the exports append ten agent columns after the existing ones (the positions of the existing columns do not change).

Migrating: nothing breaks for an integration that already retries 5xx and 409 responses sensibly. If yours does not, add the 503 workspace_busy retry rule above before relying on timesheet validation in a workspace your tool writes to.

In 422 validation responses, when the offending value is a JSON body property, errors[].field is now its name as you sent it, in camelCase, on every endpoint. List items keep their JSON path (items[0].projectId).

Errors about something other than a body property keep naming it where you sent it (see Errors): a query parameter by its documented name (summary[breakdown_by]), a group of query parameters by the group name (rounding for rounding[grain] / rounding[mode], range for from / to), a request header by the header name (Tickr-Origin, Tickr-Agent), and an import blocker by its location in the uploaded file (manifest.schema_version, projects.visibility).

Until now the spelling depended on the handler: entry and timer endpoints returned snake_case (project_id, task_id, stopped_at), some validations returned the C# property name (StartedAt, Role), others camelCase.

Before Now Where
project_id, task_id, stopped_at projectId, taskId, stoppedAt Create / update entry, bulk upsert, start / stop timer (internal and /api/v1)
Items[0].project_id items[0].projectId POST /api/v1/entries:bulk (missing project)
project_id, task_id (flat, one error per offending id) items[n].projectId, items[n].taskId (one error per offending item) POST /api/v1/entries:bulk (item referencing a project outside the workspace, or a task outside the workspace or in another project)
StartedAt, StoppedAt, Email, Name… startedAt, stoppedAt, email, name… Requests validated by a schema rule
Role, ExpiresInDays role, expiresInDays API key creation
hourly_rate, default_hourly_rate hourlyRate, defaultHourlyRate Rate endpoints, clients
preferred_language, default_project_visibility, view_config preferredLanguage, defaultProjectVisibility, viewConfig Preferences, workspace settings, saved reports

Unchanged: the code of each error, the type URI, and the field of errors about query parameters, headers and import files.

Bulk upsert references: in POST /api/v1/entries:bulk, an item that references a project outside the workspace, or a task outside the workspace or in another project, now gets its own error at its JSON path (items[3].projectId, items[7].taskId), like every other bulk error. They used to be reported once per offending id, without the item index. When several items carry the same unknown id, each of them is reported. At most 100 such errors are returned, in item order; beyond that, one last error with field items and code too_many_errors says how many were omitted.

PATCH /api/v1/entries/{id}: when the update would put the stop before the start and you sent only startedAt, the error now names startedAt (it named stoppedAt, a property you had not sent). It still names stoppedAt when you sent stoppedAt.

Migrating: if you matched field against the old spelling, compare against the camelCase name — or, to stay compatible with both, compare ignoring case and separators (task_id is taskId). Matching on code is unaffected.