Development Toolkit

Introduction to development

What does add-on consist of?

Each add-on has three main elements:

  • Manifest 
  • Business logic
  • UI (optional)

Manifest describes add-on’s capabilities and the way it integrates with the Clockify app.

Business logic represents the functionalities provided by an add-on. It can be presented on both, the frontend and backend side. The UI, however, will be displayed on frontend through the configured components.

UI is a visual representation of an add-on displayed to users. It is optional part of an add-on. Add-ons that don’t contain UI can be developed to perform various functions, e.g. handling webhook events, data import and export, third-party integrations and so on. 

UI components use the same base URL as add-ons and must be accessible when making GET requests for a specified path.

How does add-on hosting infrastructure work? 

Add-on resources are not hosted on their own. You must host all the resources needed for an add-on to function, including a manifest file, a web server to handle communication with Clockify and any other integral part of an add-on e.g. UI.
You need to make sure that all the resources mentioned above are working and accessible. 

Add-on code examples

Add-on code examples are used to demonstrate how to use add-on’s specific features or functionality. These examples are tested and functional, therefore you can use them as a reference and build upon them to create your own custom integrations.

Add-on SDK 

Add-on SDK is written in Java and aims to help you with the development of your add-ons. It contains various modules to help you with the development, including schema models, validators, helpers, as well as support for web frameworks. 

Add-on web components

Add-on web components are a set of components and CSS styles aimed to help you develop your UIs, and, at the same time maintain a design style that is consistent with the CAKE.com style guide.
For more information, visit the Add-on web components documentation

Which settings need to be made to start building add-on? 

Add-on needs various configurations and inputs from users in order to be useful and serve its purpose. That’s why it needs a mechanism to store its settings. 

How does Clockify interact with add-on through UI?

If your add-on contains some additional UI elements, your manifest can specify where those elements will be in the Clockify UI and user access permissions that they will have.
There are three types of UI locations in Clockify where an add-on element can be added.

UI locations are loaded and rendered in iFrame. Each iFrame has its own URL. This URL loads what the component renders. URL also contains an ‘auth_token’ parameter and its value is JWT provided by Clockify. That JWT is generated for user that currently views the iFrame and it lasts 30 minutes. With this JWT, add-on can refer to Clockify API as the user that’s currently viewing that iFrame. Also, this JWT can be also used to validate requests that come from add-on frontend to add-on backend, so that the add-on could confirm that the request comes from Clockify, by using Clockify public key.

JWT token contains additional information located in the JWT claims.

ClaimExampleDescription
backendUrlhttps://api.clockify.me/apiBackend API url
ptoUrlhttps://pto.api.clockify.mePTO API url
reportsUrlhttps://reports.api.clockify.meReports API url
typeaddonClockify token type
theme darkAdd-on theme from Clockify user settings
languageENSelected language in Clockify user settings
workspaceId64676cb639febb722cf8beefWorkspace ID
addonId64676eb039febb722cf8bf09Add-on workspace installation ID
user64676cb639febb722cf8beeeLogged-in user ID

While widgets and/or sidebar tabs are rendered inside the Clockify app, the actual component itself is loaded and rendered inside an iFrame. To render a UI component, the app loads the component’s path inside an iFrame.  

Most of the requests that Clockify sends to an add-on are authenticated. The “Clockify-Signature” request header will contain a JWT token which must be used in order to verify the authenticity of the request. 

Using configurable no-code UI

Add-on settings can be defined in manifest with Clockify taking care of both rendering them to the user and storing the data. Whenever user updates the settings, add-on will be notified through a lifecycle event. This approach is the fastest way to get started with building add-ons and supports building customizable settings screens in a straightforward way.
Supported types of settings are defined in the manifest.  

Using a custom UI component

Add-on can be configured to define and host its own Settings screen. This setup can be beneficial if the UI is complex, if you’d like to store “settings” in your own infrastructure, or if the “settings” need to follow a specific design.
In order to use a custom UI component, you need to define it in the manifest. The defined component will be rendering the UI and persisting the “settings”.  

How does it work? 

After user installs an add-on, the add-on is added to the workspace and its assets are loaded whenever user loads the Clockify app.

There are three ways in which Clockify interacts with the add-on:

  • Lifecycle events: Add-on receives events when installed, deleted, if its settings are updated, or status is changed  
  • Webhooks: Add-on receives events for all the webhooks it has defined in the manifest
  • Components: Add-on receives requests to render a component whenever user navigates to it 

Add-on lifecycle

A general lifecycle of an add-on includes the following events:

  • Installed
  • Settings updated
  • Status changed
  • Deleted

