Technical Implementation
This guide explains how to access Verify and handle updates. For an overview of concepts in the Pinwheel system that are referenced in this doc, review the Platform Overview.
Pinwheel Verify includes reports and core data to meet your needs. The biggest difference between the types of data is that reports are associated with an end_user_id
and individual data is associated with a payroll_account_id
. Reports aggregate data across payroll accounts.
[Beta] Verification Reports
Step 1: Subscribe to webhook events
Pinwheel sends webhook events to alert you when up-to-date Verify data is available or Monitoring has detected a change to a user's information.
Register a webhook endpoint using the POST /v1/webhooks with the data you're interested in receiving updates for (e.g. verification_reports.refreshed
). See Webhooks for additional configuration details and instructions on authenticating webhook events. See Pinwheel Verify Webhook Events for details on webhook event payloads.
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": [
"verification_reports.refreshed"
]
}
Step 2: Create a Link token
Create a Link token specifying the jobs that your application requires using required_jobs
.
POST /v1/link_tokens
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET
{
"org_name": "YOUR APP NAME",
"end_user_id": "my_user_12345",
"required_jobs": ["employment", "income"]
}
Note: The end_user_id
is your internal reference to the end user. See User Model for more information.
The response includes a short-lived token
that expires after 15 minutes and a unique id
. Persist the id
in your database.
{
"data": {
"id": "97f420ff-5d0a-46ee-9cfc-6f17d5d31256",
"expires": "2021-01-09T02:52:26+00:00",
"mode": "production",
"token": "eyJ0eXAiOiJKV1QiLCJhbGci...cyldX8fILelb6A0XKmdWsXZHMH4W1o",
}
}
Step 3: Initialize Link
Using the Link token that was created, open the Link modal in your client application. In addition to passing in the token, you can optionally pass in several callback handlers.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.getpinwheel.com/pinwheel-v2.3.js"></script>
<script>
window.addEventListener("load", () => {
Pinwheel.open({
linkToken: "INSERT LINK TOKEN",
});
});
</script>
</head>
<body></body>
</html>
Calling Pinwheel.open()
renders the Link modal in full screen mode.
Step 4: Respond to webhook events
The account.added
webhook event is published after a user logs in to their payroll account. Using the link_token_id
you persisted earlier, 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
}
}
Subsequent webhook events are published when data for a report has been updated.
{
"event": "verification_reports.refreshed",
"event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
"payload": {
"end_user_id": "my_user_12345",
"availability": {
"voe": {"status": "available", "updated_at": "2021-01-12T02:36:01.287148+00:00", "refreshed_at": "2021-01-12T02:36:01.287148+00:00"},
"voie": {"status": "available", "updated_at": "2021-01-12T02:36:01.287148+00:00", "refreshed_at": "2021-01-12T02:36:01.287148+00:00"}
}
}
}
Step 5: Retrieving data
To retrieve verification report data, you must use the end_user_id
from the webhook event that you received and the endpoint associated with the data you're retrieving.
An example for a verification of employment report is below.
Request
GET /v1/end_users/my_user_12345/verification_reports/voe
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET
Response
See the full Verification Reports API reference here.
{
"data": {
"document": {
"download_url": "download_url",
"download_url_expiration": "2024-01-06T17:47:52.782334+00:00",
"id": "15bbb566-fb4e-4835-9967-b308c164269e"
},
"employee": {
"address": {
"city": "Washington",
"country": "US",
"line1": "1234 Home Road",
"line2": null,
"postal_code": "20017",
"raw": "1234 Home Road, Washington, DC, 20017, USA",
"state": "DC"
},
"date_of_birth": "1987-02-01",
"last_four_ssn": "0090",
"name": {
"first_name": "Alicia",
"full_name": "Alicia Green",
"last_name": "Green",
"middle_name": ""
},
"phone_numbers": [
{
"type": "mobile",
"value": "+14155552671"
}
]
},
"employments": [
{
"employer_address": {
"city": "Washington",
"country": "US",
"line1": "1234 Main St",
"line2": "Suite 3",
"postal_code": "20036",
"raw": "1234 Main St, Suite 3, Washington, DC, 20036, USA",
"state": "DC"
},
"employer_name": "Acme Corporation",
"employment_duration_months": 23,
"employment_status": "employed",
"employment_type": "full time",
"most_recent_pay_date": "2021-01-05",
"start_date": "2020-06-01",
"termination_date": null,
"title": "Engineer"
}
],
"id": "aea8cc55-93fa-452d-8eac-b83c80b19a41",
"refreshed_at": "2021-01-06T15:59:13.530178+00:00",
"report_type": "voe",
"updated_at": "2021-01-06T15:59:13.530178+00:00"
}
}
Step 6 (Optional): Performing an On Demand Update
There are times when a user's account loses connection or you need the latest information for a user. We recognize that asking the user to log in multiple times introduces friction, so we've added the ability to re-access the payroll account without needing the user to re-enter credentials.
Enabling this functionality is similar to the way standard Link tokens are created. When creating the Link token, pass in the account_id
parameter from a prior account.added webhook event or from the login
client side event in Link. The account_id
can be preserved and passed in any time you have it for a user.
POST /v1/link_tokens
Host: api.getpinwheel.com
Content-Type: application/json
X-API-SECRET: YOUR-API-SECRET
{
"org_name": "YOUR APP NAME",
"end_user_id": "my_user_12345",
"required_jobs": ["employment", "income"],
"account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d"
}
It is not necessary to pass end_user_id
when creating Link tokens for On Demand Updates, as the information is persisted from the initial connection.
Once you've created the Link token, follow Step 3 to initialize Link. When the user is presented with the Pinwheel Link flow, they will be directed to the payroll platform associated with their account, bypassing login if possible.
Core Data
Step 1: Subscribe to webhook events
Pinwheel sends webhook events to alert you when up-to-date Verify data is available or Monitoring has detected a change to a user's information.
Register a webhook endpoint using the POST /v1/webhooks with the data you're interested in receiving updates for (e.g. employment.added
or income.added
). 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": [
"employment.added",
"income.added",
"paystubs.added"
]
}
Step 2: Create a Link token
Create a Link token specifying the jobs that your application requires using required_jobs
.
POST /v1/link_tokens
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET
{
"org_name": "YOUR APP NAME",
"end_user_id": "my_user_12345",
"required_jobs": ["employment", "income", "paystubs"]
}
Note: The end_user_id
is your internal reference to the end user. See User Model for more information.
The response includes a short-lived token
that expires after 15 minutes and a unique id
. Persist the id
in your database.
{
"data": {
"id": "97f420ff-5d0a-46ee-9cfc-6f17d5d31256",
"expires": "2021-01-09T02:52:26+00:00",
"mode": "production",
"token": "eyJ0eXAiOiJKV1QiLCJhbGci...cyldX8fILelb6A0XKmdWsXZHMH4W1o",
}
}
It is not necessary to pass end_user_id
when creating Link tokens for On Demand Updates, as the information is persisted from the initial connection.
Once you've created the Link token, follow Step 3 to initialize Link. When the user is presented with the Pinwheel Link flow, they will be directed to the payroll platform associated with their account, bypassing login if possible.
Step 3: Initialize Link
Using the Link token that was created, open the Link modal in your client application. In addition to passing in the token, you can optionally pass in several callback handlers.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.getpinwheel.com/pinwheel-v2.3.js"></script>
<script>
window.addEventListener("load", () => {
Pinwheel.open({
linkToken: "INSERT LINK TOKEN",
});
});
</script>
</head>
<body></body>
</html>
Calling Pinwheel.open()
renders the Link modal in full screen mode.
Step 4: Respond to webhook events
The account.added
webhook event is published after a user logs in to their payroll account. Using the link_token_id
you persisted earlier, 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
}
}
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 and paystub can take quite some time. To communicate this progress, both the paystubs.added
and 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. See Verify Webhooks Events for more details and additional webhook events for paystubs and shifts.
Step 5: Retrieving data
To retrieve core data, you must use the account_id
from the webhook event that you received and the endpoint associated with the data you're retrieving.
Examples for employment and income are below.
Employment
Request
GET /v1/accounts/03bbc20e-bc39-464a-b4dc-4b63ffb7213d/employment
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET
Response
See the full Employment API reference here.
{
"data": {
"account_id": "eb240ec6-a227-47ca-b7c7-fbbdfe7170a0",
"created_at": "2021-01-06T15:59:13.530178+00:00",
"employer_address": {
"city": "Washington",
"country": "US",
"line1": "1234 Main St",
"line2": "Suite 3",
"postal_code": "20036",
"raw": "1234 Main St, Suite 3, Washington, DC, 20036, USA",
"state": "DC"
},
"employer_name": "Acme Corporation",
"employer_phone_number": {
"type": "mobile",
"value": "+14155552671"
},
"id": "9d3309c3-b1d5-47dd-a175-20acf291f419",
"start_date": "2020-06-01",
"status": "employed",
"termination_date": null,
"title": "Engineer",
"updated_at": "2021-01-12T00:00:00.000000+00:00"
}
}
Income
Request
GET /v1/accounts/03bbc20e-bc39-464a-b4dc-4b63ffb7213d/income
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET
Response
See the full Income API reference here.
{
"data": {
"id": "6309e185-9c87-4384-aaae-767c7875775e",
"account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d",
"created_at": "2021-01-06T15:59:13.530178+00:00",
"compensation_amount": 400000,
"compensation_unit": "monthly",
"currency": "USD",
"pay_frequency": "monthly",
}
}
Step 6 (Optional): Query for Job Results
If your application never subscribed to webhook events, or your application server failed to handle the webhook events, you can query the Jobs endpoint with the link_token_id
used to initialize Link to fetch the results.
Request
GET /v1/jobs?link_token_id=97f420ff-5d0a-46ee-9cfc-6f17d5d31256&job_types=employment
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET
Response
{
"meta": {
"count": 1
},
"data": [
{
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d",
"link_token_id": "7c4ac4be-4a0e-4468-ab26-c42b249b233b",
"name": "employment",
"timestamp": "2021-01-12T02:36:01.287148+00:00",
"outcome": "success",
"params": {
"amount": null
}
}
]
}
Step 7 (Optional): Performing an On Demand Update
There are times when a user's account loses connection or you need the latest information for a user. We recognize that asking the user to log in multiple times introduces friction, so we've added the ability to re-access the payroll account without needing the user to re-enter credentials.
Enabling this functionality is similar to the way standard Link tokens are created. When creating the Link token, pass in the account_id
parameter from a prior account.added webhook event or from the login
client side event in Link. The account_id
can be preserved and passed in any time you have it for a user.
POST /v1/link_tokens
Host: api.getpinwheel.com
Content-Type: application/json
X-API-SECRET: YOUR-API-SECRET
{
"org_name": "YOUR APP NAME",
"end_user_id": "my_user_12345",
"required_jobs": ["employment", "income"],
"account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d"
}
It is not necessary to pass end_user_id
when creating Link tokens for On Demand Updates, as the information is persisted from the initial connection.
Once you've created the Link token, follow Step 3 to initialize Link. When the user is presented with the Pinwheel Link flow, they will be directed to the payroll platform associated with their account, bypassing login if possible.
Updated over 1 year ago