Implement Switch Kit

Overview

This guide explains how to enable Switch Kit for an end user through Pinwheel's SDK.

Switch Kit allows users to perform an direct deposit switch and switch bills from their existing bank accounts and credit cards in the same session.

For the deposit switch flow, in just a few clicks, users can change where they receive part or all of their paycheck.

For the bill switching phase, the user connects to external bank accounts and credit cards, and Pinwheel automatically identifies which recurring bills & subscriptions are paid out of those accounts. The user can then perform an automated payment method switch to the card or bank account of their choice.

Prerequisites

Link SDK version 3.0

In order to use Bill Navigator, the Link SDK used in your application must be upgraded to version 3.0 or later. We recommend upgrading to the latest version for maximum conversion.

API and Webhook Version v2025-07-08

The use of Bill Navigator requires version v2025-07-08 or higher. 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: Set up access to external account transactions

In order to identify an end user's recurring bills and subscriptions, Pinwheel needs access to transaction data from external user accounts that those bills are paid out of. Pinwheel supports the Plaid banking aggregator for retrieving transaction data from external bank and credit card accounts.

Pinwheel leverages the Plaid account you already have, so that you continue to own and control that data. Pinwheel gets access to transaction data through Plaid Processor tokens, which are tightly scoped to allow a specific partner access to a specific data stream (e.g. transactions data for a given user's account).

In order to facilitate this access, here are the requirements needed for Pinwheel to successfully integrate your Plaid account/connection. This includes analyzing existing connected accounts for the user and connecting new accounts within the user experience.

Step 1a: Set up Pinwheel partnership configuration within your Plaid dashboard

This step is unique per customer. We will provide details at our implementation kickoff.

Step 1b: Create an API to create and return a Plaid link token for a given user

If the end user has not yet connected external accounts within your app already, Pinwheel will need a Plaid link token to facilitate connecting a new account. To this end, Pinwheel requires an API endpoint that will provide a Plaid link token for the user.

The API endpoint should be a POST endpoint that accepts a JSON schema as input. See Authorization below for notes on securing the endpoint.

Example path: POST https://<url>/.../plaid/link_tokens

Pinwheel will provide the values for all four of the fields in this request:

{
	"user_id":"<user_id>",
	"webhook":"<webhook>",
	"redirect_uri":"<redirect_uri>",
	"completion_redirect_uri":"<completion_redirect_uri>"
}

The endpoint you build will make a call to Plaid's Link Token Create API using the above data as input. The following configuration is also necessary:

  • Enable Product transactions
  • Add Account filters:
    • Depository; checking & saving
    • Credit; credit cards
  • Request 730 days of data for proper recurring transaction analysis.
  • Include the supplied webhook and redirect_uri in the top level
  • Add a hosted_link object including the supplied completion_redirect_uri and set the flag is_mobile_app to True

The API's response body should be a JSON object that contains the link token and the hosted link URL:

{
	"plaid_link_token": "<link_token>",
	"hosted_link_url":"<hosted_link_url>"
}

👍

See Appendix for code sample implementing this endpoint

Step 1c: Create an API to exchange public token for processor tokens

Once the user has connected to their bank or credit card platform, Plaid returns a public_token to Pinwheel. This next API endpoint will receive this public_token from Pinwheel, and return one or more processor_tokens back to Pinwheel. It is these processor tokens that Pinwheel will use to retrieve the transaction data necessary to identify recurring bills.

The API endpoint should be a POST endpoint that accepts a JSON schema as input. See Authorization below for notes on securing the endpoint.

Example path: POST https://<url>/.../plaid/processor_tokens

Pinwheel will provide the value for the public_token in this request:

{
	"public_token":"<public_token>",
}

This API will take the provided public token and exchange it for an access token using the Plaid public token exchange endpoint.

With the access token, you can now retrieve the list of accounts associated with this access token using the Plaid accounts/get endpoint.

Finally, for each account, you will create a processor token using Plaid's processor token create endpoint.

We recommend storing both the access token and the processor tokens for later use. The third API endpoint (see below) returns existing processor tokens that have already been created; this saves the user from having to connect to their account on subsequent visits.

The API endpoint should return the processor tokens using this JSON schema:

{
	"processor_tokens": [...],
}

👍

See Appendix for code sample implementing this endpoint

Step 1d: Create an API to return all processor tokens for a user

This third endpoint simply returns any processor tokens that are already available for the given user.

The API endpoint should be a GET endpoint that includes a user ID in the path or query string. See Authorization below for notes on securing the endpoint.

Example path: GET https://<url>/.../plaid/processor_tokens?user_id=<user_id>

Pinwheel will provide the value for the user_id.

This API should simply return any processor tokens that have already been created and stored for the given user. Note that if the user has already connected an external account for a different purpose, processor tokens can be generated on the fly for Pinwheel (exactly as in the previous API), so long as you have stored the access token associated with the user's bank or credit card vendor.

The API endpoint should return the processor tokens using this JSON schema:

{
	"processor_tokens": [...],
}

Authorization

To secure these endpoints, we recommend a simple shared-secret approach as described below, optionally augmented with IP whitelisting that rejects requests from any unknown IP address. However, Pinwheel can usually accommodate whatever authorization scheme is in place for any existing public APIs you have already implemented. Please flag this for us during implementation kickoff if the shared secret approach is not viable.

Shared secret

In this scheme, you will provide Pinwheel with a secret during implementation time. Pinwheel offers both sandbox and production environments, so a separate secret is required for each.

Pinwheel will send the secret in the Authorization header in all API requests:

"Authorization": f"Basic {secret}"

Step 2: Create a Pinwheel Link token

Once the banking aggregator endpoints are implemented, you're ready to focus on the real-time Switch Kit user experience.

Start by creating a Link Token for Switch Kit. To do so, you'll need to provide the following:

  • The solution field must be set to "Switch Kit"
  • The features field must specify both "direct_deposit_switch" and "bill_switch"
  • For Direct Deposit Switch:
    • Details for the bank account(s) that the user can choose to receive direct deposits (e.g. type, routing number, account number, and an account nickname when applicable)
    • If you're using PreMatch, you'll need to provide the user details required to leverage the superior conversion and user experience that PreMatch offers.
  • For Bill Switch:
    • Debit card details for the bank account(s) that the user can choose to switch bills to
  • A unique identifier for the user that you can resolve to your own internal user model

See Link Token Creation docs for full details on required fields.

Here's an example Switch Kit link token:

POST /v1/link_tokens
Host: api.getpinwheel.com
Content-Type: application/json
Pinwheel-Version: 2025-07-08
x-api-secret: YOUR-API-SECRET
{
  "solution": "Switch Kit",
  "features": ["direct_deposit_switch", "bill_switch"],
  "org_name": "YOUR APP NAME",
  "end_user_id": "my_user_12345",
  "allocation": {
    "targets": [
      {
        "name": "My Checking Account",
        "type": "checking",
        "routing_number": "07464755",
        "account_number": "193464372203"
      }
    ]
  },
  "cards": [
   {
      "card_name": "Card Name",
      "card_number": "12345678912345",
      "cvc": "111",
      "expiration_date": "05/29",
      "name_on_card": "Jane Doe",
      "card_zip_code": "12345",
      "billing_address": "123 Lane St",
      "billing_address2": "Apt 987",
      "city": "New York",
      "state": "NY"
    }
  ],
  // required for PreMatch; otherwise not needed
  "end_user": {
    "platform_matching": {
      "social_security_number": "123456789",
      "mobile_phone_number": "0123456789",
      "home_address_zip_code": "12345",
      "first_name": "John",
      "last_name": "Smith",
      "date_of_birth": "1999-12-31",
      "email": "[email protected]"
    }
  },
}

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-v3.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 success of both direct deposit switches and bill switches and contains metadata about each. It is important to note that multiple switches can be executed within a single Pinwheel link flow.

Example onSuccess result for a direct deposit switch:

{  
  "account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d",  // user's account on the platform
  "platform_id": "97f420ff-5d0a-46ee-9cfc-6f17d5d31256", // payroll platform 
  "job": "direct_deposit_switch",  
  "params": {  
    "action": "full_switch",  
    "allocation": {  
      "target": {  
        "account_type": "checking"  
      }  
    }  
  }  
}  

Example onSuccess result for a bill switch:

{  
  "account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d",  // user's account on the platform
  "platform_id": "97f420ff-5d0a-46ee-9cfc-6f17d5d31256", // merchant (e.g. Netflix)
  "job": "bill_switch",  
  "params": {  
    "payment_method": {
      "type": "card",
      "card": {  
        "card_name": "Card Name",
        "card_num_last4": "2345",
      }  
    }  
  }  
}

Step 4: Respond to Webhook Events

Deposit Switching

After a user successfully logs into their payroll account, an account.added webhook event is published. Using either the end_user_id or the link_token_id, you can associate the account with the user who logged in with Link. For example:

{
  "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",
    "platform_name": "Workday",    
    "created_at": "2021-01-12T02:36:01.287148+00:00",
    "connected": true
  }
}