Installed

Add-on is installed on a workspace. After installation, a lifecycle event is triggered and the add-on is provided with the installation context and an API token.

Example of INSTALLED lifecycle request that Clockify sends to add-ons whenever a lifecycle hook occurs:

POST INSTALL

Request Headers

  • Content-Type : application/json
  • X-Addon-Lifecycle-Token : {{token}}
{
  "addonId": "62ddf9b201f42e74228efa3c",
  "authToken": "{{token}}",
  "workspaceId": "60332d61ff30282b1f23e624",
  "asUser": "60348d63df70d82b7183e635",
  "apiUrl": "{{apiUrl}}",
  "addonUserId": "1a2b3c4d5e6f7g8h9i0j1k2l",
  "webhooks": {
     "path": "https://example.com/webhook"
     "webhookType": "ADDON"
     "authToken": "{{token}}"
  }, 
}

The API token is required for all authenticated requests to the Clockify API. For more information, go to Authentication and authorization.

Settings updated and status changed

After installation, add-on is automatically enabled and becomes active. Active add-on integrates with the Clockify API in order to provide added functionality to the application. 

Add-on can integrate with Clockify through:

  • Clockify webhooks

Add-on can listen for any event that triggers a webhook request. This can be used to trigger business logic, integrate with third-party platforms, or export data. 

  • External triggers

Add-on can act upon an event triggered by a third-party platform and perform its business logic using the Clockify API. 

This can be used in a wide range of applications for provisioning users from identity provider, importing / synchronizing data between Clockify and third-party platform and automating tasks.

  • User-triggered events

Add-on can act upon an event triggered by user and perform its business logic using the Clockify API. User can interact with the add-on’s UI components in order to use these extra functionalities that the add-on provides. That can be, for example, to configure and generate reports from the Clockify app, perform automated tasks, etc.     

Examples of STATUS-CHANGED and SETTINGS-UPDATED lifecycle request that Clockify sends to add-ons whenever a lifecycle hook occurs:

STATUS CHANGED DISABLED

Request Headers

  • Content-Type : application/json
  • X-Addon-Lifecycle-Token : {{token}}
{
  "addonId": "62ddf9b201f42e74228efa3c",
  "workspaceId": "60332d61ff30282b1f23e624",
  "status": "INACTIVE"
}

STATUS CHANGED ENABLED

Request Headers

  • Content-Type : application/json
  • X-Addon-Lifecycle-Token : {{token}}
{
  "addonId": "62ddf9b201f42e74228efa3c",
  "workspaceId": "60332d61ff30282b1f23e624",
  "status": "ACTIVE"
}

SETTINGS UPDATED

Request Headers

  • Content-Type : application/json
  • X-Addon-Lifecycle-Token : {{token}}
{
  "workspaceId": "60332d61ff30282b1f23e624",
  "addonId": "62ddf9b201f42e74228efa3c",
  "settings": [
    {
      "id": "txt-setting",
      "name": "Txt setting",
      "value": "Some text"
    },
    {
      "id": "link-setting",
      "name": "Link setting",
      "value": "https://clockify.me"
    },
    {
      "id": "number-setting",
      "name": "Number setting",
      "value": 5
    },
    {
      "id": "checkbox-setting",
      "name": "Checkbox setting",
      "value": true
    },
    {
      "id": "dropdown-single-setting",
      "name": "Dropdown single setting",
      "value": "option 1"
    },
    {
      "id": "dropdown-multiple-setting",
      "name": "Dropdown multiple setting",
      "value": [
        "option 1",
        "option 2"
      ]
    }
  ]
}

Deleted

When an add-on is deleted from a workspace, a lifecycle event is triggered and the add-on is provided with a required context. Add-on’s API token becomes invalid and from that moment on, add-on can no longer interact with the Clockify API on behalf of the workspace user. 

Example of DELETED lifecycle request that Clockify sends to add-ons whenever a lifecycle hook occurs:

DELETED

Request Headers

  • Content-Type : application/json
  • X-Addon-Lifecycle-Token : {{token}}
{
  "addonId": "62ddf9b201f42e74228efa3c",
  "workspaceId": "60332d61ff30282b1f23e624",
  "asUser": "60348d63df70d82b7183e635"
}

Are there any additional tools? 

There are some additional tools that CAKE.com provides to help you easily get your add-ons up and running and speed up the process of development. 

Add-on endpoints

Manifest schema

This endpoint gets JSON manifest schema.

/clockifyUrl/addons/manifest-schema

Parameters:

  • version – e.g. 1.0

This parameter is not required. If no parameter is sent with the request, or you put “latest” as the value, you will get the latest version of the schema.
To get older versions, you need to specify version number.

