Overview

Introduction

TimeTac provides an easy accessible RESTful application programming interface (API) for developers to connect and integrate a TimeTac account into their own apps.

The API as well as every TimeTac company account is decoupled from all the other company accounts. Your API access will be restricted to your account only, and no other API user can ever access your companies’ data without your credentials (see chapter “Authentication” for more information).

Getting Started

The TimeTac API is not open to the public. In order to get started with the API, you will need to request a private API key that will enable the API for your TimeTac company account and grant you access to your resources. Please contact us at support@timetac.com and tell us about the project you are planning to use our API for, and we will be happy to assist you with an API key and answers to all the questions you might have.

Once you have your key you will be able to make authenticated requests to our API by calling the URL for a specific resource (see chapter “Resources”). A resource URL will always have the following format:

Generic API URL

https://go.timetac.com/<company_account>/userapi/<api_version>/<resource>/<action>/

  • <company_account>
    The name of the requested company account. We will only allow requests to your own company account that will be permanently mapped to your API key.
  • <api_version>
    If updates to our API would break backward compatibility, we will release a new API version but keep the previous one available to give every developer enough time to adapt their apps to the new changes.
  • <resource>
    The name of the requested resource.
  • <action>
    The action you want to perform on the specified resource (get/create/update/delete).

Example

https://go.timetac.com/TimeTacGmbH/userapi/v3/tasks/get/?id=31

Authentification - OAuth 2

We are currently only supporting OAuth2 password_grant authentication.

Request an access token

Password Grant
The password grant is a type where you will have to POST both user credentials and client credentials in order to directly receive an access token for the user, without going through the process of requesting a request token or asking for user consent.

  • Request method: POST
  • URI: /<account>/auth/oauth2/token
  • Header:
    • Content-Type: application/x-www-form-urlencoded
  • Parameter:
    • grant_type: The OAuth2 grant type to use – here “password”
    • client_id: Your Clients ID on the backend — ask an admin/support.
    • client_secret: Your Clients Secret (key/password) — ask an admin/support.
    • username: The TimeTac username of the user to authenticate
    • password: The password of the TimeTac user to authenticate

Example HTTP Request:

                  POST //auth/oauth2/token HTTP/1.1
