The public timr REST API v1 replaces the closed beta v0.2.
This guide covers the consumer-visible changes that matter when moving an existing beta integration to the public contract. It focuses on what changed, whether the change is breaking, and what you need to do before switching over.
The migration is more than a base-path update. Authentication, error handling, selected payloads, and several endpoint semantics have changed. Review every section that applies to your integration before switching production traffic.
Timeline
v1 and the v0.2 beta run in parallel during the migration period. Do not wait for the retirement date to begin validation against v1.
- Customer communication date: 06.08.2026
- Public v1 rollout date: 18.08.2026 (planned)
-
v0.2 beta cutoff date: 06.10.2026
Checklist
Use this list as a concrete migration checklist for your integration.
- Switch your base URL from
https://api.timr.com/v0.2/tohttps://api.timr.com/v1/. - Replace
POST /loginwith an OAuth2 flow appropriate for your integration. - Accept RFC 9457 Problem Details responses with the
application/problem+jsonmedia type. - Review the
/working-time-date-spansandworking-time-requestschanges together. - Update affected request parameters and payload models described below.
- Regenerate or revalidate generated clients against the published v1 OpenAPI specification.
Please note: If you have fully completed the migration before 6 October 2026 and no longer need version 0.2, please let us know via info@timr.com. This information helps us plan the parallel operation and reduce the additional effort involved.
Changes
1. Base URL and authentication – Breaking Change
What changed
- The public API base URL is
https://api.timr.com/v1/. -
POST /loginis not part of the public v1 contract. - Public v1 uses OAuth2 access tokens. Send the resulting token as an HTTP bearer token with every API request.
Choose the OAuth2 flow that matches your integration:
-
Client Credentials for machine-to-machine integrations with full access to all resources of the account. Request the
openidandtimrclientscopes. -
Authorization Code with refresh tokens for integrations that act on behalf of a timr user. Request the
openidandoffline_accessscopes.
Create OAuth clients in the timr web application under Administration > Settings > Integrations, for further instructions, see here: OAuth2 authorization for the timr API
Existing tokens issued through the beta login flow remain valid during the transition. Their retirement will be communicated separately, but new integrations must use OAuth2.
Required action
- Update every base URL reference from
/v0.2/to/v1/. - Remove code that submits user credentials to
/login. - Implement token acquisition and renewal for the selected OAuth2 flow.
- Continue to send
Authorization: Bearer <access-token>with each API request.
References
- Protected-Resource metadata:
https://api.timr.com/v1/.well-known/oauth-protected-resource - OAuth2 OpenID Provider:
https://system.timr.com/id/.well-known/openid-configuration - Generic OAuth2 documentation: OAuth 2.0 Overview
- timr OAuth setup guide: OAuth2 authorization for the timr API
2. RFC 9457 Problem Details responses – Breaking Change
What changed
Error responses now use RFC 9457 Problem Details and the application/problem+json media type. The beta code and message response shape, including E0xxx identifiers, is replaced by a structured problem document.
-
typeis a semantic URI that identifies the error category, for examplehttps://errors.timr.com/validation/end-time-required. -
title,status,detail, andinstancedescribe the HTTP problem. -
timestampandtrace_idprovide operational context for support and logging. -
errorscan contain field-level or nested validation details. - Mutation endpoints can now document
409 Conflictand422 Unprocessable Contentwhere those outcomes apply.
Required action
- Accept and parse
application/problem+jsonresponses. - Branch on the HTTP status and stable
typeURI instead of the removedE0xxxcode. - Surface field-level entries from
errorswhen presenting validation feedback. - Log
trace_idwith failed requests so timr support can correlate the request.
Example
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://errors.timr.com/validation",
"title": "Bad Request",
"status": 400,
"detail": "Request validation failed",
"instance": "/working-times",
"timestamp": "<RFC 3339 timestamp>",
"trace_id": "<trace id>",
"errors": [
{
"detail": "must not be null",
"field": "start",
"type": "https://errors.timr.com/validation/not-null"
}
]
}
3. Idempotency – Breaking Change
Affected endpoints
PUT /teams/{team_id}/members/{user_id}PUT /teams/{team_id}/leaders/{user_id}PUT /tasks/{task_id}/leaders/{user_id}PUT /tasks/{task_id}/teams/{team_id}PUT /tasks/{task_id}/users/{user_id}PATCH /tasks/{task_id}/users/{user_id}
What changed
Repeated adds are now idempotent for every endpoint above. Previously, a PUT only created the assignment (201 Created), and repeating it for one that already existed was rejected as an error (400 Bad Request). Now the same PUT returns 201 Created when it creates and 200 OK when the assignment already existed, so a repeated call succeeds instead of failing.
Task-to-user additionally becomes an upsert. In v0.2, its writes were split across two verbs — PUT created the assignment and a separate PATCH updated it. In v1, the single PUT /v1/tasks/{task_id}/users/{user_id} both creates (201 Created) and updates (200 OK), and the PATCH verb is removed. On update, only the properties present in the request body change; omitted properties keep their current value.
Required action
- Treat both
200and201as success for every assignmentPUT. - Update retry logic that previously interpreted duplicate add operations as failures.
- For task-to-user assignments, drop the old
PATCHcall and route both create and update through the singlePUT.
4. Effective working-time date spans – Non-Breaking Response Expansion
Affected endpoints
GET /working-time-date-spans-
GET /working-time-date-spans/{id}
What changed
/working-time-date-spans is now the authoritative public read model for effective date spans, including records created directly and records that became effective through a request workflow. For most integrations, this simplifies the read path: fetch the effective result from one endpoint instead of querying both working-time date spans and working-time requests and reconciling them in the client.
What you need to review
- Use
/working-time-date-spansas the default endpoint whenever your use case is "show me effective date spans of all sick leaves and vacations." - Do not assume that a returned date span was or was not created as part of a request workflow.
- Review deduplication, synchronization, and reconciliation logic if you currently merge date spans with request data or other absence sources.
- If you use
/working-time-requestsonly because approved requests imply effective date spans, move that read to/working-time-date-spans. - If your beta integration currently makes two calls to reconstruct the effective picture, verify whether one
/working-time-date-spanscall is now sufficient.
5. Working-time request endpoints – Breaking Change – Future Plans
Affected endpoints
GET /working-time-requestsGET /working-time-requests/{id}GET /working-time-requests:deleted-
GET /working-times(response body'sworking_time_requestproperty)
What changed
The working-time-requests endpoint family is temporarily not included in the published public v1 release. We are currently working on a larger update to the working-time request functionality, which will also require changes to the related API endpoints.
We plan to make working-time request endpoints available again in a future v1 update. The exact API contract and release date have not yet been finalized.
Until then, request creation, approval state, and workflow history are not available through the public v1 API. In addition, working-time responses no longer expose the working_time_request property.
For integrations that only require the resulting effective working-time records, /working-time-date-spans can be used as the replacement read model. It includes effective records even when they originated from request workflows but does not expose the request lifecycle.
Required action
- Do not migrate request calls by changing only the version in the URL.
- Replace effective-date-span reads with
/working-time-date-spanswhere that matches your use case. - Remove parsing of
WorkingTime.working_time_request. - If your integration depends on request creation, approval state, or workflow history, contact your timr representative before migrating.
6. Deleted-entry filters – Breaking Change
Affected endpoints
GET /working-times:deletedGET /project-times:deletedGET /drive-logs:deleted-
GET /working-time-date-spans:deleted
What changed
The optional query parameter last_modified is renamed to last_modified_after, for consistency with the plain non-deleted endpoints. Its meaning is unchanged: only entries modified after the supplied instant are returned.
Required action
- Rename the query parameter in every deleted-entry synchronization job.
7. Work schedules use explicit weeks – Breaking Change
Affected endpoints
GET /work-schedule-modelsGET /work-schedule-models/{id}GET /users/{id}/work-schedule-model
What changed
The work-schedule model no longer stores weekdays directly on the top-level object. It now contains a weeks array instead.
- A fixed-week schedule contains exactly one week;
week_numberandnamecan be omitted. - An alternating-week schedule contains every configured week; each entry includes a zero-based
week_numberand aname.
Required action
- Read and write weekday targets inside
weeks. - Update strict deserializers and generated clients for the nested model.
- Do not assume that a schedule always contains one week.
Before
{
"type": "daily_target_hours",
"target_duration_weekly_minutes": 2400,
"monday": { "target_duration_minutes": 480 },
"tuesday": { "target_duration_minutes": 480 },
"wednesday": { "target_duration_minutes": 480 },
"thursday": { "target_duration_minutes": 480 },
"friday": { "target_duration_minutes": 480 },
"saturday": { "target_duration_minutes": 0 },
"sunday": { "target_duration_minutes": 0 }
}After
{
"type": "daily_target_hours",
"target_duration_weekly_minutes": 2400,
"weeks": [
{
"week_number": 0,
"name": "Week A",
"monday": { "target_duration_minutes": 480 },
"tuesday": { "target_duration_minutes": 480 },
"wednesday": { "target_duration_minutes": 480 },
"thursday": { "target_duration_minutes": 480 },
"friday": { "target_duration_minutes": 480 },
"saturday": { "target_duration_minutes": 0 },
"sunday": { "target_duration_minutes": 0 }
}
]
}
8. Task budget writes – Breaking Change
Affected endpoints
POST /tasksPATCH /tasks/{id}
What changed
Task create and update payloads use budget_planning_type instead of budget_inherited to control inheritance. Task responses continue to expose budget_inherited.
Required action
- Stop sending
budget_inheritedin task write payloads. - Send
"budget_planning_type": "inherited"when the task should inherit budget settings from its parent. - Continue reading
budget_inheritedfrom responses if your integration uses it.
9. Location and GPS data – Breaking Change
Affected endpoints
/working-times/project-times/drive-logs- All uses of
start_locationandend_location
What changed
Create, update, and response payloads now have distinct location requirements.
- Create payloads require coordinates.
- Update payloads can provide partial location data without coordinates.
- Responses can contain
lat: nullandlon: nullwhen the concrete coordinates were cleaned up under a data-retention policy.
Required action
- Keep sending
latandlonwhen creating a location. - Allow partial location objects in update requests.
- Make response parsing tolerant of null coordinates.
- Use separate validation rules for create, update, and read models.
10. Overtime and surcharge accounts – Breaking Change
Affected endpoints
GET /users/{id}/accounts/{date}
What changed
The separate overtime and allowances properties are replaced by a single overtime_surcharge_accounts property. The new array groups each account by rule and percentage and provides separate overtime and surcharge balances.
Required action
- Replace parsing of
overtimeandallowanceswith iteration overovertime_surcharge_accounts. - Use the nested
ruleandpercentageto identify an account. - Read balance fields from the nested
overtimeandsurchargeobjects.
Response shape
{
"overtime_surcharge_accounts": [
{
"rule": {
"id": "<rule-id>",
"name": "Overtime 50%",
"short_name": "OT50"
},
"percentage": 50,
"overtime": {
"balance_last_period_minutes": 120,
"new_minutes": 30,
"balance_total_minutes": 150
},
"surcharge": {
"balance_last_period_minutes": 60,
"new_minutes": 15,
"balance_total_minutes": 75
}
}
]
}
Comments
0 comments
Article is closed for comments.