GuidesAPI ReferenceChangelog
Log In
Guides

Implement Taxes

Pinwheel’s API makes it easier to develop tax filing solutions by simplifying the process of retrieving tax forms. Instead of asking the end user to provide the paperwork manually, users can leverage Pinwheel's payroll connectivity to provide access to their tax documents seamlessly.

Once an end user connects their payroll_ account, a webhook is sent, notifying you of the available tax forms. You can then retrieve the relevant tax forms via an API call using the tax forms ID.

Prerequisites

Link SDK version 2.4

In order to enable Taxes, the Link SDK used in your application must be upgraded to version 2.4 or later. We recommend upgrading to the latest version for maximum conversion.

API and Webhook Version v2023-11-22

To use the latest functionality of Taxes, you should upgrade to v2023-11-22. Breaking changes for each API version upgrade are listed in our breaking change list. Functionality of older versions can be found by using the API version dropdown in the top left corner of this page.

Step 1: Subscribe to the webhook events

Subscribe to the following webhook event to receive instant notifications when tax data becomes available or Monitoring detects changes in a user's information.

  • tax_forms.added: Notifies you when information about tax forms is available. Specify this webhook inenabled_events.

Register a webhook endpoint using Pinwheel's Create Webhook endpoint, as the following example illustrates.

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": [
    "tax_forms.added",
  ]
}

A webhook is published when tax forms are ready to download and new data is available. You receive a single webhook with metadata about each tax form added or deleted in the job. See Tax Forms Webhook Events for webhook payload schemas and example payloads.

📘

The end_user_id is your internal reference to the end user. See User Model for more information.

Step 2: Create a Link token

Launch the Pinwheel Link that your customers can use to connect a new account. To initialize Pinwheel Link, your server-side code generates a short-lived Link token by sending a POST request to the /link_tokens endpoint. Link tokens are intended to be single-use and expire after 15 minutes. Your server must generate a new link token each time you want to launch Pinwheel Link.

Pass tax_forms into the required_jobs when creating the Link Token to indicate that tax forms are required.

See the Implementation Guide for additional customization parameters for Link.

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": ["tax_forms"]
}

Pass the token from the response to one of the Link SDKs to launch a Link session inside your application.

⚙️

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: Initialize Link

Use the Link token to open the Link modal in your client application. In addition to passing in the token, you can optionally pass several callback handlers.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.getpinwheel.com/pinwheel-v2.3.js"></script>
    <script>
      Pinwheel.open({
        linkToken: "INSERT LINK TOKEN",
          onSuccess: (result) => {
            console.log("Job succeeded!");
        },
      });
    </script>
  </head>
  <body></body>
</html>

The onSuccess callback handler is executed on job success and contains metadata about the direct deposit job.

Step 4: Respond to webhook events

After a user successfully links their payroll account, Pinwheel will collect the user's tax forms data and send a tax_forms.added webhook event.

Step 5: Retrieve the tax forms

You must first get the tax form ID to retrieve a tax form. You can obtain this ID either via the tax_forms.added webhooks or via the Tax Forms LIST endpoint.

Once you have the tax form ID, you can call the Tax Forms GET endpoint to retrieve the tax form. There are two options for retrieving tax forms:

Document only

This is the default option. It provides a download_url that you can use to download the PDF document.

Request

GET /v1/accounts/449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65/tax_forms/b0e92f7b-c0b2-4418-a34d-3f316ca45c84
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET

Response

{
    "data": {
        "id": "b0e92f7b-c0b2-4418-a34d-3f316ca45c84",
        "created_at": "2022-07-12T17:32:23.162839-04:00",
        "account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",
        "form_type": "W-2",
        "year": 2021,
        "document": {
            "id": "8b15fc92-4221-44bd-bac1-1121641ccffd",
            "download_url": [tax forms download URL],
            "download_url_expiration": "2022-07-18T17:47:52.782334+00:00"
        },
        "parsed_data": null,
        "warnings": null
    }
}

Document + parsed data

🚧

Parsed data is currently only available for W-2 documents

This option provides the PDF document in addition to optical character recognition (OCR) to convert the document into machine-encoded text that is appended to the default response. The parsed data makes it faster to process the document without manually reviewing or applying OCR after retrieving it.

Add the with_parsed_data=true query parameter to retrieve the tax form with parsed data. If the Pinwheel cannot provide parsed data, the request fails. You can then retry the request with query param with_parsed_data=false to retrieve the document only.

For security purposes, the download_url is only active for 15 minutes. Download the document as soon as you retrieve the download_url. If the download_url has expired, call /tax_forms endpoint to generate a new, active download_url.

See the full Tax Forms API reference here.

Request

GET /v1/accounts/449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65/tax_forms/b0e92f7b-c0b2-4418-a34d-3f316ca45c84?with_parsed_data=true
Host: api.getpinwheel.com
Content-Type: application/json
x-api-secret: YOUR-API-SECRET

Response

{
    "data": {
        "id": "b0e92f7b-c0b2-4418-a34d-3f316ca45c84",
        "created_at": "2022-07-12T17:32:23.162839-04:00",
        "account_id": "449e7a5c-69d3-4b8a-aaaf-5c9b713ebc65",
        "form_type": "W-2",
        "year": 2021,
        "document": {
            "id": "8b15fc92-4221-44bd-bac1-1121641ccffd",
            "download_url": [tax forms download URL],
            "download_url_expiration": "2022-07-18T17:47:52.782334+00:00"
        },
        "parsed_data": {
            "box_a": "XXX-XX-1234",
            "box_b": "123456789",
            "box_c": {
                "name": "SAMPLE COMPANY INC",
                "address": {
                    "raw": "123 MAIN ST, ANYWHERE, CA 123456 1234",
                    "line1": "123 MAIN ST",
                    "line2": null,
                    "city": "ANYWHERE",
                    "state": "CA",
                    "postal_code": "123456",
                    "country": "US"
                }
            },
            "box_d": "000011 R#/123",
            "box_e": "JOHN SMITH",
            "box_f": {
                "raw": "1234 S MAPLE ST, ANYWHERE, CA 123456",
                "line1": "1234 S MAPLE ST",
                "line2": null,
                "city": "ANYWHERE",
                "state": "CA",
                "postal_code": "123456",
                "country": "US"
            },
            "box_1": 2350000,
            "box_2": 150000,
            "box_3": 2350000,
            "box_4": 145700,
            "box_5": 2350000,
            "box_6": 34057,
            "box_7": null,
            "box_8": null,
            "box_9": null,
            "box_10": null,
            "box_11": null,
            "box_12": [
                {
                    "code": "W",
                    "amount": 50000
                },
                {
                    "code": "DD",
                    "amount": 2100
                }
            ],
            "box_13": {
                "statutory_employee": false,
                "retirement_plan": false,
                "third_party_sick_pay": false
            },
            "box_14": null,
            "box_15_to_20": [
                {
                    "box_15_state": "CA",
                    "box_15_employer_state_id": "12345678901ABC",
                    "box_16": 2350000,
                    "box_17": 80000,
                    "box_18": null,
                    "box_19": null,
                    "box_20": null
                }
            ]
        },
        "warnings": null
    }
}

Interested in integrating tax forms into your workflow? Reach out to [email protected] for access today!