Host: go-sandbox.timetac.com (https://go-sandbox.timetac.com/)
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=manager&password=1xxxxxxxxxxxxxxxxxxxxxxxx6&client_id=TT_NATIV
              

Example Server Response:
                  {
    "access_token": "5256xxxxxxxxxxxxxxxxxxxxxxxx140f",
    "token_type": "bearer",
    "expires_in": 3600,
    "refresh_token": "264fxxxxxxxxxxxxxxxxxxxxxxxx57e8"
}
              

You will have to remember both, the access_token as well as the refresh_token, or else you will have to send the user credentials again once the access_token expires.

Once the access_token is expired, you will no longer have access to the API, and will receive an error response on further requests:

{
    "error_description": "The provided access token is invalid.",
    "error": "invalid_request"
}

From here on, you will have to either request a new access_token with another password_grant, or “refresh” your access with the previous refresh_token.

Refresh an access token

If you have the corresponding refresh_token for an expired access_token, you can request a new acces_token without the need to send the user credentials again.

Request method: POST
URI: //auth/oauth2/token
Header: Content-Type: application/x-www-form-urlencoded
Parameter:
grant_type: The OAuth2 grant type to use – here "refresh_token"
client_id: Your Clients ID on the backend -- ask an admin/support.
client_secret: Your Clients Secret (key/password) -- ask an admin/support.
refresh_token: A valid refresh token


Example HTTP Request:
{
    "access_token": "7053xxxxxxxxxxxxxxxxxxxxxxxx066c",
    "token_type": "bearer",
    "expires_in": 3600,
    "refresh_token": "fc7dxxxxxxxxxxxxxxxxxxxxxxxxaf61"
}

You will receive a new token response, with a new access_token and refresh_token to use.

Example Server Response:

{
    "access_token": "7053xxxxxxxxxxxxxxxxxxxxxxxx066c",
    "token_type": "bearer",
    "expires_in": 3600,
    "refresh_token": "fc7dxxxxxxxxxxxxxxxxxxxxxxxxaf61"
}

Authorize an API request

Once you have aquired an access_token, you can use it to authorize an API request. The request itself depends on the resource you want to query and the action you want to perform, but the authorization is always done via the “Authorization” header:

  • Header:
    • Authorization: Bearer <access_token>

Example HTTP Request:

GET //userapi/v3/user/read/?hr_manager=1&active=1 HTTP/1.1
Host: go-sandbox.timetac.com
Authorization: Bearer 7053xxxxxxxxxxxxxxxxxxxxxxxx066c
Cache-Control: no-cache

Example Server Response:
{
    "Host": "go-sandbox.timetac.com",
    "Success": true,
    "NumResults": 0,
    "ResourceName": "User",
    "Results": []
}

Server request

The request parameters can be sent in 2 different ways:

1. x-www-form-urlencoded

Example:

POST /devettpttlmlive/userapi/v3/messages/create/ HTTP/1.1
Host: go-dev.timetac.com
Authorization: Bearer 7053xxxxxxxxxxxxxxxxxxxxxxxx066c
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: d937270f-6c60-2f15-db73-bb73cce43126

sender_id=1&message=Hello+Max%2C+I+will+be+late+today+so+please+meet+me+at+10%3A00.+Thank+you×tamp=2016-09-27+07%3A00%3A00

Example (nested entities): timeTrackings - creates 1 timeTracking and 2 checkpointTrackings

POST /devettpttlmlive/userapi/v3/messages/create/ HTTP/1.1
Host: go-dev.timetac.com
Authorization: Bearer 7053xxxxxxxxxxxxxxxxxxxxxxxx066c
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: d937270f-6c60-2f15-db73-bb73cce43126

start_time=2017-06-13+13%3A28%3A36&task_id=4&user_id=1&start_time_timezone=Europe%2FVienna&end_time=2017-06-22+18%3A05%3A00&end_time_timezone=Europe%2FVienna&nestedEntities=%7B%22checkpointTrackings%22%3A%5B%7B%22checkpoint_id%22%3A%2210%22%2C%22timestamp%22%3A%222017-06-13+13%3A28%3A36%22%2C%22timezone%22%3A%224%22%7D%2C%7B%22checkpoint_id%22%3A%2211%22%2C%22timestamp%22%3A%222017-06-13+13%3A35%3A36%22%2C%22timezone%22%3A%224%22%7D%5D%7D

2. JSON (Batch request)

Batch requests are very useful because they provide you with possibility to execute multiple entities with a single API call.

Batch requests are limited to 100 items per request and this is a hard limit for every user account.

The current batch limit is there because of both performance and security reasons, and API solutions should be developed with this limit in mind.

In general, if you are using batch request you should:

  • Implement the "slicer" from your side which will separate your requests to multiples of 100.
  • Implement proper error handling mechanisms and models which can examine results of bulk executions.
  • Increase response time waiting (timeout config) because batch requests can be slower, depending on what is inside of the batch.

Example:

POST /devettpttlmlive/userapi/v3/messages/create/ HTTP/1.1
Host: go-dev.timetac.com
Authorization: Bearer 7053xxxxxxxxxxxxxxxxxxxxxxxx066c
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: f10b6a07-d261-4edc-c79f-f85819fe42a5

[ { "message": "Lorem Ipsum", "sender_id": 1, "receiver_type": "USER", "receiver_id": 10 }, { "message": "Dolor Sit Amet", "sender_id": 1, "receiver_type": "USER", "receiver_id": 11 }, { "message": "Consectetur Adipiscing Elit", "sender_id": 1, "receiver_type": "USER", "receiver_id": 12 } ]

Example (nested entities): timeTrackings - creates 1 timeTracking and 2 checkpointTrackings:

POST /devettpttlmlive/userapi/v3/timeTrackings/create/ HTTP/1.1
Host: go-dev.timetac.com
Authorization: Bearer 7053xxxxxxxxxxxxxxxxxxxxxxxx066c
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: f10b6a07-d261-4edc-c79f-f85819fe42a5

[ { "user_id":10, "task_id":4, "start_time":"2024-01-13 08:00:00", "end_time":"2024-01-13 09:00:00", "start_time_timezone":"Europe/Vienna", "end_time_timezone":"Europe/Vienna", "nestedEntities":{ "checkpointTrackings":[ { "checkpoint_id":1, "timestamp":"2024-01-13 08:20:00", "timezone":"Europe/Vienna" }, { "checkpoint_id":1, "timestamp":"2024-01-13 08:40:00", "timezone":"Europe/Vienna" } ] } } ]

Example projects - creates 1 project with 2 tasks + 1 subproject with 2 tasks + 1 subproject:

POST /devettpttlmlive/userapi/v3/projects/create/ HTTP/1.1
Host: go-dev.timetac.com
Authorization: Bearer 7053xxxxxxxxxxxxxxxxxxxxxxxx066c
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: f10b6a07-d261-4edc-c79f-f85819fe42a5

[
   {
      "mother_id":3,
      "sort_order":50,
      "name":"Marketing New Project",
      "nestedEntities":{
         "tasks":[
            {
               "sort_order":"10",
               "name":"1 of 2 Tasks"
            },
            {
               "sort_order":"11",
               "name":"2 of 2 Tasks"
            }
         ],
         "projects":[
            {
               "sort_order":"10",
               "name":"1 Project with 2 Nested tasks here",
               "nestedEntities":{
                  "projects":[
                     {
                        "sort_order":"10",
                        "name":"1 of 2 Project Nested"
                     },
                     {
                        "sort_order":"11",
                        "name":"2 of 2 Project Nested"
                     }
                  ]
               }
            },
            {
               "sort_order":"10",
               "name":"1 Project"
            }
         ]
      }
   }
]

Server response

Every request to the TimeTac API will have:

  • a well formed JSON response (default)
  • a well formed XML response ("Accept" header set to application/xml, like "Accept: application/xml")

1. x-www-form-urlencoded

Key Type Description
Host String The host that processed the request.
Success Boolean False if any error occurred while processing the request.
SuccessNested Boolean True only if Success of this request, and Success of all nested child requests (if any), are true.
NumResults Integer The number of results contained in a successful response.
NumResultsNested Integer The number of results contained in a successful response. NumResults and all NumResults of nested children.
ResourceName String The name of the resource that was requested.
Results Array If the requested action provides a result set and the set is not empty, this array will contain all resource entities as JSON objects.
Error Integer If an error occurred, this will contain the error code so a client can react accordingly (see “Error handling”).
ErrorMessage String A human readable error message that will explain the error. These messages are only available in English.

Example: JSON response


{
   "Host":"go.timetac.com",
   "Success":true,
   "NumResults":1,
   "ResourceName":"User",
   "Results":[
      {
         "id":"1",
         "internal_user_group":"1",
         "active":"1",
         "hr_manager":"1",
         "department_id":"3",
         "username":"manager",
         "lastname":"Mustermann",
         "firstname":"Max",
         ...
      }
   ]
}

Example: XML response


<?xml version="1.0"?>
<response>
    <Host>go-dev.timetac.com</Host>
    <Success>1</Success>
    <NumResults>2</NumResults>
    <ResourceName>user</ResourceName>
    <RequestStartTime>2017-07-18 14:28:41</RequestStartTime>
    <RequestEndTime>2017-07-18 14:28:41</RequestEndTime>
    <ServerTimeZone>Europe/Vienna</ServerTimeZone>
    <Results>
        <id>1</id>
        <internal_user_group>1</internal_user_group>
        <active>1</active>
        <hr_manager>1</hr_manager>
        <department_id>1</department_id>
        <username>manager</username>
        <lastname>Mustermann</lastname>
        <firstname>Erika</firstname>
    </Results>
    <Results>
        <id>10</id>
        <internal_user_group>2</internal_user_group>
        <active>1</active>
        <hr_manager>0</hr_manager>
        <department_id>1</department_id>
        <username>mustermann</username>
        <lastname>Mustermann</lastname>
        <firstname>Max</firstname>
    </Results>
...
</response>

2. JSON (Batch response)

Key Type Description
Host String The host that processed the request.
Success Boolean False if any error occurred while processing the request
NumResults Integer The number of results contained in a successful response
NumErrors Integer The number of failed batch processes, if no errors happened the field is not delivered in the response
isBatch Integer If the request was a batch request (multiple parameter sets) its value is set to 1, else the field is not delivered in the response
ResourceName String The name of the resource that was requested.
Results Array If the requested action provides a result set and the set is not empty, this array will contain all resource entities as JSON objects.
Error Integer If an error occurred, this will contain the error code so a client can react accordingly (see “Error handling”).
ErrorMessage String A human readable error message that will explain the error. These messages are only available in English.

Example: JSON response


{
   "Host":"go-dev.timetac.com",
   "Success":true,
   "NumResults":3,
   "isBatch":1,
   "ResourceName":"messages",
   "Results":[
      {
         "Success":true,
         "NumResults":1,
         "Results":[
            {
               "id":146,
               "sender_id":1,
               "message":"Hello Max, I will be late today so please meet me at 10:00. Thank you",
               "timestamp":"2017-01-27 07:00:00"
            }
         ]
      },
      {
         "Success":true,
         "NumResults":1,
         "Results":[
            {
               "id":147,
               "sender_id":1,
               "message":"Thanks for the coffee, we appreciate it :)",
               "timestamp":"2017-01-27 07:00:00"
            }
         ]
      },
      {
         "Success":true,
         "NumResults":1,
         "Results":[
            {
               "id":148,
               "sender_id":1,
               "message":"Who is Max? Max is dead, baby",
               "timestamp":"2017-01-27 07:00:00"
            }
         ]
      }
   ]
}

Example: XML response


<?xml version="1.0"?>
<response>
    <Host>go-dev.timetac.com</Host>
    <Success>1</Success>
    <NumResults>2</NumResults>
    <isBatch>1</isBatch>
    <ResourceName>messages</ResourceName>
    <RequestStartTime>2017-07-18 15:04:50</RequestStartTime>
    <RequestEndTime>2017-07-18 15:04:50</RequestEndTime>
    <ServerTimeZone>Europe/Vienna</ServerTimeZone>
    <Results>
        <Success>1</Success>
        <NumResults>1</NumResults>
        <Results>
            <id>16</id>
            <sender_id>1</sender_id>
            <message>Hello Max, I will be late today so please meet me at 10:00. Thank you</message>
            <receiver_type>ALL</receiver_type>
            <receiver_id />
            <include_sub_department>0</include_sub_department>
            <data_changed>2017-07-18 15:04:50</data_changed>
            <timestamp_update>2017-07-18 15:04:50</timestamp_update>
            <timestamp>2017-01-20 07:00:00</timestamp>
        </Results>
    </Results>
    <Results>
        <Success>1</Success>
        <NumResults>1</NumResults>
        <Results>
            <id>17</id>
            <sender_id>1</sender_id>
            <message>Thanks for the coffee, we appreciate it :)</message>
            <receiver_type>ALL</receiver_type>
            <receiver_id />
            <include_sub_department>0</include_sub_department>
            <data_changed>2017-07-18 15:04:50</data_changed>
            <timestamp_update>2017-07-18 15:04:50</timestamp_update>
            <timestamp>2017-01-20 07:00:00</timestamp>
        </Results>
    </Results>
