API
REST API for the Timebutler HR SaaS. Used by integrators for absence, time tracking, substitute, and user profile workflows. All endpoints except /oauth2/token require a bearer JWT access token.
OAuth 2.0 token endpoint. Issues JWT access tokens and rotating refresh tokens. This endpoint is public — no bearer token is required.
As an alternative to the OAuth 2.0 password flow, individual users can create personal access tokens. Tokens are long-lived, named, revocable, and scoped to a single user — no password sharing or refresh-token rotation required.
Tokens have the prefix tb_ followed by an opaque, URL-safe Base64 string. Send them in the Authorization header just like any other bearer credential:
curl "https://app.timebutler.com/api/v2/timeentry/pendingcount" \ -H "Authorization: Bearer tb_a1b2c3d4e5f6..."
/oauth2/tokenIssue or refresh a token
Supports five grant types:
grant_type=password — authenticates with username and password. Returns an access token and refresh token on success. If the account has 2FA enabled, returns an MFA challenge (mfa_required=true, mfa_token, mfa_expires_in) instead — complete the flow with grant_type=mfa_totp.
grant_type=refresh_token — exchanges a valid refresh_token for a new access token and rotates the refresh token (each refresh token can be used exactly once).
grant_type=mfa_totp — second step of the 2FA flow. Submit the mfa_token from the password-grant challenge together with the current totp_code from the user's authenticator app. Returns an access token and refresh token on success.
grant_type=sso_id_token — single sign-on via an ID token the mobile app already obtained from the SSO provider. Submit provider=google and the provider's id_token (a JWT). Timebutler verifies the JWT's signature, issuer and audience and returns an access token and refresh token for the linked Timebutler account.
grant_type=sso_code — single sign-on via an authorization code the mobile app obtained from the SSO provider's consent screen. Submit provider=microsoft or provider=slack, the code from the provider, and the exact redirect_uri used in the authorize request. For Microsoft public-client app registrations, also send the PKCE code_verifier whose SHA-256 hash was used as code_challenge in the authorize step. Timebutler exchanges the code with the provider, identifies the user and returns the token pair.
All grant types also require client_id. Use client_id=public to authenticate without a secret, or supply both client_id and client_secret for a registered confidential client.
| Name | Type | Description |
|---|---|---|
| grant_type | string | OAuth 2.0 grant type. Supported values: password, refresh_token, mfa_totp, sso_id_token, sso_code. |
| username | string | User's email address. Required for grant_type=password. |
| password | string | User's password. Required for grant_type=password. |
| refresh_token | string | Refresh token obtained from a previous token response. Required for grant_type=refresh_token. |
| client_id | string | Client identifier. Use public for unauthenticated public clients. |
| client_secret | string | Client secret. Required for confidential clients; omit when using client_id=public. |
| mfa_token | string | MFA challenge token received from a password grant when 2FA is required. Required for grant_type=mfa_totp. |
| totp_code | string | Current TOTP code from the user's authenticator app. Required for grant_type=mfa_totp. |
| provider | string | SSO provider identifier. Required for grant_type=sso_id_token (google) and grant_type=sso_code (microsoft, slack). |
| id_token | string | SSO ID token issued by the provider (e.g. a Google ID token JWT). Required for grant_type=sso_id_token. |
| code | string | Authorization code returned by the SSO provider's authorize endpoint. Required for grant_type=sso_code. |
| redirect_uri | string | Redirect URI that was used when obtaining the authorization code. Must match the value registered with the SSO provider. Required for grant_type=sso_code. |
| code_verifier | string | PKCE code verifier — the plaintext value whose SHA-256 hash was sent as code_challenge in the authorization request. Required for grant_type=sso_code when the SSO provider's app registration is a public client (e.g. Microsoft Azure AD mobile app). |
OauthTokenResponse (access token + refresh token) for refresh_token and mfa_totp grants, or an MfaChallengeResponse (MFA challenge) for a password grant when the account has 2FA enabled.{
"access_token" : "eyJhbGciOiJSUzI1NiJ9...",
"refresh_token" : "rt-a1b2c3d4e5f6...",
"token_type" : "Bearer",
"expires_in" : 3600,
"scope" : "read"
}
| Name | Type | Description |
|---|---|---|
| error | string | OAuth 2.0 error code (e.g. invalid_grant, invalid_client). |
| error_description | string | Human-readable description of the error. |
{
"error" : "invalid_grant",
"error_description" : "Invalid username or password"
}
| Name | Type | Description |
|---|---|---|
| error | string | OAuth 2.0 error code (e.g. invalid_grant, invalid_client). |
| error_description | string | Human-readable description of the error. |
{
"error" : "invalid_grant",
"error_description" : "Invalid username or password"
}
| Name | Type | Description |
|---|---|---|
| error | string | OAuth 2.0 error code (e.g. invalid_grant, invalid_client). |
| error_description | string | Human-readable description of the error. |
{
"error" : "invalid_grant",
"error_description" : "Invalid username or password"
}
| Name | Type | Description |
|---|---|---|
| error | string | OAuth 2.0 error code (e.g. invalid_grant, invalid_client). |
| error_description | string | Human-readable description of the error. |
{
"error" : "invalid_grant",
"error_description" : "Invalid username or password"
}
curl -X POST "https://app.timebutler.com/api/v2/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'grant_type=password&username=user%40example.com&password=s3cr3t&client_id=public'
curl -X POST "https://app.timebutler.com/api/v2/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'grant_type=refresh_token&refresh_token=550e8400-e29b-41d4-a716-446655440000&client_id=public'
curl -X POST "https://app.timebutler.com/api/v2/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'grant_type=mfa_totp&mfa_token=mfa-a1b2c3d4e5f6&totp_code=123456&client_id=public'
Vacation, sick-leave and other time-off requests. Employees submit and manage their own requests (/my, POST, PUT /{id}, DELETE /{id}); managers and admins see their team's open requests (/team, /pending-count) and approve or reject them (POST /{id}/approve, POST /{id}/reject). All endpoints require a bearer token.
/absence-requests/{id}/approveApprove a pending absence request
Records the caller's approval on a request in the pending state. If the employee has multiple managers, the request only transitions to approved once all managers have approved (or if an admin approves). Callers must be an admin or the direct manager of the request's owner.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Internal ID of the absence request to approve. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request. |
| type | string | Absence type code (3 characters), e.g. "URL" for vacation. |
| startDate | string | First day of the absence in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | Last day of the absence in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans, calculated according to the employee's working-days schedule. |
| status | string | Current status of the request. One of pending (awaiting manager approval), approved, or rejected.Possible values: pending approved rejected |
| halfDay | string | Half-day indicator. "morning" or "afternoon" for half-day requests; null for full-day absences.Possible values: morning afternoon |
| substitutePending | boolean | Whether the substitute employee's confirmation is still pending. null if no substitute was set. |
| medicalCertificateProvided | boolean | Whether a medical certificate has been uploaded for this absence. Only present for absence types that require a sick note; null otherwise. |
| employeeName | string | Display name of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
| employeeId | string | Internal ID of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
{
"id" : "12345",
"type" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"halfDay" : "morning",
"substitutePending" : false,
"medicalCertificateProvided" : false,
"employeeName" : "Maria Müller",
"employeeId" : "42"
}
id exists for the caller's company.pending state (already approved, rejected, or deleted).curl -X POST "https://app.timebutler.com/api/v2/absence-requests/{id}/approve" \
-H "Authorization: Bearer <token>"
/absence-requestsList the caller's own absence requests (alias)
Convenience alias that returns the same result as GET /my. Prefer /my in new integrations — this path exists for backwards compatibility.
[ {
"id" : "12345",
"type" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"halfDay" : "morning",
"substitutePending" : false,
"medicalCertificateProvided" : false,
"employeeName" : "Maria Müller",
"employeeId" : "42"
} ]
curl -X GET "https://app.timebutler.com/api/v2/absence-requests" \ -H "Authorization: Bearer <token>"
/absence-requestsCreate a new absence request
Submits a new absence or vacation request for the caller. The resulting status depends on the absence type's configuration: types that require manager approval start as pending, otherwise they go straight to approved. Dates are ISO-8601 (YYYY-MM-DD). For half-day requests set halfDay to morning or afternoon and use the same value for startDate and endDate.
Fields of the new absence request.
| Name | Type | Description |
|---|---|---|
| absenceTypeIdrequired | string | Absence type code (3 characters), e.g. "URL" for vacation. Must match an active type from GET /absence-types. |
| startDaterequired | string | First day of the absence in ISO-8601 format (YYYY-MM-DD). |
| endDaterequired | string | Last day of the absence in ISO-8601 format (YYYY-MM-DD). Must be on or after startDate. For half-day requests must equal startDate. |
| halfDay | string | Set to "morning" or "afternoon" to request a single half-day. When set, startDate and endDate must be the same day. Omit or send null for a full-day absence.Possible values: morning afternoon |
| comments | string | Optional free-text note visible to the employee's managers. |
| substituteEmployeeId | string | Employee ID of the designated substitute. Must belong to the same company. Omit or send null if no substitute is needed. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request. |
| type | string | Absence type code (3 characters), e.g. "URL" for vacation. |
| startDate | string | First day of the absence in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | Last day of the absence in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans, calculated according to the employee's working-days schedule. |
| status | string | Current status of the request. One of pending (awaiting manager approval), approved, or rejected.Possible values: pending approved rejected |
| halfDay | string | Half-day indicator. "morning" or "afternoon" for half-day requests; null for full-day absences.Possible values: morning afternoon |
| substitutePending | boolean | Whether the substitute employee's confirmation is still pending. null if no substitute was set. |
| medicalCertificateProvided | boolean | Whether a medical certificate has been uploaded for this absence. Only present for absence types that require a sick note; null otherwise. |
| employeeName | string | Display name of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
| employeeId | string | Internal ID of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
{
"id" : "12345",
"type" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"halfDay" : "morning",
"substitutePending" : false,
"medicalCertificateProvided" : false,
"employeeName" : "Maria Müller",
"employeeId" : "42"
}
absenceTypeId, unparseable date, endDate < startDate, bad halfDay value, or unknown substituteEmployeeId).curl -X POST "https://app.timebutler.com/api/v2/absence-requests" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"absenceTypeId":"URL","startDate":"2025-08-01","endDate":"2025-08-05","halfDay":"morning","comments":"Holiday trip","substituteEmployeeId":"42"}'
/absence-requests/{id}Update an existing absence request
Updates a request that is still in the pending or edit state. Only the fields provided in the request body are changed; null fields are left as they were. Non-admins can only update their own requests. Requests that have already been approved or rejected cannot be updated and will return 409 Conflict.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Internal ID of the absence request to update. Matches id from GET /my or GET /team. |
Fields to update. Omit a field (send null) to keep the existing value.
| Name | Type | Description |
|---|---|---|
| absenceTypeId | string | New absence type code (3 characters). Send null to keep the existing type. |
| startDate | string | New start date in ISO-8601 format (YYYY-MM-DD). Send null to keep the existing value. |
| endDate | string | New end date in ISO-8601 format (YYYY-MM-DD). Must be on or after startDate. Send null to keep the existing value. |
| halfDay | string | New half-day setting. "morning" or "afternoon" to switch to a half-day; empty string "" to remove half-day and make it a full day; null to keep the existing value.Possible values: morning afternoon |
| comments | string | New free-text comment. Send null to keep the existing comment; send an empty string to clear it. |
| substituteEmployeeId | string | New substitute employee ID. Send null to keep the existing substitute; send "0" or an empty string to remove the substitute. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request. |
| type | string | Absence type code (3 characters), e.g. "URL" for vacation. |
| startDate | string | First day of the absence in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | Last day of the absence in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans, calculated according to the employee's working-days schedule. |
| status | string | Current status of the request. One of pending (awaiting manager approval), approved, or rejected.Possible values: pending approved rejected |
| halfDay | string | Half-day indicator. "morning" or "afternoon" for half-day requests; null for full-day absences.Possible values: morning afternoon |
| substitutePending | boolean | Whether the substitute employee's confirmation is still pending. null if no substitute was set. |
| medicalCertificateProvided | boolean | Whether a medical certificate has been uploaded for this absence. Only present for absence types that require a sick note; null otherwise. |
| employeeName | string | Display name of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
| employeeId | string | Internal ID of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
{
"id" : "12345",
"type" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"halfDay" : "morning",
"substitutePending" : false,
"medicalCertificateProvided" : false,
"employeeName" : "Maria Müller",
"employeeId" : "42"
}
id exists for the caller's company.curl -X PUT "https://app.timebutler.com/api/v2/absence-requests/{id}" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"absenceTypeId":"URL","startDate":"2025-08-01","endDate":"2025-08-05","halfDay":"morning","comments":"Rescheduled trip","substituteEmployeeId":"42"}'
/absence-requests/{id}Delete an absence request or send a cancellation request
Behaviour depends on the current state of the request and the caller's rights: if the caller may delete the request outright it is removed and the result field of the response is "deleted". Otherwise a cancellation request is created that the employee's managers then have to approve, and the response result is "cancellation_requested". A reason may be required by the company's configuration when only cancellation is possible.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Internal ID of the absence request to delete or cancel. |
Optional body with a free-text reason. Required for cancellation requests when the company's configuration forces a reason.
| Name | Type | Description |
|---|---|---|
| reason | string | Optional free-text reason for the cancellation. Required when the company's configuration mandates a reason for cancellation requests. |
result field tells the caller which path was taken.| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the affected record. |
| result | string | Outcome of the operation. "deleted" if the record was removed outright; "cancellation_requested" if a cancellation request was created instead; "deletion_requested" if a deletion request was created (for time entries that require approval).Possible values: deleted cancellation_requested deletion_requested |
{
"id" : "12345",
"result" : "deleted"
}
id exists for the caller's company.curl -X DELETE "https://app.timebutler.com/api/v2/absence-requests/{id}" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"reason":"Plans changed"}'
/absence-requests/myList the caller's own absence requests
Returns every absence and vacation request belonging to the caller, in any state (pending, approved, rejected, done). Does not include requests the caller manages for their direct reports — for those see /team.
[ {
"id" : "12345",
"type" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"halfDay" : "morning",
"substitutePending" : false,
"medicalCertificateProvided" : false,
"employeeName" : "Maria Müller",
"employeeId" : "42"
} ]
curl -X GET "https://app.timebutler.com/api/v2/absence-requests/my" \ -H "Authorization: Bearer <token>"
/absence-requests/pending-countPending request counts for the caller's team
Returns two counts for the caller's direct reports: open absence requests awaiting the caller's approval, and open cancellation requests. Used to show notification badges on the manager dashboard. Returns zeros when the caller has no direct reports.
| Name | Type | Description |
|---|---|---|
| numberOfAbsenceRequests | integer(int32) | Number of absence requests in the caller's team that are waiting for the caller's approval. |
| numberOfAbsenceRequestCancellations | integer(int32) | Number of cancellation requests in the caller's team that are waiting for approval. |
{
"numberOfAbsenceRequests" : 3,
"numberOfAbsenceRequestCancellations" : 1
}
curl -X GET "https://app.timebutler.com/api/v2/absence-requests/pending-count" \ -H "Authorization: Bearer <token>"
/absence-requests/teamList absence requests of the caller's direct reports
Returns every absence request belonging to employees the caller manages (admins see all company users), across all states. Each entry includes the employee's display name and ID so the result can be rendered as a team calendar or review list. Empty array if the caller has no direct reports.
[ {
"id" : "12345",
"type" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"halfDay" : "morning",
"substitutePending" : false,
"medicalCertificateProvided" : false,
"employeeName" : "Maria Müller",
"employeeId" : "42"
} ]
curl -X GET "https://app.timebutler.com/api/v2/absence-requests/team" \ -H "Authorization: Bearer <token>"
/absence-requests/{id}/rejectReject a pending absence request
Marks a pending absence request as rejected. An optional rejection reason may be passed in the body — it is stored and shown to the requesting employee. Callers must be an admin or the direct manager of the request's owner.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Internal ID of the absence request to reject. |
Optional body containing a reason string shown to the employee.
| Name | Type | Description |
|---|---|---|
| reason | string | Optional free-text reason for the rejection. Stored on the request and shown to the employee. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request. |
| type | string | Absence type code (3 characters), e.g. "URL" for vacation. |
| startDate | string | First day of the absence in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | Last day of the absence in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans, calculated according to the employee's working-days schedule. |
| status | string | Current status of the request. One of pending (awaiting manager approval), approved, or rejected.Possible values: pending approved rejected |
| halfDay | string | Half-day indicator. "morning" or "afternoon" for half-day requests; null for full-day absences.Possible values: morning afternoon |
| substitutePending | boolean | Whether the substitute employee's confirmation is still pending. null if no substitute was set. |
| medicalCertificateProvided | boolean | Whether a medical certificate has been uploaded for this absence. Only present for absence types that require a sick note; null otherwise. |
| employeeName | string | Display name of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
| employeeId | string | Internal ID of the employee who submitted the request. Only present in responses from GET /team; null in GET /my responses. |
{
"id" : "12345",
"type" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"halfDay" : "morning",
"substitutePending" : false,
"medicalCertificateProvided" : false,
"employeeName" : "Maria Müller",
"employeeId" : "42"
}
id exists for the caller's company.pending state.curl -X POST "https://app.timebutler.com/api/v2/absence-requests/{id}/reject" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"reason":"Insufficient staffing during that period"}'
Lookup list of absence types configured for the company (vacation, sick leave, etc.). Used to populate dropdowns when creating an absence request.
/absence-typesList absence types
Returns all active absence types the authenticated user is allowed to submit.
[ {
"id" : "URL",
"name" : "Urlaub",
"needsApproval" : true,
"requiresMedicalCertificate" : false
} ]
curl -X GET "https://app.timebutler.com/api/v2/absence-types" \ -H "Authorization: Bearer <token>"
Requests by employees to cancel (delete) an already-approved absence. Managers and admins see pending cancellation requests for their team and can approve or reject them.
/cancellation-requests/{id}/approveApprove a cancellation request
Approves the pending cancellation request for the given absence. The absence is deleted and notification emails are sent. Requires MANAGER or ADMIN role.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Absence ID. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request being cancelled. |
| employeeId | string | Internal ID of the employee who submitted the original absence request. |
| employeeName | string | Display name of the employee who submitted the original absence request. |
| absenceType | string | Absence type code (3 characters) of the request being cancelled. |
| startDate | string | Start date of the absence being cancelled, in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | End date of the absence being cancelled, in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans. |
| status | string | Current status of the cancellation request. One of pending (awaiting approval) or approved.Possible values: pending approved |
| cancelComments | string | Optional free-text reason provided by the employee when requesting the cancellation. |
{
"id" : "12345",
"employeeId" : "42",
"employeeName" : "Maria Müller",
"absenceType" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"cancelComments" : "Plans changed"
}
curl -X POST "https://app.timebutler.com/api/v2/cancellation-requests/{id}/approve" \
-H "Authorization: Bearer <token>"
/cancellation-requestsList pending cancellation requests
Returns all pending cancellation requests visible to the authenticated manager or admin. Requires MANAGER or ADMIN role.
[ {
"id" : "12345",
"employeeId" : "42",
"employeeName" : "Maria Müller",
"absenceType" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"cancelComments" : "Plans changed"
} ]
curl -X GET "https://app.timebutler.com/api/v2/cancellation-requests" \ -H "Authorization: Bearer <token>"
/cancellation-requests/pending-countGet pending cancellation count
Returns the number of cancellation requests waiting for a manager or admin decision. Requires MANAGER or ADMIN role.
| Name | Type | Description |
|---|---|---|
| numberOfCancellationRequests | integer(int32) | Number of cancellation requests in the caller's team that are waiting for approval. |
{
"numberOfCancellationRequests" : 2
}
curl -X GET "https://app.timebutler.com/api/v2/cancellation-requests/pending-count" \ -H "Authorization: Bearer <token>"
/cancellation-requests/{id}/rejectReject a cancellation request
Rejects the pending cancellation request. The absence is kept; the employee is notified by email. Requires MANAGER or ADMIN role.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Absence ID. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request being cancelled. |
| employeeId | string | Internal ID of the employee who submitted the original absence request. |
| employeeName | string | Display name of the employee who submitted the original absence request. |
| absenceType | string | Absence type code (3 characters) of the request being cancelled. |
| startDate | string | Start date of the absence being cancelled, in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | End date of the absence being cancelled, in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans. |
| status | string | Current status of the cancellation request. One of pending (awaiting approval) or approved.Possible values: pending approved |
| cancelComments | string | Optional free-text reason provided by the employee when requesting the cancellation. |
{
"id" : "12345",
"employeeId" : "42",
"employeeName" : "Maria Müller",
"absenceType" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending",
"cancelComments" : "Plans changed"
}
curl -X POST "https://app.timebutler.com/api/v2/cancellation-requests/{id}/reject" \
-H "Authorization: Bearer <token>"
Lookup list of time-tracking categories for the company. Used when creating or stopping a clock entry.
/categoriesList categories
Returns all active categories along with the user's default category and whether a category is required when saving a time entry.
| Name | Type | Description |
|---|---|---|
| categories | CategoryDto[] | List of categories available to the authenticated user's company. |
| id | string | Internal ID of the category, used when submitting a time entry. |
| name | string | Human-readable name of the category. |
| defaultCategoryId | string | ID of the category pre-selected by default when creating a time entry. null if no default is configured. |
| isCategoryMandatory | boolean | Whether the employee must select a category when creating a time entry. |
{
"categories" : [ {
"id" : "7",
"name" : "Internal project"
} ],
"defaultCategoryId" : "7",
"isCategoryMandatory" : false
}
curl -X GET "https://app.timebutler.com/api/v2/categories" \ -H "Authorization: Bearer <token>"
Directory of the employees the authenticated manager or admin can see, including stable identifiers (internal ID, personnel number, email) for mapping against other endpoints.
/employeesList employees
Returns the employees the authenticated user can see: admins get every user in the company, managers get their direct reports (including themselves) plus any additional employees they have been granted view access to. Each entry carries the internal id, the personnel number, display name, email, role, department, and branch office. The id matches the employeeId returned by GET /absence-requests/team, so this endpoint can be used to resolve which employee an absence request belongs to. Locked (deactivated) accounts are included with active set to false.
[ {
"id" : "42",
"employeeNumber" : "MA-1024",
"firstName" : "Maria",
"lastName" : "Müller",
"name" : "Maria Müller",
"email" : "maria.mueller@example.com",
"phone" : "+49 30 1234567",
"mobile" : "+49 170 1234567",
"role" : "USER",
"department" : "Engineering",
"costCenter" : "CC-100",
"branchOffice" : "Berlin",
"language" : "de_DE",
"managerIds" : [ "17" ],
"additionalInfo" : "Works part-time on Fridays",
"employmentStartDate" : "2020-03-15",
"employmentEndDate" : "2024-12-31",
"dateOfBirth" : "1988-07-24",
"active" : true
} ]
curl -X GET "https://app.timebutler.com/api/v2/employees" \ -H "Authorization: Bearer <token>"
Lookup list of time-tracking projects for the company. Favorites are listed first. Used when creating a clock entry or time-tracking record.
/projectsList projects
Returns all active projects. The user's favourites appear first, followed by all other active projects. Also returns the user's default project and whether a project is required.
| Name | Type | Description |
|---|---|---|
| projects | ProjectDto[] | List of projects available to the authenticated user's company. |
| id | string | Internal ID of the project, used when submitting a time entry. |
| name | string | Human-readable name of the project. |
| isFavorite | boolean | Whether this project is marked as a favourite by the authenticated user. |
| defaultProjectId | string | ID of the project pre-selected by default when creating a time entry. null if no default is configured. |
| isProjectMandatory | boolean | Whether the employee must select a project when creating a time entry. |
{
"projects" : [ {
"id" : "5",
"name" : "Website Relaunch",
"isFavorite" : true
} ],
"defaultProjectId" : "5",
"isProjectMandatory" : true
}
curl -X GET "https://app.timebutler.com/api/v2/projects" \ -H "Authorization: Bearer <token>"
Lookup list of employees that may be selected as a substitute when submitting an absence request.
/substitute-employeesList substitute employees
Returns all active employees in the company that may serve as a substitute for the authenticated user. The authenticated user is excluded from the list.
[ {
"id" : "42",
"name" : "Thomas Schmidt",
"department" : "Engineering"
} ]
curl -X GET "https://app.timebutler.com/api/v2/substitute-employees" \ -H "Authorization: Bearer <token>"
Requests sent to the authenticated user asking them to act as a substitute during a colleague's absence. The user can accept or reject each request.
/substitute-requests/{id}/acceptAccept a substitute request
Accepts the substitute request for the given absence. Only the designated substitute may call this endpoint.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Absence ID. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request for which the substitute was requested. |
| employeeId | string | Internal ID of the employee who is absent and needs a substitute. |
| employeeName | string | Display name of the absent employee. |
| absenceType | string | Absence type code (3 characters) of the absence for which this substitute was requested. |
| startDate | string | Start date of the absence, in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | End date of the absence, in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans. |
| status | string | Current status of the substitute confirmation. One of pending (awaiting the substitute's confirmation) or confirmed.Possible values: pending confirmed |
{
"id" : "12345",
"employeeId" : "7",
"employeeName" : "Anna Bauer",
"absenceType" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending"
}
curl -X POST "https://app.timebutler.com/api/v2/substitute-requests/{id}/accept" \
-H "Authorization: Bearer <token>"
/substitute-requests/pending-countGet pending substitute request count
Returns the number of substitute requests that are still waiting for the authenticated user's response.
| Name | Type | Description |
|---|---|---|
| numberOfSubstituteRequests | integer(int32) | Number of substitute requests awaiting the caller's confirmation. |
{
"numberOfSubstituteRequests" : 1
}
curl -X GET "https://app.timebutler.com/api/v2/substitute-requests/pending-count" \ -H "Authorization: Bearer <token>"
/substitute-requestsList substitute requests
Returns all substitute requests directed at the authenticated user, regardless of their status (pending, accepted, rejected).
[ {
"id" : "12345",
"employeeId" : "7",
"employeeName" : "Anna Bauer",
"absenceType" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending"
} ]
curl -X GET "https://app.timebutler.com/api/v2/substitute-requests" \ -H "Authorization: Bearer <token>"
/substitute-requests/{id}/rejectReject a substitute request
Rejects the substitute request for the given absence. Only the designated substitute may call this endpoint.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Absence ID. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the absence request for which the substitute was requested. |
| employeeId | string | Internal ID of the employee who is absent and needs a substitute. |
| employeeName | string | Display name of the absent employee. |
| absenceType | string | Absence type code (3 characters) of the absence for which this substitute was requested. |
| startDate | string | Start date of the absence, in ISO-8601 format (YYYY-MM-DD). |
| endDate | string | End date of the absence, in ISO-8601 format (YYYY-MM-DD). |
| days | number(double) | Number of working days the absence spans. |
| status | string | Current status of the substitute confirmation. One of pending (awaiting the substitute's confirmation) or confirmed.Possible values: pending confirmed |
{
"id" : "12345",
"employeeId" : "7",
"employeeName" : "Anna Bauer",
"absenceType" : "URL",
"startDate" : "2025-08-01",
"endDate" : "2025-08-05",
"days" : 4.0,
"status" : "pending"
}
curl -X POST "https://app.timebutler.com/api/v2/substitute-requests/{id}/reject" \
-H "Authorization: Bearer <token>"
Real-time time clock. Start, pause, resume, stop, or cancel the running clock for the authenticated user. Business-trip mode is also controlled here.
/time-clock/business-trip-endEnd a business trip
Marks the end of an active business trip on the current clock. Returns 409 if no business trip is currently active.
| Name | Type | Description |
|---|---|---|
| status | string | Current state of the time clock. One of idle (not running), running (clocked in), paused, or waiting (started but held until a minimum interval passes).Possible values: idle running paused waiting |
| startTimestamp | integer(int64) | Unix timestamp in milliseconds when the current clock-in session started. null when status is idle. |
| pauseTimestamp | integer(int64) | Unix timestamp in milliseconds when the current pause started. null if the clock is not paused. |
| workTimeElapsedSeconds | integer(int32) | Total working time elapsed in the current session, in seconds (excludes breaks). |
| breakElapsedSeconds | integer(int32) | Duration of the current ongoing break, in seconds. 0 if the clock is not paused. |
| accumulatedBreakSeconds | integer(int32) | Total accumulated break time in the current session, in seconds (including any completed breaks). |
| waitSeconds | integer(int32) | Minimum number of seconds remaining before the employee is allowed to clock out, based on company policy. null if there is no minimum. |
| isBusinessTripActive | boolean | Whether a business trip is currently active for this session. false when no business trip is active or the feature is not enabled. |
| startOnlyOnTerminal | boolean | Whether the company restricts starting the clock to the time recording terminal only, so this user may not start it through the time clock in their account. Already resolved for the authenticated user (an exception user group is taken into account). |
| pauseOnlyOnTerminal | boolean | Whether the company restricts pausing/resuming the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
| stopOnlyOnTerminal | boolean | Whether the company restricts stopping the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
{
"status" : "running",
"startTimestamp" : 1753700400000,
"pauseTimestamp" : 1753714800000,
"workTimeElapsedSeconds" : 14400,
"breakElapsedSeconds" : 0,
"accumulatedBreakSeconds" : 1800,
"waitSeconds" : 0,
"isBusinessTripActive" : false,
"startOnlyOnTerminal" : false,
"pauseOnlyOnTerminal" : false,
"stopOnlyOnTerminal" : false
}
curl -X POST "https://app.timebutler.com/api/v2/time-clock/business-trip-end" \ -H "Authorization: Bearer <token>"
/time-clock/business-trip-startStart a business trip
Marks the start of a business trip on the currently running or paused clock. Returns 400 if business trips are not enabled for this company; returns 409 if the clock is not active or a business trip is already in progress.
| Name | Type | Description |
|---|---|---|
| status | string | Current state of the time clock. One of idle (not running), running (clocked in), paused, or waiting (started but held until a minimum interval passes).Possible values: idle running paused waiting |
| startTimestamp | integer(int64) | Unix timestamp in milliseconds when the current clock-in session started. null when status is idle. |
| pauseTimestamp | integer(int64) | Unix timestamp in milliseconds when the current pause started. null if the clock is not paused. |
| workTimeElapsedSeconds | integer(int32) | Total working time elapsed in the current session, in seconds (excludes breaks). |
| breakElapsedSeconds | integer(int32) | Duration of the current ongoing break, in seconds. 0 if the clock is not paused. |
| accumulatedBreakSeconds | integer(int32) | Total accumulated break time in the current session, in seconds (including any completed breaks). |
| waitSeconds | integer(int32) | Minimum number of seconds remaining before the employee is allowed to clock out, based on company policy. null if there is no minimum. |
| isBusinessTripActive | boolean | Whether a business trip is currently active for this session. false when no business trip is active or the feature is not enabled. |
| startOnlyOnTerminal | boolean | Whether the company restricts starting the clock to the time recording terminal only, so this user may not start it through the time clock in their account. Already resolved for the authenticated user (an exception user group is taken into account). |
| pauseOnlyOnTerminal | boolean | Whether the company restricts pausing/resuming the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
| stopOnlyOnTerminal | boolean | Whether the company restricts stopping the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
{
"status" : "running",
"startTimestamp" : 1753700400000,
"pauseTimestamp" : 1753714800000,
"workTimeElapsedSeconds" : 14400,
"breakElapsedSeconds" : 0,
"accumulatedBreakSeconds" : 1800,
"waitSeconds" : 0,
"isBusinessTripActive" : false,
"startOnlyOnTerminal" : false,
"pauseOnlyOnTerminal" : false,
"stopOnlyOnTerminal" : false
}
curl -X POST "https://app.timebutler.com/api/v2/time-clock/business-trip-start" \ -H "Authorization: Bearer <token>"
/time-clock/cancelCancel the time clock
Discards the current clock session without saving a time entry. Returns 409 if the clock is already idle.
idle status.| Name | Type | Description |
|---|---|---|
| status | string | Current state of the time clock. One of idle (not running), running (clocked in), paused, or waiting (started but held until a minimum interval passes).Possible values: idle running paused waiting |
| startTimestamp | integer(int64) | Unix timestamp in milliseconds when the current clock-in session started. null when status is idle. |
| pauseTimestamp | integer(int64) | Unix timestamp in milliseconds when the current pause started. null if the clock is not paused. |
| workTimeElapsedSeconds | integer(int32) | Total working time elapsed in the current session, in seconds (excludes breaks). |
| breakElapsedSeconds | integer(int32) | Duration of the current ongoing break, in seconds. 0 if the clock is not paused. |
| accumulatedBreakSeconds | integer(int32) | Total accumulated break time in the current session, in seconds (including any completed breaks). |
| waitSeconds | integer(int32) | Minimum number of seconds remaining before the employee is allowed to clock out, based on company policy. null if there is no minimum. |
| isBusinessTripActive | boolean | Whether a business trip is currently active for this session. false when no business trip is active or the feature is not enabled. |
| startOnlyOnTerminal | boolean | Whether the company restricts starting the clock to the time recording terminal only, so this user may not start it through the time clock in their account. Already resolved for the authenticated user (an exception user group is taken into account). |
| pauseOnlyOnTerminal | boolean | Whether the company restricts pausing/resuming the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
| stopOnlyOnTerminal | boolean | Whether the company restricts stopping the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
{
"status" : "running",
"startTimestamp" : 1753700400000,
"pauseTimestamp" : 1753714800000,
"workTimeElapsedSeconds" : 14400,
"breakElapsedSeconds" : 0,
"accumulatedBreakSeconds" : 1800,
"waitSeconds" : 0,
"isBusinessTripActive" : false,
"startOnlyOnTerminal" : false,
"pauseOnlyOnTerminal" : false,
"stopOnlyOnTerminal" : false
}
idle.curl -X POST "https://app.timebutler.com/api/v2/time-clock/cancel" \ -H "Authorization: Bearer <token>"
/time-clock/statusGet time clock status
Returns the current state of the time clock: idle, running, paused, or waiting. Also returns elapsed time, pause time, and business-trip flag.
| Name | Type | Description |
|---|---|---|
| status | string | Current state of the time clock. One of idle (not running), running (clocked in), paused, or waiting (started but held until a minimum interval passes).Possible values: idle running paused waiting |
| startTimestamp | integer(int64) | Unix timestamp in milliseconds when the current clock-in session started. null when status is idle. |
| pauseTimestamp | integer(int64) | Unix timestamp in milliseconds when the current pause started. null if the clock is not paused. |
| workTimeElapsedSeconds | integer(int32) | Total working time elapsed in the current session, in seconds (excludes breaks). |
| breakElapsedSeconds | integer(int32) | Duration of the current ongoing break, in seconds. 0 if the clock is not paused. |
| accumulatedBreakSeconds | integer(int32) | Total accumulated break time in the current session, in seconds (including any completed breaks). |
| waitSeconds | integer(int32) | Minimum number of seconds remaining before the employee is allowed to clock out, based on company policy. null if there is no minimum. |
| isBusinessTripActive | boolean | Whether a business trip is currently active for this session. false when no business trip is active or the feature is not enabled. |
| startOnlyOnTerminal | boolean | Whether the company restricts starting the clock to the time recording terminal only, so this user may not start it through the time clock in their account. Already resolved for the authenticated user (an exception user group is taken into account). |
| pauseOnlyOnTerminal | boolean | Whether the company restricts pausing/resuming the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
| stopOnlyOnTerminal | boolean | Whether the company restricts stopping the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
{
"status" : "running",
"startTimestamp" : 1753700400000,
"pauseTimestamp" : 1753714800000,
"workTimeElapsedSeconds" : 14400,
"breakElapsedSeconds" : 0,
"accumulatedBreakSeconds" : 1800,
"waitSeconds" : 0,
"isBusinessTripActive" : false,
"startOnlyOnTerminal" : false,
"pauseOnlyOnTerminal" : false,
"stopOnlyOnTerminal" : false
}
curl -X GET "https://app.timebutler.com/api/v2/time-clock/status" \ -H "Authorization: Bearer <token>"
/time-clock/pausePause the time clock
Pauses a running time clock. Returns 409 if the clock is not in running state.
| Name | Type | Description |
|---|---|---|
| status | string | Current state of the time clock. One of idle (not running), running (clocked in), paused, or waiting (started but held until a minimum interval passes).Possible values: idle running paused waiting |
| startTimestamp | integer(int64) | Unix timestamp in milliseconds when the current clock-in session started. null when status is idle. |
| pauseTimestamp | integer(int64) | Unix timestamp in milliseconds when the current pause started. null if the clock is not paused. |
| workTimeElapsedSeconds | integer(int32) | Total working time elapsed in the current session, in seconds (excludes breaks). |
| breakElapsedSeconds | integer(int32) | Duration of the current ongoing break, in seconds. 0 if the clock is not paused. |
| accumulatedBreakSeconds | integer(int32) | Total accumulated break time in the current session, in seconds (including any completed breaks). |
| waitSeconds | integer(int32) | Minimum number of seconds remaining before the employee is allowed to clock out, based on company policy. null if there is no minimum. |
| isBusinessTripActive | boolean | Whether a business trip is currently active for this session. false when no business trip is active or the feature is not enabled. |
| startOnlyOnTerminal | boolean | Whether the company restricts starting the clock to the time recording terminal only, so this user may not start it through the time clock in their account. Already resolved for the authenticated user (an exception user group is taken into account). |
| pauseOnlyOnTerminal | boolean | Whether the company restricts pausing/resuming the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
| stopOnlyOnTerminal | boolean | Whether the company restricts stopping the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
{
"status" : "running",
"startTimestamp" : 1753700400000,
"pauseTimestamp" : 1753714800000,
"workTimeElapsedSeconds" : 14400,
"breakElapsedSeconds" : 0,
"accumulatedBreakSeconds" : 1800,
"waitSeconds" : 0,
"isBusinessTripActive" : false,
"startOnlyOnTerminal" : false,
"pauseOnlyOnTerminal" : false,
"stopOnlyOnTerminal" : false
}
curl -X POST "https://app.timebutler.com/api/v2/time-clock/pause" \ -H "Authorization: Bearer <token>"
/time-clock/resumeResume the time clock
Resumes a paused time clock. Returns 409 if the clock is not in paused state.
| Name | Type | Description |
|---|---|---|
| status | string | Current state of the time clock. One of idle (not running), running (clocked in), paused, or waiting (started but held until a minimum interval passes).Possible values: idle running paused waiting |
| startTimestamp | integer(int64) | Unix timestamp in milliseconds when the current clock-in session started. null when status is idle. |
| pauseTimestamp | integer(int64) | Unix timestamp in milliseconds when the current pause started. null if the clock is not paused. |
| workTimeElapsedSeconds | integer(int32) | Total working time elapsed in the current session, in seconds (excludes breaks). |
| breakElapsedSeconds | integer(int32) | Duration of the current ongoing break, in seconds. 0 if the clock is not paused. |
| accumulatedBreakSeconds | integer(int32) | Total accumulated break time in the current session, in seconds (including any completed breaks). |
| waitSeconds | integer(int32) | Minimum number of seconds remaining before the employee is allowed to clock out, based on company policy. null if there is no minimum. |
| isBusinessTripActive | boolean | Whether a business trip is currently active for this session. false when no business trip is active or the feature is not enabled. |
| startOnlyOnTerminal | boolean | Whether the company restricts starting the clock to the time recording terminal only, so this user may not start it through the time clock in their account. Already resolved for the authenticated user (an exception user group is taken into account). |
| pauseOnlyOnTerminal | boolean | Whether the company restricts pausing/resuming the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
| stopOnlyOnTerminal | boolean | Whether the company restricts stopping the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
{
"status" : "running",
"startTimestamp" : 1753700400000,
"pauseTimestamp" : 1753714800000,
"workTimeElapsedSeconds" : 14400,
"breakElapsedSeconds" : 0,
"accumulatedBreakSeconds" : 1800,
"waitSeconds" : 0,
"isBusinessTripActive" : false,
"startOnlyOnTerminal" : false,
"pauseOnlyOnTerminal" : false,
"stopOnlyOnTerminal" : false
}
curl -X POST "https://app.timebutler.com/api/v2/time-clock/resume" \ -H "Authorization: Bearer <token>"
/time-clock/startStart the time clock
Starts the time clock for the authenticated user. Returns 409 if the clock is already running. The clock may enter waiting state if a minimum start interval is configured.
| Name | Type | Description |
|---|---|---|
| status | string | Current state of the time clock. One of idle (not running), running (clocked in), paused, or waiting (started but held until a minimum interval passes).Possible values: idle running paused waiting |
| startTimestamp | integer(int64) | Unix timestamp in milliseconds when the current clock-in session started. null when status is idle. |
| pauseTimestamp | integer(int64) | Unix timestamp in milliseconds when the current pause started. null if the clock is not paused. |
| workTimeElapsedSeconds | integer(int32) | Total working time elapsed in the current session, in seconds (excludes breaks). |
| breakElapsedSeconds | integer(int32) | Duration of the current ongoing break, in seconds. 0 if the clock is not paused. |
| accumulatedBreakSeconds | integer(int32) | Total accumulated break time in the current session, in seconds (including any completed breaks). |
| waitSeconds | integer(int32) | Minimum number of seconds remaining before the employee is allowed to clock out, based on company policy. null if there is no minimum. |
| isBusinessTripActive | boolean | Whether a business trip is currently active for this session. false when no business trip is active or the feature is not enabled. |
| startOnlyOnTerminal | boolean | Whether the company restricts starting the clock to the time recording terminal only, so this user may not start it through the time clock in their account. Already resolved for the authenticated user (an exception user group is taken into account). |
| pauseOnlyOnTerminal | boolean | Whether the company restricts pausing/resuming the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
| stopOnlyOnTerminal | boolean | Whether the company restricts stopping the clock to the time recording terminal only. Already resolved for the authenticated user (an exception user group is taken into account). |
{
"status" : "running",
"startTimestamp" : 1753700400000,
"pauseTimestamp" : 1753714800000,
"workTimeElapsedSeconds" : 14400,
"breakElapsedSeconds" : 0,
"accumulatedBreakSeconds" : 1800,
"waitSeconds" : 0,
"isBusinessTripActive" : false,
"startOnlyOnTerminal" : false,
"pauseOnlyOnTerminal" : false,
"stopOnlyOnTerminal" : false
}
curl -X POST "https://app.timebutler.com/api/v2/time-clock/start" \ -H "Authorization: Bearer <token>"
/time-clock/stopStop and save the time clock
Stops the running or paused clock and saves the resulting time entry. An optional project ID and remarks can be provided. If the clock was in waiting state it is discarded without saving and 204 is returned. Returns 409 if the total duration is too short (< 1 minute) or if a duplicate entry is detected.
If the session violates the company's break-time regulation and no breakRegulation decision was sent, the entry is not saved: the response has outcome: confirmationRequired and a breakRegulation object describing the required change. Re-send the stop with breakRegulation: apply (accept the correction) or breakRegulation: ignore (save unchanged). Clients should only offer ignore when the regulation is not obligatory (breakRegulation.obligatory in the response); the server does not currently enforce this. When the company has auto-apply configured, the correction is applied on the first stop without a decision.
Optional project ID, category ID, and remarks for the saved entry.
| Name | Type | Description |
|---|---|---|
| projectId | integer(int32) | ID of the project to assign the completed session to. Send 0 if no project assignment is needed. |
| categoryId | integer(int32) | ID of the category to assign the completed session to. Send 0 if no category assignment is needed. |
| remarks | string | Optional free-text note for the completed time entry. |
| breakRegulation | string | Decision on how to handle a break-time-regulation violation. Omit on the first stop attempt: if the session violates the break regulation the server does not save and instead returns outcome: confirmationRequired describing the required change. Re-send the stop with apply to accept the correction (reduce working time and add the missing break) or ignore to save the session unchanged. Clients should only offer ignore when the regulation is not obligatory (obligatory in the confirmation response); the server does not currently enforce this.Possible values: apply ignore |
outcome: saved) or a break-regulation confirmation request (outcome: confirmationRequired).| Name | Type | Description |
|---|---|---|
| outcome | string | saved when the entry was persisted, or confirmationRequired when a break-regulation decision is needed before saving.Possible values: saved confirmationRequired |
| id | string | Internal ID of the time entry created when the clock was stopped. Null when outcome is confirmationRequired. |
| workedMinutes | integer(int32) | Net working time recorded for the session, in minutes (total duration minus breaks). Null when outcome is confirmationRequired. |
| breakMinutes | integer(int32) | Total break time recorded for the session, in minutes. Null when outcome is confirmationRequired. |
| breakRegulationApplied | boolean | true when a break-regulation correction was applied to the saved entry (working time reduced, break increased). |
| breakRegulationIgnored | boolean | true when the entry was saved unchanged despite a break-regulation violation (the user chose to ignore it). |
| breakRegulation | TimeClockBreakRegulationInfo | Break-regulation violation details. Present only when outcome is confirmationRequired; null otherwise. |
| hasNoRemedy | boolean | true when the violation cannot be resolved automatically (e.g. the daily maximum working time is already exceeded). The session can then only be discarded or, if the regulation is not obligatory, saved unchanged with ignore. |
| obligatory | boolean | true when the break regulation is obligatory for this company. When obligatory the session must not be saved unchanged; the client hides the ignore option. |
| correctionWorkMinutes | integer(int32) | Minutes that would be deducted from working time if the correction is applied. |
| correctionBreakMinutes | integer(int32) | Minutes that would be added to break time if the correction is applied. |
| resultingWorkMinutes | integer(int32) | Resulting net working time in minutes after the correction is applied. |
| resultingBreakMinutes | integer(int32) | Resulting total break time in minutes after the correction is applied. |
| detailText | string | Human-readable, localized summary of the required change, suitable for display. |
{
"outcome" : "saved",
"id" : "98765",
"workedMinutes" : 450,
"breakMinutes" : 30,
"breakRegulationApplied" : true,
"breakRegulationIgnored" : false,
"breakRegulation" : {
"hasNoRemedy" : false,
"obligatory" : false,
"correctionWorkMinutes" : 30,
"correctionBreakMinutes" : 30,
"resultingWorkMinutes" : 450,
"resultingBreakMinutes" : 30,
"detailText" : "Required change: working time -0:30, break +0:30"
}
}
waiting state and was discarded without saving.curl -X POST "https://app.timebutler.com/api/v2/time-clock/stop" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"projectId":5,"categoryId":7,"remarks":"Code review","breakRegulation":"apply"}'
Manage individual time-tracking entries. Any user can list their own entries (/my) and delete their own entries (subject to month-lock and approval-workflow rules); managers and admins can see how many entries are pending approval.
/time-entries/{id}Delete a time entry
Deletes the specified time entry. If the company's time-tracking settings require manager approval for deletions, a deletion request is submitted instead of an immediate delete. The response field result is either deleted or deletion_requested.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Time entry ID. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the affected record. |
| result | string | Outcome of the operation. "deleted" if the record was removed outright; "cancellation_requested" if a cancellation request was created instead; "deletion_requested" if a deletion request was created (for time entries that require approval).Possible values: deleted cancellation_requested deletion_requested |
{
"id" : "12345",
"result" : "deleted"
}
curl -X DELETE "https://app.timebutler.com/api/v2/time-entries/{id}" \
-H "Authorization: Bearer <token>"
/time-entries/myList the caller's own time entries
Returns the caller's recorded time entries, matching the time-tracking list view in the web UI. Without parameters the current month is returned. Pass year and month to select a different month, or year alone for a whole year. Entries can additionally be filtered by project, category, and status. Entries are sorted by date and start time, newest first.
| Name | Type | Description |
|---|---|---|
| year | integer(int32) | Year to list entries for. Accepted range: 1970-2100. Defaults to the current year. |
| month | integer(int32) | Month to list entries for (1-12). Defaults to the current month when year is also omitted; omit while passing year to get the whole year. |
| projectId | integer(int32) | Only return entries assigned to this project. Matches id from GET /projects. |
| categoryId | integer(int32) | Only return entries assigned to this category. Matches id from GET /categories. |
| status | string | Only return entries with this status. One of done, pending, approved, rejected, editing. |
[ {
"id" : "98765",
"date" : "2025-07-28",
"startTime" : "08:00",
"endTime" : "17:00",
"workedMinutes" : 480,
"breakMinutes" : 60,
"projectId" : "17",
"project" : "Website Relaunch",
"categoryId" : "4",
"category" : "Internal project",
"status" : "done",
"autoStopped" : false,
"remarks" : "Sprint planning"
} ]
year, month, projectId, categoryId, or status value.curl -X GET "https://app.timebutler.com/api/v2/time-entries/my" \ -H "Authorization: Bearer <token>"
/time-entries/pending-countGet pending time-entry count
Returns the number of time entries in the manager's team that are awaiting approval. Requires MANAGER or ADMIN role.
| Name | Type | Description |
|---|---|---|
| numberOfTimeEntries | integer(int32) | Number of time entries in the caller's team that are waiting for approval. |
{
"numberOfTimeEntries" : 5
}
curl -X GET "https://app.timebutler.com/api/v2/time-entries/pending-count" \ -H "Authorization: Bearer <token>"
Pending change or deletion requests for time entries that require manager or admin approval. Managers and admins can list, approve, and reject these requests.
/time-entry-requests/{id}/approveApprove a time-entry request
Approves the pending change or deletion request for the given time entry. For deletion requests the entry is removed; for change requests the entry is updated to the proposed values. Requires MANAGER or ADMIN role.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Time entry ID. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the time entry. |
| employeeId | string | Internal ID of the employee who submitted the time entry. Only present in team/manager views. |
| employeeName | string | Display name of the employee who submitted the time entry. Only present in team/manager views. |
| date | string | Date of the time entry in ISO-8601 format (YYYY-MM-DD). |
| startTime | string | Clock-in time in HH:mm format. |
| endTime | string | Clock-out time in HH:mm format. |
| workedMinutes | integer(int32) | Net working time for this entry, in minutes (total duration minus breaks). |
| breakMinutes | integer(int32) | Total break time for this entry, in minutes. |
| project | string | Name of the project assigned to this entry. null if no project was assigned. |
| category | string | Name of the category assigned to this entry. null if no category was assigned. |
| remarks | string | Free-text note attached to this entry. null if no remark was entered. |
| requestType | string | Type of request that produced this entry. One of manual (entered by hand) or clock (created by stopping the time clock).Possible values: manual clock |
| status | string | Approval status of this time entry. One of pending (awaiting manager approval) or approved.Possible values: pending approved |
{
"id" : "98765",
"employeeId" : "42",
"employeeName" : "Maria Müller",
"date" : "2025-07-28",
"startTime" : "08:00",
"endTime" : "17:00",
"workedMinutes" : 480,
"breakMinutes" : 60,
"project" : "Website Relaunch",
"category" : "Internal project",
"remarks" : "Sprint planning",
"requestType" : "manual",
"status" : "pending"
}
curl -X POST "https://app.timebutler.com/api/v2/time-entry-requests/{id}/approve" \
-H "Authorization: Bearer <token>"
/time-entry-requestsList pending time-entry requests
Returns all time-entry change/deletion requests in the manager's team that are waiting for approval. Requires MANAGER or ADMIN role. Returns an empty list if time tracking is not active or the caller is a manager in an admin-only-approval company.
[ {
"id" : "98765",
"employeeId" : "42",
"employeeName" : "Maria Müller",
"date" : "2025-07-28",
"startTime" : "08:00",
"endTime" : "17:00",
"workedMinutes" : 480,
"breakMinutes" : 60,
"project" : "Website Relaunch",
"category" : "Internal project",
"remarks" : "Sprint planning",
"requestType" : "manual",
"status" : "pending"
} ]
curl -X GET "https://app.timebutler.com/api/v2/time-entry-requests" \ -H "Authorization: Bearer <token>"
/time-entry-requests/{id}/rejectReject a time-entry request
Rejects the pending change or deletion request for the given time entry. An optional rejection reason can be included in the request body. The employee is notified by email. Requires MANAGER or ADMIN role.
| Name | Type | Description |
|---|---|---|
| idrequired | integer(int32) | Time entry ID. |
Optional rejection reason.
| Name | Type | Description |
|---|---|---|
| reason | string | Optional free-text reason for the rejection. Stored on the request and shown to the employee. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the time entry. |
| employeeId | string | Internal ID of the employee who submitted the time entry. Only present in team/manager views. |
| employeeName | string | Display name of the employee who submitted the time entry. Only present in team/manager views. |
| date | string | Date of the time entry in ISO-8601 format (YYYY-MM-DD). |
| startTime | string | Clock-in time in HH:mm format. |
| endTime | string | Clock-out time in HH:mm format. |
| workedMinutes | integer(int32) | Net working time for this entry, in minutes (total duration minus breaks). |
| breakMinutes | integer(int32) | Total break time for this entry, in minutes. |
| project | string | Name of the project assigned to this entry. null if no project was assigned. |
| category | string | Name of the category assigned to this entry. null if no category was assigned. |
| remarks | string | Free-text note attached to this entry. null if no remark was entered. |
| requestType | string | Type of request that produced this entry. One of manual (entered by hand) or clock (created by stopping the time clock).Possible values: manual clock |
| status | string | Approval status of this time entry. One of pending (awaiting manager approval) or approved.Possible values: pending approved |
{
"id" : "98765",
"employeeId" : "42",
"employeeName" : "Maria Müller",
"date" : "2025-07-28",
"startTime" : "08:00",
"endTime" : "17:00",
"workedMinutes" : 480,
"breakMinutes" : 60,
"project" : "Website Relaunch",
"category" : "Internal project",
"remarks" : "Sprint planning",
"requestType" : "manual",
"status" : "pending"
}
curl -X POST "https://app.timebutler.com/api/v2/time-entry-requests/{id}/reject" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"reason":"Insufficient staffing during that period"}'
Today's work and time-off summaries for the authenticated user, plus an endpoint for submitting completed clock entries (start/end timestamps) directly without the real-time time clock.
/time-tracking/clock-entrySubmit a completed clock entry
Creates a time entry from explicit start and end Unix timestamps (milliseconds). Start and end must be on the same calendar day. If the company requires manager approval for new entries, the entry is created with status pending; otherwise it is immediately done.
Clock entry with start/end timestamps and optional project and remarks.
| Name | Type | Description |
|---|---|---|
| startTimestamprequired | integer(int64) | Start of the work period as a Unix timestamp in milliseconds. |
| endTimestamprequired | integer(int64) | End of the work period as a Unix timestamp in milliseconds. |
| pauseSeconds | integer(int32) | Total break duration within the work period, in seconds. |
| projectId | integer(int32) | ID of the project to assign this entry to. Send 0 if no project assignment is needed. |
| remarks | string | Optional free-text note for this time entry. |
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the created time entry. |
| workedMinutes | integer(int32) | Net working time recorded for this entry, in minutes (total duration minus breaks). |
| breakMinutes | integer(int32) | Total break time recorded for this entry, in minutes. |
{
"id" : "98765",
"workedMinutes" : 480,
"breakMinutes" : 30
}
curl -X POST "https://app.timebutler.com/api/v2/time-tracking/clock-entry" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"startTimestamp":1753700400000,"endTimestamp":1753729200000,"pauseSeconds":1800,"projectId":5,"remarks":"Sprint planning"}'
/time-tracking/time-off-summaryGet time-off summary
Returns the authenticated user's remaining and total vacation days for the current year, plus the number of illness days taken.
| Name | Type | Description |
|---|---|---|
| vacationDaysRemaining | number(double) | Number of vacation days the employee still has available in the current year, after accounting for approved and pending requests. |
| vacationDaysTotal | number(double) | Total vacation day entitlement for the current year. |
| illnessDaysTaken | number(double) | Number of sick-leave days taken in the current year. |
{
"vacationDaysRemaining" : 12.5,
"vacationDaysTotal" : 30.0,
"illnessDaysTaken" : 3.0
}
curl -X GET "https://app.timebutler.com/api/v2/time-tracking/time-off-summary" \ -H "Authorization: Bearer <token>"
/time-tracking/work-summaryGet today's work summary
Returns the authenticated user's work commitment, worked minutes, remaining minutes, completion percentage, and break minutes for today.
| Name | Type | Description |
|---|---|---|
| commitmentMinutes | integer(int32) | Total working time the employee is contractually required to work in the queried period, in minutes. |
| workedMinutes | integer(int32) | Total working time the employee has actually worked in the queried period, in minutes. |
| remainingMinutes | integer(int32) | Remaining working time still to be worked in the queried period, in minutes. Negative values indicate overtime. |
| percentDone | integer(int32) | Percentage of the contractual working time that has been worked so far, rounded to the nearest integer. |
| breakMinutes | integer(int32) | Total break time recorded in the queried period, in minutes. |
{
"commitmentMinutes" : 2400,
"workedMinutes" : 2280,
"remainingMinutes" : 120,
"percentDone" : 95,
"breakMinutes" : 300
}
curl -X GET "https://app.timebutler.com/api/v2/time-tracking/work-summary" \ -H "Authorization: Bearer <token>"
Basic profile information for the authenticated user: name, email, role, department, and avatar URL.
/user/profileGet profile
Returns the authenticated user's display name, email address, role (USER, MANAGER, ADMIN), department, branch office, and avatar URL.
| Name | Type | Description |
|---|---|---|
| id | string | Internal ID of the authenticated user. |
| name | string | Full display name of the user. |
| string | Email address of the user. | |
| role | string | Role of the user within the company. One of USER, MANAGER, or ADMIN.Possible values: USER MANAGER ADMIN |
| department | string | Name of the department the user belongs to. null if the company does not use departments. |
| branchOffice | string | Name of the branch office the user is assigned to. null if the company does not use branch offices. |
| country | object | Country of the user as configured in their profile. The structure depends on the company's configuration. |
| avatar | string | URL of the user's profile picture. null if no avatar has been uploaded. |
| isBusinessTripEnabled | boolean | Whether the business trip feature is enabled for this user. null if the feature is not available in the company's plan. |
| isTimeTrackingEnabled | boolean | Whether time tracking is active for the user's company. |
{
"id" : "42",
"name" : "Maria Müller",
"email" : "maria.mueller@example.com",
"role" : "MANAGER",
"department" : "Engineering",
"branchOffice" : "Berlin",
"country" : { },
"avatar" : "/images/avatar/42/abc123.jpg?v=5",
"isBusinessTripEnabled" : true,
"isTimeTrackingEnabled" : true
}
curl -X GET "https://app.timebutler.com/api/v2/user/profile" \ -H "Authorization: Bearer <token>"