Once the user has completed the deposit switch, a direct_deposit_switch.added webhook event is published. For example:

{
  "event": "direct_deposit_switch.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": "97f420ff-5d0a-46ee-9cfc-6f17d5d31256",
    "name": "direct_deposit_switch",
    "timestamp": "2021-01-12T02:36:01.287148+00:00",
    "outcome": "success",
    "params": {
      "action": "partial_switch",
      "allocation": {
        "type": "amount",
        "value": 5400,
        "target": {
          "account_name": "My Checking Account",
          "account_type": "checking",
          "last_four_account_number":"1234"
        }
      }
    }
  }
}

Bill Switching

After a user successfully logs into the merchant, an account.added webhook event is published. Similarly to Deposit Switch, an account_id is generated that represents the end user's account with the merchant they are switching payment methods for.

Using either the end_user_id or the link_token_id, you can associate the account with the user who logged in with Link. For example:

{
  "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", // refers to the merchant (e.g. Netflix)
    "platform_name": "Netflix",
    "created_at": "2021-01-12T02:36:01.287148+00:00",
    "connected": true
  }
}

Once the user has completed the deposit switch, a bill_switch.added webhook event is published. For example:

{
  "event": "bill_switch.added",
  "event_id": "5a141122-4235-4fa1-bd76-0628573880b0",
  "payload": {
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "account_id": "03bbc20e-bc39-464a-b4dc-4b63ffb7213d", // users's account with the merchant
    "platform_id": "fce3eee0-285b-496f-9b36-30e976194736", // refers to the merchant (e.g. Netflix)
    "platform_name": "Netflix",    
    "end_user_id": "my_user_12345",
    "link_token_id": "97f420ff-5d0a-46ee-9cfc-6f17d5d31256",
    "name": "bill_switch",
    "timestamp": "2021-01-12T02:36:01.287148+00:00",
    "outcome": "success",
    "params": {  
      "payment_method": {
        "type": "card",
        "card": {  
          "card_name": "Card Name",
          "card_num_last4": "2345",
        }  
      }  
    }  
  }
}