</response>

Error handling

If any error occurred while the request was processed, the “Success” key of the response will be set to false. In that case, two new keys will be included in the response JSON, a error code in “Error” and a human readable description in “ErrorMessage”.

A client should always at least check the error code in “Error” and try to implement a basic form of error correction if possible. The following table should help with identifying a problem:

Key Description
400 Server received a bad request. Most of the time this is due to a missing required parameter.
401 Client tried to access a forbidden resource. This may occur if your account tries to access restricted information or the provided user credentials are wrong.
403 Server received an unauthenticated request. The client did not implement a HTTP Basic authentication header.
422 The request parameters described an unprocessable entity, probably because of a parameter had an invalid value.
500 An internal server error occurred on the backend side.
503 The service is temporary unavailable. The server is experiencing technical difficulties which prevented it from processing the request.

Resources

The following chapters will explain all resources that are available through the TimeTac API and how they should be handled. The API will enable you to create, read, update and delete (CRUD) resources in the TimeTac application, but depending on the resource you are requesting some of these operations may be not available due to security restrictions.

In almost all cases, you will have to adjust your request method according to the type of action you want to call. The API will respond to the following request methods as defined below:

  • GET
    Read only request, these Requests will never affect the state of a resource.

    • Parameters are expected to be part of the URI query string
  • POST
    Creates a new entity of the requested resource. When called multiple times in a row, each request will lead to a completely new entity being created.
    If possible, the created resource will be included in the result of the response.

    • Parameters are expected to be x-www-form-urlencoded
  • PUT
    Updates an existing resource. These requests are idempotent, so identical subsequent calls won’t change the result beyond the initial application.
    If possible, the created resource will be included in the result of the response.

    • Parameters are expected to be in the raw body
  • DELETE
    Completely removes the specified entity.

    • Parameters are expected to be part of predefined URI segments

