Connect a Time & Attendance Account
While many companies have time and attendance (T&A) data available via their payroll platform, some companies record employee shifts data in a separate system. To account for this, we've built out coverage for the major T&A platforms, such as Humanity, Timesheets.com, When I Work, Deputy, Homebase, and many more.
If you need shifts data and your users work at employers which have separate T&A platforms, you can use Link to connect to these T&A accounts and retrieve shifts, employment, and identity information.
You can query GET /v1/platforms?type=time_and_attendance
(docs) to see which T&A platforms are currently supported.
Implementation
Step 1: Subscribe to Webhook Events
Pinwheel executes data retrieval in the background, so the best way to be notified when data is available is to subscribe to webhook events. As a user's account is connected and data is collected, events are posted to your webhook to alert your service that the data is ready for consumption.
Register a webhook endpoint using the POST /v1/webhooks with earnings_stream.payouts.refreshed
specified in the enabled_events
. See Webhooks for additional configuration details and instructions on authenticating webhook events.
POST /v1/webhooks
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET
{
"url": "https://your-domain.com/webhook_endpoint",
"status": "ACTIVE",
"enabled_events": [
"account.added",
"employment.added",
"shifts.added",
"identity.added"
]
}
T&A accounts support the following webhook events:
account.added
, a new account has successfully been connected (docs).account.monitoring_status.updated
, Pinwheel's ability to monitor a T&A account has changed (docs ).employment.added
, new employment data is available for the account (docs).identity.added
, new identity data is available for the account (docs).shifts.added
, new shifts data is available for the account (docs).
Development Tip
Use a tool like Webhook.site to see webhook events in real-time without writing any code. Once you've developed a webhook endpoint, use a tool like ngrok to run it on your local machine and receive webhook events.
Step 2: Connect an Account
Step 2a: Create a Link Token
If you implemented your own search
If no platform_id
is included in the webhook event, show users a search screen for platforms where the attribute platform_type
is set to time_and_attendance
.
Once the user selects a T&A platform, create a Link token with platform_type
set to time_and_attendance
and platform_id
or employer_id
set to the user’s selection. shifts
should be the only job in the required_jobs
list.
If you use Link’s search
Create a Link token with platform_type
set to time_and_attendance
. shifts
should be the only job in the required_jobs
list.
Link Configuration
end_user_id
is required in all Link tokens as of API version 2023-07-18. See the User Model for more information aboutend_user_id
. Visit the Implementation Guide to see additional customization parameters for Link.
Step 2b: Launch Link
You'll pass the token
from the response to one of the Link SDKs to launch a Link session inside your application.
See Link SDK Errors for the various states you should handle.
Development Tip
Test this flow without writing any code using the Console to create a Link Token and launch a Link session in the Dashboard.
Step 3: Handle Account Creation
The account.added
webhook event is published after a user logs in to their payroll account. Using the end_user_id
you provided during Link Token creation, you can associate the account with the user who logged in with Link.
{
"event": "account.added",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload": {
"account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d",
"end_user_id": "my_user_12345",
"link_token_id": "97f420ff-5d0a-46ee-9cfc-6f17d5d31256",
"platform_id": "fce3eee0-285b-496f-9b36-30e976194736",
"created_at": "2021-01-12T02:36:01.287148+00:00",
"connected": true
}
}
Step 4: Retrieve Data
Step 4a: Handle Data Retrieval Updates
Subsequent webhook events are published when data for a job is available. For example, the employment.added
event is sent once employment data is ready.
{
"event": "employment.added",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload": {
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d",
"end_user_id": "my_user_12345",
"link_token_id": "7c4ac4be-4a0e-4468-ab26-c42b249b233b",
"name": "employment",
"timestamp": "2021-01-21T20:16:28+00:00",
"outcome": "success",
"error_code": null
}
}
{
"event": "paystubs.added",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload": {
"id": "24fe697f-a893-42a5-adca-a727f11ad792",
"account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d",
"end_user_id": "my_user_12345",
"link_token_id": "7c4ac4be-4a0e-4468-ab26-c42b249b233b",
"name": "paystubs",
"timestamp": "2021-01-21T20:16:28+00:00",
"outcome": "success",
"error_code": null,
"added": [{"id": "dbff9830-55ce-4aa5-82f1-e04f196da041"}],
"deleted": [{"id": "57043868-6fb3-47e8-a2b7-c993bd023f25"}],
"params": {
"from_pay_date": "2020-10-01",
"to_pay_date": "2020-12-31",
"count": 6,
"sync_status": "in_progress",
}
}
}
{
"event": "income.added",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload":{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",
"end_user_id": "my_user_12345",
"link_token_id": "4787acbc-11cf-4db3-998c-5ea7c4feebcd",
"name": "income",
"timestamp": "2021-01-12T02:36:01.287148+00:00",
"outcome": "success"
}
}
{
"event": "identity.added",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload":{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",
"end_user_id": "my_user_12345",
"link_token_id": "4787acbc-11cf-4db3-998c-5ea7c4feebcd",
"name": "identity",
"timestamp": "2021-01-12T02:36:01.287148+00:00",
"outcome": "success"
}
}
{
"event": "shifts.added",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload":{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",
"end_user_id": "my_user_12345",
"link_token_id": "4787acbc-11cf-4db3-998c-5ea7c4feebcd",
"name": "shifts",
"timestamp": "2021-01-12T02:36:01.287148+00:00",
"outcome": "success",
"added": [{"id": "dbff9830-55ce-4aa5-82f1-e04f196da041"}],
"deleted": [{"id": "57043868-6fb3-47e8-a2b7-c993bd023f25"}],
"params": {
"count": 14,
"sync_status": "complete",
}
}
}
For accounts with a lot of data, gathering every available shift can take quite some time. To communicate this progress, shifts.added
webhook events have a payload.parms.sync_status
field letting you know when Pinwheel is done syncing data from your user's payroll platform. A status of in_progress
means we are still collecting data, while a status of complete
mean we are done.
If you would instead prefer to be notified after specific days of data have been synced, you can subscribe to any of the sync status events which fire as soon as the relevant data has been retrieved. The sync status events are:
shifts.seven_days_synced
: Triggered when 7 days of shifts have been collectedshifts.thirty_days_synced
: Triggered when 30 days of shifts have been collectedshifts.ninety_days_synced
: Triggered when 90 days of shifts have been collectedpaystubs.fully_synced
: Triggered when all available shifts have been collected
You can use these sync events to fit your use case. E.g, if you only require the most recent week containing shifts, you can subscribe to shifts.seven_days_synced
. This event will trigger once 7 days has elapsed from the first observed shift, i.e. you will be notified as soon as the first shift is available. Similarly, you would subscribe to the paystubs.thirty_days_synced
event if you need at least 1 month of shifts.
For Shifts, follow the same convention but swap out the prefix, e.g., shifts.seven_days_synced
.
{
"event": "paystubs.seven_days_synced",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload":{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",
"end_user_id": "my_user_12345",
"link_token_id": "4787acbc-11cf-4db3-998c-5ea7c4feebcd",
"name": "paystubs",
"timestamp": "2021-01-12T02:36:01.287148+00:00",
"outcome": "success",
"params": {
"from_pay_date": "2020-12-01",
"to_pay_date": "2020-12-31",
"count": 1
}
}
}
{
"event": "shifts.seven_days_synced",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload":{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",
"end_user_id": "my_user_12345",
"link_token_id": "4787acbc-11cf-4db3-998c-5ea7c4feebcd",
"name": "shifts",
"timestamp": "2021-01-12T02:36:01.287148+00:00",
"outcome": "success",
"params": {
"count": 6
}
}
}
Occasionally there might be a delay until the data is available. In these cases, we send a webhook with a pending
outcome. This indicates that the data retrieval was unable to be completed on its first attempt and will be retried up to 24 hours after the initial attempt. A webhook event with a pending
outcome will transition to either an error
or a success
within the 24 hour window.
Step 4b: Retrieve Data
Upon receiving the relevant webhook events, make API requests to retrieve data from the different Core Data endpoints.
For example, querying GET /v1/accounts/{account_id}/shifts
would return a response like:
{
"data": [
{
"id": "c134b9bd-29f8-452b-bfaa-0dbcc6f6f026",
"account_id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"created_at": "2021-01-29 22:06:06.920124+00:00",
"start_date": "2021-01-26",
"end_date": "2021-01-26",
"type": "shift",
"timezone": "America/New_York",
"timestamps": [
{
"from": "2021-01-26T12:00:00.745660+00:00",
"to": "2021-01-26T20:21:00.745660+00:00"
}
],
"earnings": [
{
"name": "Regular Hours",
"category": "hourly",
"hours": 8,
"rate": 2875,
"amount": 23000
},
{
"name": "Overtime",
"category": "overtime",
"hours": 0.35,
"rate": 4311,
"amount": 1509
}
],
"currency": "USD"
}
],
"meta": {
"count": 1
}
}
Step 5: [Optional] Perform an On Demand Update
You can refresh the data for a user by performing an On Demand Update.
Triggering an On Demand Update is similar to the standard Link flow in Step 2. However, when creating the Link token, pass in the account_id
parameter for an existing account. You can find the account_id
value for a user the account.added
webhook event, the login
client side event in Link, or by querying GET /v1/end_users/{end_user_id}/accounts
.
Launch Link using this new Link token to initiate a data refresh. Link will only prompt the user for input if the account connection is no longer persisted, e.g., if an MFA code needs to be entered.
Please contact [email protected] for access to our Dashboard.
Updated 3 months ago