Integrating Payroll / EWA

This step-by-step guide will walk you through creating a payroll or EWA (Earned Wage Access) integration with 7shifts through the 7shifts REST API.

🚧

MAPPING NOTE

This section requires an understanding of how to map locations, departments, and roles.

Upon completion of this guide, you will have created an integration that can interact with the 7shifts platform as follows:

  • Establish entity mapping between your payroll system & 7shifts
  • Read raw clock in/clock out data from 7shifts

What’s in it for the client?

  • Ability to set up integrated payroll processing flow where data is read automatically from 7shifts and brought into your payroll system.
  • Ensuring unpaid & paid breaks are separated when processing payroll.
  • Ensuring only approved punches are included for processing payroll.

Requirements

  • An API key or OAuth Client for a test/sandbox 7shifts account
  • cURL or Postman to make test requests

Restrictions

  • You may only make 10 requests per second per token across all API endpoints.
  • When synchronizing time punch data from 7shifts, please schedule it between 08:00:00 UTC and 15:00:00 UTC on the day of or the day before payroll gets processed to ensure any recent updates to time punches in 7shifts are being reflected in the payroll data.

Functionality Overview

The payroll integration involves reading raw time punch data such as clock in, clock out, break in & break out time, and using that data to calculate the total number of worked hours in your system.
You can then calculate the total worked hours data per employee on your side, multiply it by the employee’s assigned wages, and use the calculated earned wages as input for your payroll processing.

You will be interacting with the following 7shifts API endpoints for building this integration:

NOTE: Our preferred method to sync time_punches changes is to subscribe to our time_punches webhook events. If your application supports webhooks, read our webhooks Introduction and contact us to subscribe.

This section focuses on:

  • Structure of GET requests to the List Time Punches endpoint:
    • Get familiar with the request you need to make to read a list of time punch data.
  • Narrowing the request scope:
    • Understand how you can read time punches by location, by department or by user, how to filter by time range and how to page through responses.
  • Understanding the response:
    • Extract relevant information on time punches and breaks from the response

GET Structure for /time_punches

Time punch data in 7shifts can be read through the /time_punches endpoint, either one punch at a time or by getting punch data in a list.

An example GET request to the /time_punches endpoint to list some time punch data looks as follows:

Request URL

curl --request GET --url 'https://api.7shifts.com/v2/company/1234/time_punches'

By default, without any additional URL query parameters, this request will return the first 20 punches, order by earliest created.

Webhooks time_punch Events

If you support webhooks, we highly encourage you to subscribe to time_punches events to automatically receive real-time changes. This minimize the number of GET requests you have to execute to keep track of time punch information. We offer three topics to on the time_punch event:

For most EWA integrators you are looking to see when a time punch has been approved. We encourage you to subscribe to the time_punch.edited topic as it will notify you when time_punches are approved. You can monitor incoming webhook events for time_punches and inspect the approved flag value.

Filtering by Time

Since you will be reading time punch data from 7shifts in the context of a specific pay period, you will want to bind the request scope to a certain timeframe.

You can do this by limiting your request to only retrieve time punches that have a clock in start time that falls within the pay period you are looking for. This can be done with the help of the following URL query parameters:

ParameterTypeDescription
clocked_in[gte]DateTimeStart time range (UTC in ISO8601 format: YYYY-MM-DDTHH:mm:SS)
clocked_in[lte]DateTimeEnd of time range (UTC in ISO8601 format: YYYY-MM-DDTHH:mm:SS)

An example GET request to the /time_punches endpoint with clocking filtering:

curl --request GET --url 'https://api.7shifts.com/v2/company/1234/time_punches?clocked_in[gte]=2022-06-01T00%3A00%3A00&clocked_in[lte]=2022-06-10T00%3A00%3A00'

Filtering by Location, Department and/or User

It is also recommended that you retrieve time punches only for a specific location, department or user at a time to make it easier for you to process the response. This can be done with the help of following URL query parameters:

ParameterTypeDescription
location_idIntegerID of the location you want to retrieve time punches for.
department_idIntegerID of the department you want to retrieve time punches for.
user_idIntegerID of the user you want to retrieve time punches for.

All the above query parameters can also be used in combination. Here’s an example request on how to get all time punches for user ID 12345 that were created at location ID 98765 for department ID 24680, on & between Aug 16 2020 and Aug 31 2020 (UTC):

curl --request GET --url 'https://api.7shifts.com/v2/company/1234/time_punches?user_id=12345&location_id=98765&department_id=24680&clocked_in/[gte/]=2020-08-16T00:00:00&clocked_in/[lte/]=2020-08-31T23:59:59'

Retrieve Location Timezone

You should reference the locations timezone to localize the time_punch data. Retrieve the data in the location’s local timezone as specified in 7shifts by getting a locations information and read the timezone parameter.

curl --request GET --url 'https://api.7shifts.com/v2/company/1234/locations/98765

Paging through responses

Finally, in the response, you may notice that you only see the 20 time punches that match the filter criteria which were created the earliest. This is because our API supports “pagination” by using limit can cursor URL query parameters together to get the remaining results.

These parameters are described below:

ParameterTypeDescription
limitIntegerMaximum number of objects you want in the response (default = 20, min = 1, max = 200).
cursorStringThe cursor used to paginate previous and next results.

For example,