For each resource, you will find a table with all the parameters that are supported, alongside with their description, datatype and availability for the different actions:

Name Type Description C(reate) R(ead) U(pdate)
Name/key of the parameter
  • Integer
  • Whole numbersChar
  • A single character.String
  • If not stated otherwise, all unicode characters.Boolean
  • Either “1” or “0”
  • Decimal
    If not stated otherwise, 10 digits to the left, and 2 digits to the right of the decimal point.
  • Date
    Values in “YYYY-MM-DD” format.
  • DateTime
    Values in “YYYY-MM-DD HH:MM:SS” format
Short explanation
  • “-“ Not supported
  • “O” Optional
  • “R” Required

The following parameters are available for all resources:

Name Type Description C R U
_limit Integer Limits the amount entries in the result.
Default value: 100
O
_offset Integer If there are more results than can be displayed in one response (> limit), offset will skip this many entries and returns the next page of the resultset. O
_order_by String Any of the supported parameters name. This will sort the resultset by the specified field in alphanumerical order. O
_order_desc Boolean If set to “1”, the resultset will be sorted in reversed alphanumerical order. O
_op__<fieldname> String If specified, the search will use the given operand instead of comparing for equality. Supported operands are:
eq
Equal (=), default
gt
Greater than (>)
gteq
Greater than or equal (>=)
lt
Less than (<)
lteq
Less than or equal (<=)
in
Pipe separated list of values to check against. E.g.: _op__id=in&id=1|2|3
betw
Filter results to range between two pipe separated values.
first|second is evaluated as first <= result <= second.
E.g.: _op__timestamp=betw&timestamp=2017-01-16 00:00:00|2017-02-05 10:58:00
like
Checks if the given value if part of the actual field value.
_aggregate__<columnname>=sum String Supported funtions are:

  • “sum”: sum
  • “avg”: average
  • “max”: maximum
  • “min”: minimum
  • “count”: count
  • “group_concat”: E.g.: _aggregate__columnName=group_concat