Settings

There is an endpoint that gets the settings hosted on a Clockify side for an add-on.

/addon/workspaces/{workspaceId}/settings

Parameters that need to be sent include:

  • workspaceId – ID of workspace from which the ‘settings’ are retrieved

Authentication with an add-on token is required to access this endpoint.

Settings refers to both, the workspace and the user for whom the add-on is provided.

Request

GET url-to-clockify-backend/addon/workspaces/6138751f050bf21482aad3b2/settings

Response

{
    "tabs": [
        {
            "name": "Tab one title",
            "id": "tab one id",
            "groups": [
                {
                    "id": "group nested in tab",
                    "title": "Group title",
                    "description": "group description",
                    "header": {
                        "title": "Header title"
                    },
                    "settings": [
                        {
                            "id": "Setting id",
                            "name": "Default setting",
                            "description": "Description of default setting",
                            "placeholder": "Default setting here...",
                            "type": "TXT",
                            "value": "Value of default setting",
                            "required": true,
                            "copyable": true,
                            "readOnly": false,
                            "accessLevel": "ADMINS"

                        }
                    ]
                }
            ],
            "header": {
                "title": "title header 2"
            },
            "settings": [
                {
                    "id": "Setting id 2",
                    "name": "Default setting",
                    "description": "Description of default setting",
                    "placeholder": "Default setting here...",
                    "type": "TXT",
                    "value": "Value of default setting 2",
                    "required": true,
                    "copyable": true,
                    "readOnly": false,
                    "accessLevel": "ADMINS"
                }
            ]
        }
    ]
}

 Update settings  

This endpoint is restricted to admin users only.

Request

PATCH /addon/workspaces/{workspaceId}/settings

Request body

[
    {    
         "id": "settingId",
         "value": "New value of setting"
     }
 ]

Response

 [
    {
        "id": "640717dae2172c18ab1a1830",
        "workspaceId": "6137bb5addd64b2759e031e8",
        "userId": "6137bb5addd64b2759e031e7",
        "addonKey": "example-addon",
        "name": "Example addon",
        "description": "Addon that serves as example for others.",
        "status": "ACTIVE",
        "url": "https://example.com/",
        "components": [
            {
                "type": "sidebar",
                "options": {
                    "option 1": "option 1 value",
                    "option 2": "option 2 value"
                },
                "accessLevel": "ADMINS",
                "label": "Some sidebar",
                "path": "/sidebar",
                "iconPath": null
            }
        ],
        "settings": {
            "tabs": [
                {
                    "name": "Tab one",
                    "id": "Tab one",
                    "groups": [
                        {
                            "id": "Group nested in tab",
                            "title": "Group nested in tab",
                            "description": "Group description",
                            "header": {
                                "title": "Group nested in tab header"
                            },
                            "settings": [
                                {
                                    "id": "Group nested in tab link",
                                    "name": "Group nested in tab link",
                                    "description": null,
                                    "placeholder": null,
                                    "type": "LINK",
                                    "allowedValues": null,
                                    "required": false,
                                    "key": null,
                                    "value": "https://clockify.me",
                                    "copyable": false,
                                    "readOnly": false,
                                    "accessLevel": "EVERYONE"
                                }
                            ]
                        }
                    ],
                    "header": {
                        "title": "Tab one header"
                    },
                    "settings": [
                        {
                            "id": "Flat tab setting",
                            "name": "Flat tab setting",
                            "description": null,
                            "placeholder": null,
                            "type": "TXT",
                            "allowedValues": null,
                            "required": false,
                            "key": null,
                            "value": "flat tab setting value",
                            "copyable": false,
                            "readOnly": false,
                            "accessLevel": "ADMINS"
                        }
                    ]
                }
            ]
        },
        "addonApiToken": null
    }
]

Get current user

This endpoint is used to retrieve current user.

Request

GET /addon/workspaces/{workspaceId}/user

Response

{
     "userId": "6137bb5addd64b2759e031e7",
     "name": "John Doe",
     "email": "john.doe@example.com"
 }

Generate user addon token

Endpoint

POST /addon/user/{userId}/token

Description

This endpoint generates a user-specific addon token using an addon installation token and an active user’s ID. Generated token is valid for 30 minutes.

Parameters that need to be sent include:

 userId –  The unique identifier of the active user for whom the addon token is generated.

Request Headers

  • Content-Type: application/json
  • X-Addon-Token: {{token}}

Response

Upon successful generation, the endpoint returns the user addon token in the response body.

{

  "userAddonToken": {{userToken}}

 }