Already using Plaid? Generate a processor token with your existing integration and hand it to Seamless ACH to instantly create a verified bank funding source — no micro-deposits required.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
Breaking Changes
Processor Name Update: The Plaid
processorvalue has changed.
- Old:
paynote- New:
seamlessachPlease update your integration to use the new processor name.
Add a Funding Source via Plaid Processor Token
Already using Plaid? Generate a processor token with your existing integration and pass it to Seamless ACH 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 Seamless ACH without starting a separate bank verification flow.
How It Works
The flow spans three parties: your server, Plaid, and the Seamless ACH API. Your customer authenticates their bank through Plaid Link, you exchange the resulting tokens on your server, then pass a single processor_token to Seamless ACH.
| 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: "seamlessach" → get processor_token |
| 5 | Your server | POST processor_token + user_id to Seamless ACH → 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 Seamless ACH account with a valid API key
- ✅ You have an active Plaid account with your
client_idandsecret - ✅ Seamless ACH is enabled in your Plaid Dashboard — go to Developers → Integrations and click Enable next to Seamless ACH
- ✅ 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 Seamless ACH — if not, create them first and save the
user_id
Seamless ACH must be enabled in your Plaid Dashboard
If Seamless ACH 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>
Only
checkingandsavingsaccounts are supportedSeamless ACH accepts accounts whose Plaid
subtypeischeckingorsavings. Other subtypes are rejected — filter the account list before letting the customer choose.
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 Seamless ACH processor_token. Both API calls happen on your server — never expose your Plaid secret on the client.
async function createSeamlessAchProcessorToken(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 Seamless ACH
const processorRes = await plaidClient.processorTokenCreate({
access_token: accessToken,
account_id: accountId,
processor: 'seamlessach',
});
return processorRes.data.processor_token;
// → "processor-sandbox-0asd1-a92nc"
}
The
processorvalue must be exactly"seamlessach"The string must be
"seamlessach"— all lowercase, one word, no hyphen and no underscore. Note thatseamless-achis only the URL slug of Plaid's documentation page and is not a valid value.Migrating from Paynote: if your integration still sends
processor: "paynote", update it. Plaid currently still accepts the legacy name, but it has already been removed from Plaid's public API specification.
Step 4 — Send the processor_token to Seamless ACH (your server)
processor_token to Seamless ACH (your server)POST the processor_token from Step 3 and the customer's Seamless ACH user_id to the Seamless ACH API. Seamless ACH securely retrieves the bank account details from Plaid and creates a Verified funding source — no additional verification steps needed.
Endpoint
Use the full URL for your environment — there is nothing to concatenate.
| Environment | Full URL |
|---|---|
| Sandbox | POST https://sandbox.seamlesschex.com/ach/v2/funding-source/processor/plaid |
| Production | POST https://api.seamlesschex.com/ach/v2/funding-source/processor/plaid |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | string | Required | The Seamless ACH 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.seamlesschex.com/ach/v2/funding-source/processor/plaid \
-H "Authorization: Bearer YOUR_SEAMLESS_ACH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "usr_abc123",
"processor_token": "processor-sandbox-0asd1-a92nc"
}'
const response = await fetch(
'https://sandbox.seamlesschex.com/ach/v2/funding-source/processor/plaid',
{
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_SEAMLESS_ACH_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.source_id → use this for future payments
import requests
response = requests.post(
'https://sandbox.seamlesschex.com/ach/v2/funding-source/processor/plaid',
headers={
'Authorization': 'Bearer YOUR_SEAMLESS_ACH_API_KEY',
'Content-Type': 'application/json',
},
json={
'user_id': 'usr_abc123',
'processor_token': 'processor-sandbox-0asd1-a92nc',
}
)
data = response.json()
# data['funding_source']['source_id'] → use this for future payments
Success Response
{
"success": true,
"funding_source": {
"source_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"user_id": "usr_abc123",
"is_primary": true,
"status": "verified",
"routing": "011401533",
"bank": "Digital Federal Credit Union",
"lbacc": "0000",
"type": "checking",
"created_at": "09/07/2026"
}
}
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.source_id— you'll use it on all future payment requests for this customer. The new funding source automatically becomes the customer's primary source, replacing any previous primary.
Testing in Sandbox
Use Plaid's Sandbox test credentials when launching Link with a link_token created against PlaidEnvironments.sandbox, and use https://sandbox.seamlesschex.com/ach as your Seamless ACH 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 Seamless ACH 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 Seamless ACH Production API key — contact [email protected] if you don't have one
- Switch Plaid's
basePathfromPlaidEnvironments.sandbox→PlaidEnvironments.production - Switch the endpoint host from
sandbox.seamlesschex.com→api.seamlesschex.com— the path stays the same
Error Reference
| Error | Likely Cause | Resolution |
|---|---|---|
403 API key is missing in Authorization header | The Authorization header was not sent | Send Authorization: Bearer YOUR_SEAMLESS_ACH_API_KEY |
403 Access key invalid | Wrong or revoked Seamless ACH API key | Check the key and the environment it belongs to |
403 App inactive | Your API application is disabled | Contact [email protected] |
404 Customer not found | user_id does not exist in Seamless ACH | Create the customer first |
404 You are not the owner of the customer | The customer belongs to another merchant | Use a user_id created under your own account |
400 INVALID_PROCESSOR_TOKEN | Token created for a different processor, or Plaid and Seamless ACH environments do not match | Check processor: "seamlessach"; make sure both sides are Sandbox or both Production |
400 You exceeded the maximum number of bank accounts | The customer has reached the funding source limit | Remove an unused funding source, or use a different customer |
Errors coming from Plaid are returned with Plaid's original error body. For the full list, see the Plaid error codes reference.
Important Notes
Processor tokens are account-scoped.
Each processor_token is tied to one Plaid item and one bank account. Create a separate token for each account you want to connect, and store the returned funding_source.source_id — that is the identifier used by all later payment requests.
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 Seamless ACH 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.