GuidesAPI ReferenceChangelog
Log In
Guides

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 about end_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 collected
  • shifts.thirty_days_synced: Triggered when 30 days of shifts have been collected
  • shifts.ninety_days_synced: Triggered when 90 days of shifts have been collected
  • paystubs.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.