Step 5: Returning users

Pinwheel will use end_user_id passed in the Link token to keep track of returning users for recurring transaction monitoring and continued updating. Users who have previously connected an account will maintain their list of recurring transactions. For this use case a link token can be created exactly as above.

POST /v1/link_tokens
Host: api.getpinwheel.com
Content-Type: application/json
Pinwheel-Version: 2025-07-08
x-api-secret: YOUR-API-SECRET
{
  "solution": "Switch Kit",
  "features": ["direct_deposit_switch", "bill_switch"],
  "org_name": "YOUR APP NAME",
  "end_user_id": "my_user_12345",
  "allocation": {
    "targets": [
      {
        "name": "My Checking Account",
        "type": "checking",
        "routing_number": "07464755",
        "account_number": "193464372203"
      }
    ]
  },
  "cards": [
   {
      "card_name": "Card Name",
      "card_number": "12345678912345",
      "cvc": "111",
      "expiration_date": "05/29",
      "name_on_card": "Jane Doe",
      "card_zip_code": "12345",
      "billing_address": "123 Lane St",
      "billing_address2": "Apt 987",
      "city": "New York",
      "state": "NY"
    }
  ],
  // required for PreMatch; otherwise not needed
  "end_user": {
    "platform_matching": {
      "social_security_number": "123456789",
      "mobile_phone_number": "0123456789",
      "home_address_zip_code": "12345",
      "first_name": "John",
      "last_name": "Smith",
      "date_of_birth": "1999-12-31",
      "email": "[email protected]"
    }
  },
}

Once you've created the Link token, follow Step 3 to initialize Link.

Appendix: API code samples

Sample code for Plaid Link Token endpoint

request = LinkTokenCreateRequest(
   user=LinkTokenCreateRequestUser(
       client_user_id=user_id,
   ),
   client_name='Switch Kit',
   products=[Products('transactions')],
   transactions=LinkTokenTransactions(
       days_requested=730
   ),
   country_codes=[CountryCode('US')],
   language='en',
   account_filters=LinkTokenAccountFilters(
       depository=DepositoryFilter(
           account_subtypes=DepositoryAccountSubtypes([
               DepositoryAccountSubtype('checking'),
               DepositoryAccountSubtype('savings')
           ])
       ),
       credit=CreditFilter(
           account_subtypes=CreditAccountSubtypes([
               CreditAccountSubtype('credit card')
           ])
       ),
   )
   webhook={webhook},
   redirect_uri={redirect_uri},
   hosted_link=LinkTokenCreateHostedLink(
       is_mobile_app=True,
       completion_redirect_uri={completion_redirect_uri},
   ),
)
response = client.link_token_create(request)
link_token = response['link_token']
hosted_link_url = response['hosted_link_url']
return {
   plaid_link_token: link_token,
   hosted_link_url: hosted_link_url
}

Sample code for Plaid Processor Token Exchange endpoint

# Exchange the public token from Plaid Link for an access token.
exchange_request = ItemPublicTokenExchangeRequest(public_token=public_token)
exchange_token_response = client.item_public_token_exchange(exchange_request)
access_token = exchange_token_response['access_token']

# Fetch all accounts for this access token
accounts_request = AccountsGetRequest(access_token=access_token)
accounts_response = client.accounts_get(accounts_request)
accounts = accounts_response['accounts']

# Create processor tokens for all accounts
processor_tokens = []
for account in accounts:
    create_request = ProcessorTokenCreateRequest(
        access_token=access_token,
        account_id=account['account_id'],
        processor='scribeup'
    )
    create_response = client.processor_token_create(create_request)
    processor_tokens.append(create_response['processor_token'])