Relation Resolution

Resources can now configure relations in their scheme, which can then be resolved on demand into a display object (= JSON object with only public fields). A client can request to resolve a relation with the “_resolve” meta parameter, by specifying a comma seperated list of fields to resolve. Resolveable fields will be specified in the API documentation for each resource.

  • Resource: user/read/
  • Relation: “department_id” can be resolved into a “department” display object
  • Parameter: ?_resolve=department_id
  • Result: Response will contain the department_id as usual, but in addition, it will append a key “department” with a JSON object as its value

Aggregated Resources

We are now supporting aggregated resources, which are basically only endpoints that can handle multiple existing resources at once.

Inclusion/Exclusion

Aggregated resources can have a set of whitelisted resources to choose from, a default set of activated resources returned, or both. In either way, the client can include or exclude permitted resources from the response by specifying a list of comma seperated resourcenames:

Inclusion

Parameter: _include
Example: ?_include=user,department
Effect: Will return only the specified resources in the response

Exclusion

Normal resource parameter: ?id=5&_op__id=lt
Aggregated resource parameter: ?user__id=5&user___op__id=lt

Resource Parameter

An aggregated resource has no resource parameter on its own, instead it allows for prefixed parameters for each individual resource. So in order to filter an individual resource, you will have to prefix the usual parameter with a “<resourcename>__” string. Keep in mind that the resourcename has to be seperated by two underscores from the parameter, that means that meta parameters which are also prefixed by an underscore lead to three subsequent underscores, this is intended!

