Already using Plaid? Generate a processor token with your existing integration and hand it to Paynote to instantly create a verified bank funding source — no micro-deposits required.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Add a Funding Source via Plaid Processor Token
Already using Plaid? Generate a processor token with your existing integration and pass it to Paynote to instantly add a verified bank account as a funding source.
Who is this for?
Merchants who already have an active Plaid integration and want to connect their customers' bank accounts to Paynote without starting a separate bank verification flow.
How It Works
The flow spans three parties: your server, Plaid, and the Paynote API. Your customer authenticates their bank through Plaid Link, you exchange the resulting tokens on your server, then pass a single processor_token to Paynote.
| Step | Who | What happens |
|---|---|---|
| 1 | Your server | Create a link_token via Plaid |
| 2 | Customer's browser | Launch Plaid Link → customer picks their bank account → returns public_token + account_id |
| 3 | Your server | Exchange public_token for access_token via Plaid |
| 4 | Your server | Call Plaid /processor/token/create with processor: "paynote" → get processor_token |
| 5 | Your server | POST processor_token + user_id to Paynote → funding source created instantly as Verified |
Before You Start
Make sure all of the following are in place before writing any code:
- ✅ You have an active Paynote account with a valid API key
- ✅ You have an active Plaid account with your
client_idandsecret - ✅ Paynote is enabled in your Plaid Dashboard — go to Developers → Integrations and click Enable next to Paynote
- ✅ Your Plaid Application Profile is complete (required by some banks)
- ✅ Your Plaid Link customization is configured with your ACH use cases
- ✅ The customer already exists in Paynote — if not, create them first and save the
user_id
Paynote must be enabled in your Plaid Dashboard
If Paynote is not enabled under Plaid's Integrations page, the
/processor/token/createcall will fail. This is the most common setup mistake — check this first if you get an error.
Step 1 — Create a link_token (your server)
link_token (your server)Generate a short-lived link_token from your backend. This authenticates Plaid Link for your user's session. Call this every time a user opens the Link flow — tokens are one-time use.
const { Configuration, PlaidApi, PlaidEnvironments } = require('plaid');
const plaidClient = new PlaidApi(new Configuration({
basePath: PlaidEnvironments[process.env.PLAID_ENV], // 'sandbox' or 'production'
baseOptions: {
headers: {
'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
'PLAID-SECRET': process.env.PLAID_SECRET,
'Plaid-Version': '2020-09-14',
},
},
}));
const response = await plaidClient.linkTokenCreate({
user: { client_user_id: 'your-internal-user-id' },
client_name: 'Your App Name',
products: ['auth'],
country_codes: ['US'],
language: 'en',
});
const linkToken = response.data.link_token;
// Send linkToken to your frontend to launch Plaid Link
Tip: Force single account selection
Set Account Select to "enabled for one account" in your Plaid Dashboard. This forces customers to select a single bank account so
metadata.accountsin theonSuccesscallback always has exactly one entry — no ambiguity about which account to use.
Step 2 — Launch Plaid Link (customer's browser)
Use the link_token from Step 1 to open the Plaid Link UI. When the customer authenticates and selects their account, onSuccess fires with a public_token and the selected account_id. Send both to your server.
<!-- Include the Plaid Link script -->
<script src="https://cdn.plaid.com/link/v2/stable/link-initialize.js"></script>
<button id="connectBtn">Connect Bank Account</button>
<script>
(async function () {
// Fetch the link_token your server created in Step 1
const { link_token } = await fetch('/create_link_token').then(r => r.json());
const handler = Plaid.create({
token: link_token,
onSuccess: function(public_token, metadata) {
const account_id = metadata.accounts[0].id;
// Send both to your server to complete Steps 3 & 4
fetch('/exchange_token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ public_token, account_id }),
});
},
onExit: function(err, metadata) {
if (err) console.error('Plaid Link error:', err);
},
});
document.getElementById('connectBtn').onclick = () => handler.open();
})();
</script>
Step 3 — Exchange tokens & create a processor_token (your server)
processor_token (your server)Receive the public_token and account_id from your frontend. Exchange the public_token for an access_token, then use both to generate a Paynote processor_token. Both API calls happen on your server — never expose your Plaid secret on the client.
async function createPaynoteProcessorToken(publicToken, accountId) {
// 3a — Exchange public_token → access_token
const tokenRes = await plaidClient.itemPublicTokenExchange({
public_token: publicToken,
});
const accessToken = tokenRes.data.access_token;
// 3b — Create a processor token scoped specifically to Paynote
const processorRes = await plaidClient.processorTokenCreate({
access_token: accessToken,
account_id: accountId,
processor: 'paynote', // ← must be exactly "paynote" (lowercase)
});
return processorRes.data.processor_token;
// → "processor-sandbox-0asd1-a92nc"
}
The
processorvalue must be exactly"paynote"The string must be
"paynote"— all lowercase, no spaces. Any other value will cause Plaid to return an error and no token will be issued.
Step 4 — Send the processor_token to Paynote (your server)
processor_token to Paynote (your server)POST the processor_token from Step 3 and the customer's Paynote user_id to the Paynote API. Paynote securely retrieves the bank account details from Plaid and creates a Verified funding source — no additional verification steps needed.
Endpoint
POST https://{env}-paynote.seamlesschex.com/v2/funding-source/processor/plaid
Environments
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox-paynote.seamlesschex.com |
| Production | https://paynote.seamlesschex.com |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | string | Required | The Paynote customer ID to attach the bank account to |
processor_token | string | Required | The processor_token received from Plaid in Step 3 |
Example Request
curl -X POST https://sandbox-paynote.seamlesschex.com/v2/funding-source/processor/plaid \
-H "Authorization: Bearer YOUR_PAYNOTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "usr_abc123",
"processor_token": "processor-sandbox-0asd1-a92nc"
}'
const response = await fetch(
'https://sandbox-paynote.seamlesschex.com/v2/funding-source/processor/plaid',
{
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_PAYNOTE_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
user_id: 'usr_abc123',
processor_token: 'processor-sandbox-0asd1-a92nc',
}),
}
);
const data = await response.json();
// data.funding_source_id → use this for future payments
import requests
response = requests.post(
'https://sandbox-paynote.seamlesschex.com/v2/funding-source/processor/plaid',
headers={
'Authorization': 'Bearer YOUR_PAYNOTE_API_KEY',
'Content-Type': 'application/json',
},
json={
'user_id': 'usr_abc123',
'processor_token': 'processor-sandbox-0asd1-a92nc',
}
)
data = response.json()
# data['funding_source_id'] → use this for future payments
Success Response
{
"success": true,
"funding_source_id": "fs_123abc",
"status": "Verified",
"bank_name": "Bank of America",
"last_four": "4321"
}
The funding source is immediately ready
A
status: "Verified"response means the bank account is instantly ready for ACH debits and credits. No micro-deposit confirmation or additional steps required. Save thefunding_source_id— you'll use it on all future payment requests for this customer.
Testing in Sandbox
Use Plaid's Sandbox test credentials when launching Link with a link_token created against PlaidEnvironments.sandbox, and use https://sandbox-paynote.seamlesschex.com as your Paynote base URL.
Shortcut — bypass the Link UI during testing:
Instead of running the full Link flow, call Plaid's /sandbox/public_token/create endpoint directly to get a public_token. Note: when using this shortcut, the accounts array won't be populated. Call /accounts/get to retrieve a checking or savings account ID to use in Step 3.
Log in to the Paynote Sandbox Dashboard to inspect test funding sources and verify results end-to-end.
Going to Production
- Request Production access in your Plaid Dashboard
- Obtain your Paynote Production API key — contact [email protected] if you don't have one
- Switch Plaid's
basePathfromPlaidEnvironments.sandbox→PlaidEnvironments.production - Switch your Paynote base URL from
sandbox-paynote.seamlesschex.com→paynote.seamlesschex.com
Error Reference
| Error | Likely Cause | Resolution |
|---|---|---|
invalid_processor_token | Token already used, expired, or malformed | Re-run the Plaid Link flow to generate a new token |
customer_not_found | user_id does not exist in Paynote | Create the customer first |
unauthorized | Missing or invalid Paynote API key | Check the Authorization: Bearer header |
processor_not_enabled | Paynote not enabled in Plaid Dashboard | Enable Paynote under Plaid Dashboard → Developers → Integrations |
For errors from Plaid during token creation, see the Plaid error codes reference.
Important Notes
Processor tokens are single-use and account-scoped.
Each processor_token is tied to one Plaid item and one bank account. Once submitted to Paynote it cannot be reused. If you need to re-link an account, initiate a new Plaid Link flow.
Tokens can become invalid.
A processor token stops working if the customer revokes their Plaid connection, the Plaid item requires re-authentication, or the financial institution is disabled. If Paynote returns an invalid_processor_token error after a previously working integration, prompt the customer to reconnect via Plaid Link.
Set up webhooks.
Configure a webhook listener to receive real-time funding source status events. See Funding Source Webhooks for the full event reference.