Tip
Watch the video demo to get an overview of the integration before starting the guide!In this guide
- How to set up a Clockify account.
- How to stand up a server to convert Clockify Time Entry data to Zeal Employee Checks.
- What the code does.
Clockify account setup
First, we’ll create a Clockify account and generate an API key. Navigate to the Clockify Signup Page and follow the steps to create an account. Once your account is created, navigate to your user settings, scroll to the bottom, and click Generate under the API section.
Stand up the server
In this step, we’ll set up a Node (Express) server to receive incoming data from Clockify. In order for webhooks from Clockify to reach our local server, we’ll need to expose it to the internet. There are many ways of doing this, but we’ll be using ngrok.Associate a Zeal employee with our Clockify account
The first thing we’ll want to do before we get our server up and running, is establish a connection between our Zeal Employee and our Clockify Account. In a production application, we’d establish a process to do this automatically whenever a new employee onboards to Clockify, but for now we’ll do it manually. Using your Clockify API key from the previous step, run the following curl command.id field from the response to add an external_id to your Zeal Employee.
Set up an Express server
Clone our example repository at https://github.com/zeal-corp/zeal-clockify-integration-example..env in the root directory and add your Zeal Test API Key and the Company ID.
Note
For the scope of this example, we’ll only process time entries for one company so use the ID of the company containing the Zeal Employee from the previous step.npm run dev. You should see the following output in your terminal.
Expose the server to the public web using ngrok
Next we’ll need to install ngrok and configure our ngrok authorization token. Follow the instructions here to do so. Once the configuration of ngrok is complete we can run the following line in the terminal to expose our Express server to the internet.Forwarding field and store it for use in the next step. Note: don’t stop ngrok or your Express server.
Add a webhook URL in Clockify
Now we want to configure our webhook in our Clockify dashboard. Navigate back to the user settings in Clockify and click Manage webhooks at the bottom.
/time-entry/timer-stopped (the path in our controller).

Testing the webhook
Now for the fun part: testing that it works! Navigate to the time tracker in Clockify and click the “Project” button.

Note
You must wait at least 18 seconds because shifts are measured on an hourly basis. We must have a time duration that’s enough to be rounded to at least .01 hours otherwise we’ll receive an error in the logs.201 HTTP response in the ngrok server.
Check the logs
Additionally, we can check the Clockify webhook logs to verify that a check was created. Navigate back to the user settings in Clockify and click Manage webhooks at the bottom.

employeeCheckID.

Code review
The app (server) has two main sections that would be helpful to review:- The initial configuration on startup.
- The handler for the
/time-entry/timer-stoppedroute.
App configuration
Looking intosrc/config/app.config.ts we can see that there are several variables that the app is dependent on that are being configured.
Note:
In a production app, we’d likely rely on a database to store data such as thecompanyID and defReportingPeriods, but we wanted to keep this app as simple as possible so we chose to use this workaround.zealClient: this is basically an wrapper for the commonly used axios library for making HTTP requests. This Zeal module found insrc/services/zealsimply helps us make requests to Zeal’s API.companyID: nearly all requests to Zeal’s API require a company ID to be passed as a parameter so we initialize that here for later use.defReportingPeriods: this establishes a list of Reporting Periods that our app can reference internally, instead of having to make a call to the Zeal API every time we receive Time Entry data from Clockify.
Tip:
You can see the reporting periods the app is establishing by visiting http://localhost:3000/reporting-periods after the app starts.defReportingPeriods:
Every Zeal Employee Check should be scoped to a particular Reporting Period that describes when the work was completed. Zeal supports every potential Reporting Period for a given calendar year. Since our app is determining the Reporting Period automatically, based off the time entry data we get from Clockify, it’s useful to filter the all potential Reporting Periods down to only the ones that fit our desired pay schedules.
For the purpose of this example, the logic found in src/config/defReportingPeriods.config.ts defines a ruleset that employees should be on a weekly pay schedule and that the Reporting Periods should end one week before any given payday.
Note
For Example:If the payday is “Fri”, then Reporting Period for a 2022-09-30 check date should be 2022-09-17 - 2022-09-23.Time Entry Controller
The main functionality of the app is defined insrc/controllers/timeEntry.controller.ts. Looking into the code, we can see there are 3 main process the app performs whenever it receives Time Entry data from Clockify:
- The app pulls the Clockify
userIdfrom the request body and makes a call to Zeal to find an Employee with a matchingexternal_idfield.
- It pulls the
starttime from the request body and searches ourdefReportingPeriodsto find the Reporting Period that thestarttime falls within.
- It checks if there is an existing Employee Check for this Reporting Period and then makes a request to Zeal to either create a new check or update the existing check.