Filter user by id less than 5

Normal resource parameter: ?id=5&_op__id=lt
Aggregated resource parameter: ?user__id=5&user___op__id=lt

As you can see with the “_op__” parameter, meta parameter can also be prefixed and therefore limited to a specific resource. Other than resource parameter they are permitted to be specified without a prefix though, which will lead to them being passed on to all resources.

Delta Parameter

The meta parameter “_since” takes a serialized datetime as parameter. It limits the results to entries that have been created or updated at or after that datetime value. "_since" is available on every resource that tracks the latest change to an entry. If specified, the resource will map the provided since parameter to the resource specific “ON UPDATE CURRENT_TIMESTAMP” field, but the resource can provide a different implementation for the “_since” logic if necessary.

Example

Get all entries that were modified since the beginning of 2020
?_since=2020-01-01 00:00:00

In order to additionally obtain all elements deleted after "_since", "_fetch_deleted=1" can be added to the query. An array containing all that elements that have been deleted will be included to the response using the "Deleted" key.

Example

Get all entries that were created, deleted or modified since the beginning of 2020
?_since=2020-01-01 00:00:00&_fetch_deleted=1

The response will include the deleted elements. For example:

{
"Results": [...],
"Deleted": [
    {
      "id": "372576",
      "deleted_at": "2020-10-05 15:27:40"
    },
    {
      "id": "401087",
      "deleted_at": "2020-11-16 13:15:56"
    },
    ...]
}

If no elements have been deleted, the array with the key "Deleted" will be empty.

Aggregated resources List (Delta sync)

  • user
  • department
  • skills
  • messages
  • notifications
  • projects
  • tasks
  • generalSettings
  • timePlannings
  • holidayRequests
  • teams
  • nodesToUsers
  • absenceDays
  • timesheetAccountings
  • timesheetActionLogs
  • timestampChangelogs
  • nfcTransponder
  • timetrackings
  • timeTrackings
  • multiuserToTasks
  • clients
  • translations
  • checkpoints
  • checkpointTranslations
  • checkpointTrackings
  • permissions
  • absences
  • absenceTypes
  • absenceBans
  • permissionResolveUsers
  • permissionResolveAbsenceTypes
  • permissionResolveQuestions
  • permissionResolveDepartments
  • serverTime
  • timesheetAccountingSummaries
  • userStatusOverview
  • changeTimeTrackingRequests
  • notificationUrls
  • notificationsTypeHtml