If the request finds a total of 57 time punches matching the criteria, you can make the following request to get a list of the first 20 time punches created at the location (results #1-#20):

curl --request GET --url 'https://api.7shifts.com/v2/company/1234/time_punches?location_id=98765&clocked_in[gte]=2020-08-16 00:00:00&clocked_in[lte]=2020-08-31 23:59:59&limit=20'

In the resulting payload you will receive the pagination cursors in the meta attribute.

{
    "data": [
        {...
        }
    ],
    "meta": {
        "cursor": {
            "current": "eyJpZCI6Ijg1Njg1NjYwIiwibmV4dCI6dHJ1ZX0=",
            "prev": "eyJpZCI6Ijg1NzgxNzY4IiwibmV4dCI6ZmFsc2V9",
            "next": "eyJpZCI6Ijg2MDIzMDM2IiwibmV4dCI6dHJ1ZX0=",
            "count": 20
        }
    },
    "object": "time_punches"
}

The following request will return the next 20 time punches (results #21-#40):

curl --reuqest GET --url 'https://api.7shifts.com/v2/company/1234/time_punches?cursor=eyJpZCI6Ijg2MDIzMDM2IiwibmV4dCI6dHJ1ZX0='

To get the the last 17 time punches (results #41-57) you would use the next cursor value in a subsequent request.

When the number of returned time punch objects in the response is less than the limit, that’s how you can determine for certain that the end of the results list has been reached.

Understanding the response

Now that you know how to make a basic request to read time punch data and how to use certain filters with it, let’s explore the response. We’ll use this example response to understand what fields are important:

Example Response

{
    "data": [
        {
            "id": 852369,
            "company_id": 1234,
            "shift_id": 0,
            "user_id": 1111,
            "editable_punch": false,
            "role_id": 0,
            "location_id": 98765,
            "department_id": 0,
            "hourly_wage": 15.5,
            "approved": true,
            "clocked_in": "2021-11-26T15:12:00+00:00",
            "clocked_out": "2021-11-26T20:47:00+00:00",
            "notes": "",
            "auto_clocked_out": false,
            "clocked_in_offline": false,
            "clocked_out_offline": false,
            "tips": 0,
            "created": "2021-11-26T15:16:29+00:00",
            "modified": "2021-11-26T20:51:41+00:00",
            "deleted": false,
            "pos_type": "toast",
            "breaks": [
              {
                "id": 1472,
                "user_id": 1111,
                "custome_break_id": ,
                "paid": true,
                "in": "2021-11-26T17:00:00+00:00",
                "out": "2021-11-26T17:15:00+00:00"
              }
            ]
        }, ...
    ],
    "meta": {
        "cursor": {
            "current": "",
            "prev": null,
            "next": "eyJpZCI6Ijg1Njg1NjYwIiwibmV4dCI6dHJ1ZX0=",
            "count": 20
        }
    },
    "object": "time_punches"
}

A few things to note:

  • Within the response, every object in the data array represents one time punch.
  • Within each object, the time_punch object represents details on the actual punch itself, and every object the break array would represent every individual break that was taken during the worked shift.
  • If the break array is empty, that means that no breaks were taken.

Using the location_id, department_id, role_id & user_id fields and pairing that up with the respective mappings that were established through the mapping process, you should be able to determine who a certain time punch was for, and what location, department & role they were working for that shift.

Outside of those identifier fields, you will also need to look at these fields:

ParameterTypeDescription
clocked_inDateTimeTimestamp denoting when the employee clocked in (UTC).
clocked_outDateTimeTimestamp denoting when the employee clocked out (UTC).
approvedBooleanWhether the punch was approved in 7shifts or not.

Here are some things to note on each of the above fields:

clocked_in & clocked_out:

  • Subtracting the clocked_in time from the clocked_out time should give you the amount of time that the user worked (excluding breaks).

approved:

  • Time punches have the approved field set to true if they were either manually approved by a manager in 7shifts, or were auto-approved since they aligned with the time punch rules, or the punches originated from a POS system that were then brought in through an integration.
  • In most cases, you want to ensure that this field is set to true before accounting it for payroll processing.
ParameterTypeDescription
paidBooleanWhether the break was a paid break or not.
inDateTimeTimestamp denoting when this break started (UTC).
outDateTimeTimestamp denoting when this break started (UTC).

Here are some things to note on each of the above fields:

paid

  • For every individual break, this boolean field determines whether the break is supposed to be a paid break or not.

in & out

  • For each break that has the paid field set to false, subtracting the in time from the out time should give you the amount of time that the user went on a break for.
  • Please ensure that breaks with the paid field set to true aren’t being subtracted as paid break time would account for the total hours worked.

Putting all the info from these three sub-sections together, the integration should be able to calculate how many hours an employee worked in total, excluding unpaid breaks, within the time range of the pay period you are looking to process payroll for.

You can then use this information with wage information that you may already have in your system to determine how much an employee has earned for their worked hours, which in turn can be used as an input for your payroll processing product.

Tip

You should provide a setting on the configuration page for this integration which would allow an admin to choose whether they wish to include all breaks, only paid breaks or no breaks at all for payroll processing.

You should also provide an option that allows the admin to choose to process only approved time punches in 7shifts for payroll. Changing this option should directly enable/disable the check for the approved field being set to true.

Different managers have different flows on how they approve time punches in 7shifts, and allowing them a setting to choose would help ensure that your payroll integration fits their flow regardless of how they handle time punch approval in 7shifts.