chub-dev 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1163 @@
1
+ ---
2
+ name: banking
3
+ description: "Plaid API Coding Guidelines for JavaScript/TypeScript using the official Plaid libraries and SDKs"
4
+ metadata:
5
+ languages: "javascript"
6
+ versions: "39.1.0"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "plaid,banking,fintech,payments,financial-data"
10
+ ---
11
+
12
+ # Plaid API Coding Guidelines (JavaScript/TypeScript)
13
+
14
+ You are a Plaid API coding expert. Help me with writing code using the Plaid API calling the official libraries and SDKs.
15
+
16
+ ## Golden Rule: Use the Correct and Current SDK
17
+
18
+ Always use the official Plaid Node.js SDK for all Plaid API interactions.
19
+
20
+ - **Library Name:** Plaid Node.js SDK
21
+ - **NPM Package:** `plaid`
22
+ - **Current Version:** 39.1.0
23
+
24
+ **Installation:**
25
+
26
+ ```bash
27
+ npm install plaid
28
+ ```
29
+
30
+ **Import Patterns:**
31
+
32
+ ```typescript
33
+ // Correct - ES6 import
34
+ import { Configuration, PlaidApi, PlaidEnvironments } from 'plaid';
35
+
36
+ // Correct - CommonJS
37
+ const { Configuration, PlaidApi, PlaidEnvironments } = require('plaid');
38
+ ```
39
+
40
+ **Important Notes:**
41
+
42
+ - The plaid-node client library is updated monthly
43
+ - Always use a recent version of the library for new endpoints and fields support
44
+ - This library uses semantic versioning with breaking changes indicated by major version bumps
45
+ - The library is generated from the OpenAPI spec
46
+
47
+ ## Initialization and Authentication
48
+
49
+ The Plaid library requires creating a `Configuration` object and `PlaidApi` client instance for all API calls.
50
+
51
+ ```typescript
52
+ import { Configuration, PlaidApi, PlaidEnvironments } from 'plaid';
53
+
54
+ const configuration = new Configuration({
55
+ basePath: PlaidEnvironments.sandbox,
56
+ baseOptions: {
57
+ headers: {
58
+ 'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
59
+ 'PLAID-SECRET': process.env.PLAID_SECRET,
60
+ 'Plaid-Version': '2020-09-14',
61
+ },
62
+ },
63
+ });
64
+
65
+ const plaidClient = new PlaidApi(configuration);
66
+ ```
67
+
68
+ ### Environment Configuration
69
+
70
+ Plaid has multiple environments for different use cases:
71
+
72
+ ```typescript
73
+ // Sandbox - for testing with stateful test data
74
+ const configuration = new Configuration({
75
+ basePath: PlaidEnvironments.sandbox,
76
+ baseOptions: {
77
+ headers: {
78
+ 'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
79
+ 'PLAID-SECRET': process.env.PLAID_SANDBOX_SECRET,
80
+ 'Plaid-Version': '2020-09-14',
81
+ },
82
+ },
83
+ });
84
+
85
+ // Development - for testing with live credentials
86
+ const configuration = new Configuration({
87
+ basePath: PlaidEnvironments.development,
88
+ baseOptions: {
89
+ headers: {
90
+ 'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
91
+ 'PLAID-SECRET': process.env.PLAID_DEVELOPMENT_SECRET,
92
+ 'Plaid-Version': '2020-09-14',
93
+ },
94
+ },
95
+ });
96
+
97
+ // Production - for live users
98
+ const configuration = new Configuration({
99
+ basePath: PlaidEnvironments.production,
100
+ baseOptions: {
101
+ headers: {
102
+ 'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
103
+ 'PLAID-SECRET': process.env.PLAID_PRODUCTION_SECRET,
104
+ 'Plaid-Version': '2020-09-14',
105
+ },
106
+ },
107
+ });
108
+ ```
109
+
110
+ ### Environment Variables Setup
111
+
112
+ ```bash
113
+ # .env file
114
+ PLAID_CLIENT_ID=your_client_id_here
115
+ PLAID_SANDBOX_SECRET=your_sandbox_secret_here
116
+ PLAID_DEVELOPMENT_SECRET=your_development_secret_here
117
+ PLAID_PRODUCTION_SECRET=your_production_secret_here
118
+ ```
119
+
120
+ ```typescript
121
+ // Load environment variables
122
+ import dotenv from 'dotenv';
123
+ dotenv.config();
124
+
125
+ const configuration = new Configuration({
126
+ basePath: PlaidEnvironments.sandbox,
127
+ baseOptions: {
128
+ headers: {
129
+ 'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
130
+ 'PLAID-SECRET': process.env.PLAID_SANDBOX_SECRET,
131
+ 'Plaid-Version': '2020-09-14',
132
+ },
133
+ },
134
+ });
135
+ ```
136
+
137
+ ## Core Plaid Flow
138
+
139
+ The standard Plaid integration flow follows these steps:
140
+
141
+ 1. Create a link_token
142
+ 2. Initialize Plaid Link on the frontend
143
+ 3. Receive a public_token from Link
144
+ 4. Exchange the public_token for an access_token
145
+ 5. Use the access_token to make API requests
146
+
147
+ ### Step 1: Create Link Token
148
+
149
+ Create a temporary link_token to authenticate your app with Plaid Link:
150
+
151
+ ```typescript
152
+ import {
153
+ Configuration,
154
+ PlaidApi,
155
+ PlaidEnvironments,
156
+ Products,
157
+ CountryCode
158
+ } from 'plaid';
159
+
160
+ const plaidClient = new PlaidApi(configuration);
161
+
162
+ async function createLinkToken(userId: string) {
163
+ const request = {
164
+ user: {
165
+ client_user_id: userId,
166
+ },
167
+ client_name: 'My Application',
168
+ products: [Products.Auth, Products.Transactions],
169
+ country_codes: [CountryCode.Us],
170
+ language: 'en',
171
+ webhook: 'https://your-domain.com/plaid/webhook',
172
+ };
173
+
174
+ try {
175
+ const response = await plaidClient.linkTokenCreate(request);
176
+ const linkToken = response.data.link_token;
177
+ return linkToken;
178
+ } catch (error) {
179
+ console.error('Error creating link token:', error);
180
+ throw error;
181
+ }
182
+ }
183
+ ```
184
+
185
+ ### Step 2: Exchange Public Token for Access Token
186
+
187
+ After the user completes the Link flow, exchange the public_token for a permanent access_token:
188
+
189
+ ```typescript
190
+ async function exchangePublicToken(publicToken: string) {
191
+ try {
192
+ const response = await plaidClient.itemPublicTokenExchange({
193
+ public_token: publicToken,
194
+ });
195
+
196
+ const accessToken = response.data.access_token;
197
+ const itemId = response.data.item_id;
198
+
199
+ // Store these securely in your database
200
+ console.log('Access Token:', accessToken);
201
+ console.log('Item ID:', itemId);
202
+
203
+ return { accessToken, itemId };
204
+ } catch (error) {
205
+ console.error('Error exchanging public token:', error);
206
+ throw error;
207
+ }
208
+ }
209
+ ```
210
+
211
+ ## Product APIs
212
+
213
+ ### Auth - Account and Routing Numbers
214
+
215
+ Retrieve bank account and routing numbers for ACH transfers:
216
+
217
+ ```typescript
218
+ async function getAuthData(accessToken: string) {
219
+ try {
220
+ const request = {
221
+ access_token: accessToken,
222
+ };
223
+
224
+ const response = await plaidClient.authGet(request);
225
+ const accounts = response.data.accounts;
226
+ const numbers = response.data.numbers;
227
+
228
+ console.log('Accounts:', accounts);
229
+ console.log('Account Numbers:', numbers.ach);
230
+ console.log('Routing Numbers:', numbers.ach[0].routing);
231
+
232
+ return { accounts, numbers };
233
+ } catch (error) {
234
+ console.error('Error getting auth data:', error);
235
+ throw error;
236
+ }
237
+ }
238
+ ```
239
+
240
+ **Advanced Auth with Options:**
241
+
242
+ ```typescript
243
+ async function getAuthDataWithOptions(accessToken: string, accountIds?: string[]) {
244
+ try {
245
+ const request = {
246
+ access_token: accessToken,
247
+ options: {
248
+ account_ids: accountIds, // Optional: filter specific accounts
249
+ },
250
+ };
251
+
252
+ const response = await plaidClient.authGet(request);
253
+ return response.data;
254
+ } catch (error) {
255
+ console.error('Error getting auth data:', error);
256
+ throw error;
257
+ }
258
+ }
259
+ ```
260
+
261
+ ### Accounts - Retrieve Account Information
262
+
263
+ Get account details including balances and metadata:
264
+
265
+ ```typescript
266
+ async function getAccounts(accessToken: string) {
267
+ try {
268
+ const request = {
269
+ access_token: accessToken,
270
+ };
271
+
272
+ const response = await plaidClient.accountsGet(request);
273
+ const accounts = response.data.accounts;
274
+
275
+ accounts.forEach(account => {
276
+ console.log('Account ID:', account.account_id);
277
+ console.log('Name:', account.name);
278
+ console.log('Type:', account.type);
279
+ console.log('Subtype:', account.subtype);
280
+ console.log('Current Balance:', account.balances.current);
281
+ console.log('Available Balance:', account.balances.available);
282
+ });
283
+
284
+ return accounts;
285
+ } catch (error) {
286
+ console.error('Error getting accounts:', error);
287
+ throw error;
288
+ }
289
+ }
290
+ ```
291
+
292
+ ### Balance - Real-time Balance Information
293
+
294
+ Get up-to-date balance information:
295
+
296
+ ```typescript
297
+ async function getBalance(accessToken: string) {
298
+ try {
299
+ const request = {
300
+ access_token: accessToken,
301
+ };
302
+
303
+ const response = await plaidClient.accountsBalanceGet(request);
304
+ const accounts = response.data.accounts;
305
+
306
+ accounts.forEach(account => {
307
+ console.log(`${account.name}: $${account.balances.current}`);
308
+ console.log(`Available: $${account.balances.available}`);
309
+ console.log(`Currency: ${account.balances.iso_currency_code}`);
310
+ });
311
+
312
+ return accounts;
313
+ } catch (error) {
314
+ console.error('Error getting balance:', error);
315
+ throw error;
316
+ }
317
+ }
318
+ ```
319
+
320
+ **Advanced Balance with Account Filtering:**
321
+
322
+ ```typescript
323
+ async function getBalanceForSpecificAccounts(
324
+ accessToken: string,
325
+ accountIds: string[]
326
+ ) {
327
+ try {
328
+ const request = {
329
+ access_token: accessToken,
330
+ options: {
331
+ account_ids: accountIds,
332
+ },
333
+ };
334
+
335
+ const response = await plaidClient.accountsBalanceGet(request);
336
+ return response.data.accounts;
337
+ } catch (error) {
338
+ console.error('Error getting balance:', error);
339
+ throw error;
340
+ }
341
+ }
342
+ ```
343
+
344
+ ### Identity - Account Holder Information
345
+
346
+ Retrieve identity information for account holders:
347
+
348
+ ```typescript
349
+ async function getIdentity(accessToken: string) {
350
+ try {
351
+ const request = {
352
+ access_token: accessToken,
353
+ };
354
+
355
+ const response = await plaidClient.identityGet(request);
356
+ const accounts = response.data.accounts;
357
+
358
+ accounts.forEach(account => {
359
+ console.log('Account:', account.name);
360
+ account.owners.forEach(owner => {
361
+ console.log('Name:', owner.names);
362
+ console.log('Email:', owner.emails);
363
+ console.log('Phone:', owner.phone_numbers);
364
+ console.log('Address:', owner.addresses);
365
+ });
366
+ });
367
+
368
+ return response.data;
369
+ } catch (error) {
370
+ console.error('Error getting identity:', error);
371
+ throw error;
372
+ }
373
+ }
374
+ ```
375
+
376
+ ### Transactions - Transaction History
377
+
378
+ Plaid provides two methods for retrieving transactions: `/transactions/get` (legacy) and `/transactions/sync` (recommended).
379
+
380
+ #### Transactions Sync (Recommended)
381
+
382
+ The `/transactions/sync` endpoint provides incremental updates and is the recommended approach:
383
+
384
+ ```typescript
385
+ async function syncTransactions(accessToken: string, cursor?: string) {
386
+ try {
387
+ const request = {
388
+ access_token: accessToken,
389
+ cursor: cursor || undefined,
390
+ count: 100, // Number of transactions to fetch (max 500)
391
+ };
392
+
393
+ const response = await plaidClient.transactionsSync(request);
394
+
395
+ console.log('Added transactions:', response.data.added.length);
396
+ console.log('Modified transactions:', response.data.modified.length);
397
+ console.log('Removed transactions:', response.data.removed.length);
398
+ console.log('Next cursor:', response.data.next_cursor);
399
+ console.log('Has more:', response.data.has_more);
400
+
401
+ // Process transactions
402
+ response.data.added.forEach(transaction => {
403
+ console.log('Date:', transaction.date);
404
+ console.log('Name:', transaction.name);
405
+ console.log('Amount:', transaction.amount);
406
+ console.log('Category:', transaction.category);
407
+ });
408
+
409
+ return response.data;
410
+ } catch (error) {
411
+ console.error('Error syncing transactions:', error);
412
+ throw error;
413
+ }
414
+ }
415
+ ```
416
+
417
+ **Paginated Transaction Sync:**
418
+
419
+ ```typescript
420
+ async function getAllTransactions(accessToken: string) {
421
+ let cursor: string | undefined = undefined;
422
+ let allTransactions: any[] = [];
423
+ let hasMore = true;
424
+
425
+ try {
426
+ while (hasMore) {
427
+ const request = {
428
+ access_token: accessToken,
429
+ cursor: cursor,
430
+ count: 500, // Use maximum for efficiency
431
+ };
432
+
433
+ const response = await plaidClient.transactionsSync(request);
434
+
435
+ allTransactions = allTransactions.concat(response.data.added);
436
+ cursor = response.data.next_cursor;
437
+ hasMore = response.data.has_more;
438
+
439
+ console.log(`Fetched ${response.data.added.length} transactions`);
440
+ }
441
+
442
+ console.log(`Total transactions: ${allTransactions.length}`);
443
+ return allTransactions;
444
+ } catch (error) {
445
+ console.error('Error getting all transactions:', error);
446
+ throw error;
447
+ }
448
+ }
449
+ ```
450
+
451
+ #### Transactions Get (Legacy)
452
+
453
+ For retrieving transactions within a specific date range:
454
+
455
+ ```typescript
456
+ async function getTransactions(
457
+ accessToken: string,
458
+ startDate: string,
459
+ endDate: string
460
+ ) {
461
+ try {
462
+ const request = {
463
+ access_token: accessToken,
464
+ start_date: startDate, // Format: 'YYYY-MM-DD'
465
+ end_date: endDate, // Format: 'YYYY-MM-DD'
466
+ options: {
467
+ count: 250,
468
+ offset: 0,
469
+ },
470
+ };
471
+
472
+ const response = await plaidClient.transactionsGet(request);
473
+ const transactions = response.data.transactions;
474
+ const totalTransactions = response.data.total_transactions;
475
+
476
+ console.log(`Retrieved ${transactions.length} of ${totalTransactions}`);
477
+
478
+ return transactions;
479
+ } catch (error) {
480
+ console.error('Error getting transactions:', error);
481
+ throw error;
482
+ }
483
+ }
484
+ ```
485
+
486
+ **Paginated Transactions Get:**
487
+
488
+ ```typescript
489
+ async function getAllTransactionsInRange(
490
+ accessToken: string,
491
+ startDate: string,
492
+ endDate: string
493
+ ) {
494
+ let offset = 0;
495
+ const batchSize = 500;
496
+ let allTransactions: any[] = [];
497
+ let totalTransactions = 0;
498
+
499
+ try {
500
+ do {
501
+ const request = {
502
+ access_token: accessToken,
503
+ start_date: startDate,
504
+ end_date: endDate,
505
+ options: {
506
+ count: batchSize,
507
+ offset: offset,
508
+ },
509
+ };
510
+
511
+ const response = await plaidClient.transactionsGet(request);
512
+ const transactions = response.data.transactions;
513
+ totalTransactions = response.data.total_transactions;
514
+
515
+ allTransactions = allTransactions.concat(transactions);
516
+ offset += transactions.length;
517
+
518
+ console.log(`Fetched ${allTransactions.length} of ${totalTransactions}`);
519
+ } while (allTransactions.length < totalTransactions);
520
+
521
+ return allTransactions;
522
+ } catch (error) {
523
+ console.error('Error getting all transactions:', error);
524
+ throw error;
525
+ }
526
+ }
527
+ ```
528
+
529
+ ### Investments - Holdings and Transactions
530
+
531
+ Retrieve investment account holdings and transactions:
532
+
533
+ ```typescript
534
+ async function getInvestmentHoldings(accessToken: string) {
535
+ try {
536
+ const request = {
537
+ access_token: accessToken,
538
+ };
539
+
540
+ const response = await plaidClient.investmentsHoldingsGet(request);
541
+ const holdings = response.data.holdings;
542
+ const securities = response.data.securities;
543
+
544
+ holdings.forEach(holding => {
545
+ const security = securities.find(s => s.security_id === holding.security_id);
546
+ console.log('Security:', security?.name);
547
+ console.log('Ticker:', security?.ticker_symbol);
548
+ console.log('Quantity:', holding.quantity);
549
+ console.log('Institution Price:', holding.institution_price);
550
+ console.log('Value:', holding.institution_value);
551
+ });
552
+
553
+ return { holdings, securities };
554
+ } catch (error) {
555
+ console.error('Error getting investment holdings:', error);
556
+ throw error;
557
+ }
558
+ }
559
+ ```
560
+
561
+ **Investment Transactions:**
562
+
563
+ ```typescript
564
+ async function getInvestmentTransactions(
565
+ accessToken: string,
566
+ startDate: string,
567
+ endDate: string
568
+ ) {
569
+ try {
570
+ const request = {
571
+ access_token: accessToken,
572
+ start_date: startDate,
573
+ end_date: endDate,
574
+ };
575
+
576
+ const response = await plaidClient.investmentsTransactionsGet(request);
577
+ const transactions = response.data.investment_transactions;
578
+
579
+ transactions.forEach(transaction => {
580
+ console.log('Date:', transaction.date);
581
+ console.log('Name:', transaction.name);
582
+ console.log('Type:', transaction.type);
583
+ console.log('Amount:', transaction.amount);
584
+ console.log('Quantity:', transaction.quantity);
585
+ console.log('Price:', transaction.price);
586
+ });
587
+
588
+ return transactions;
589
+ } catch (error) {
590
+ console.error('Error getting investment transactions:', error);
591
+ throw error;
592
+ }
593
+ }
594
+ ```
595
+
596
+ ### Liabilities - Loan and Credit Card Data
597
+
598
+ Access loan balances, interest rates, and credit card information:
599
+
600
+ ```typescript
601
+ async function getLiabilities(accessToken: string) {
602
+ try {
603
+ const request = {
604
+ access_token: accessToken,
605
+ };
606
+
607
+ const response = await plaidClient.liabilitiesGet(request);
608
+ const liabilities = response.data.liabilities;
609
+
610
+ // Credit cards
611
+ liabilities.credit?.forEach(card => {
612
+ console.log('Credit Card:', card.name);
613
+ console.log('APRs:', card.aprs);
614
+ console.log('Last Payment:', card.last_payment_amount);
615
+ console.log('Minimum Payment:', card.minimum_payment_amount);
616
+ });
617
+
618
+ // Student loans
619
+ liabilities.student?.forEach(loan => {
620
+ console.log('Student Loan:', loan.account_id);
621
+ console.log('Interest Rate:', loan.interest_rate_percentage);
622
+ console.log('Origination Date:', loan.origination_date);
623
+ console.log('Outstanding Balance:', loan.outstanding_interest_amount);
624
+ });
625
+
626
+ // Mortgages
627
+ liabilities.mortgage?.forEach(mortgage => {
628
+ console.log('Mortgage:', mortgage.account_id);
629
+ console.log('Interest Rate:', mortgage.interest_rate.percentage);
630
+ console.log('Origination Date:', mortgage.origination_date);
631
+ console.log('Maturity Date:', mortgage.maturity_date);
632
+ });
633
+
634
+ return liabilities;
635
+ } catch (error) {
636
+ console.error('Error getting liabilities:', error);
637
+ throw error;
638
+ }
639
+ }
640
+ ```
641
+
642
+ ### Payment Initiation (UK and Europe)
643
+
644
+ Create and manage payments:
645
+
646
+ ```typescript
647
+ import { PaymentInitiationPaymentCreateRequest } from 'plaid';
648
+
649
+ async function createPayment(accessToken: string) {
650
+ try {
651
+ const request: PaymentInitiationPaymentCreateRequest = {
652
+ recipient_id: 'recipient_id_from_previous_call',
653
+ reference: 'Invoice #12345',
654
+ amount: {
655
+ currency: 'GBP',
656
+ value: 100.00,
657
+ },
658
+ };
659
+
660
+ const response = await plaidClient.paymentInitiationPaymentCreate(request);
661
+ const paymentId = response.data.payment_id;
662
+
663
+ console.log('Payment ID:', paymentId);
664
+ return paymentId;
665
+ } catch (error) {
666
+ console.error('Error creating payment:', error);
667
+ throw error;
668
+ }
669
+ }
670
+ ```
671
+
672
+ **Get Payment Status:**
673
+
674
+ ```typescript
675
+ async function getPaymentStatus(paymentId: string) {
676
+ try {
677
+ const request = {
678
+ payment_id: paymentId,
679
+ };
680
+
681
+ const response = await plaidClient.paymentInitiationPaymentGet(request);
682
+ const payment = response.data;
683
+
684
+ console.log('Status:', payment.status);
685
+ console.log('Amount:', payment.amount);
686
+ console.log('Last Updated:', payment.last_status_update);
687
+
688
+ return payment;
689
+ } catch (error) {
690
+ console.error('Error getting payment status:', error);
691
+ throw error;
692
+ }
693
+ }
694
+ ```
695
+
696
+ ## Items Management
697
+
698
+ An Item represents a user's connection to a financial institution.
699
+
700
+ ### Get Item Information
701
+
702
+ ```typescript
703
+ async function getItem(accessToken: string) {
704
+ try {
705
+ const request = {
706
+ access_token: accessToken,
707
+ };
708
+
709
+ const response = await plaidClient.itemGet(request);
710
+ const item = response.data.item;
711
+
712
+ console.log('Item ID:', item.item_id);
713
+ console.log('Institution ID:', item.institution_id);
714
+ console.log('Available Products:', item.available_products);
715
+ console.log('Billed Products:', item.billed_products);
716
+ console.log('Error:', item.error);
717
+
718
+ return item;
719
+ } catch (error) {
720
+ console.error('Error getting item:', error);
721
+ throw error;
722
+ }
723
+ }
724
+ ```
725
+
726
+ ### Remove Item
727
+
728
+ ```typescript
729
+ async function removeItem(accessToken: string) {
730
+ try {
731
+ const request = {
732
+ access_token: accessToken,
733
+ };
734
+
735
+ const response = await plaidClient.itemRemove(request);
736
+ console.log('Item removed successfully');
737
+ return response.data;
738
+ } catch (error) {
739
+ console.error('Error removing item:', error);
740
+ throw error;
741
+ }
742
+ }
743
+ ```
744
+
745
+ ### Update Item Webhook
746
+
747
+ ```typescript
748
+ async function updateWebhook(accessToken: string, newWebhook: string) {
749
+ try {
750
+ const request = {
751
+ access_token: accessToken,
752
+ webhook: newWebhook,
753
+ };
754
+
755
+ const response = await plaidClient.itemWebhookUpdate(request);
756
+ const item = response.data.item;
757
+
758
+ console.log('Webhook updated to:', newWebhook);
759
+ return item;
760
+ } catch (error) {
761
+ console.error('Error updating webhook:', error);
762
+ throw error;
763
+ }
764
+ }
765
+ ```
766
+
767
+ ## Webhooks
768
+
769
+ Plaid sends webhook notifications for various events. Configure webhooks via `/link/token/create` or the Plaid Dashboard.
770
+
771
+ ### Webhook Handler Example
772
+
773
+ ```typescript
774
+ import express from 'express';
775
+
776
+ const app = express();
777
+ app.use(express.json());
778
+
779
+ app.post('/plaid/webhook', async (req, res) => {
780
+ const webhook = req.body;
781
+
782
+ console.log('Webhook Type:', webhook.webhook_type);
783
+ console.log('Webhook Code:', webhook.webhook_code);
784
+
785
+ switch (webhook.webhook_type) {
786
+ case 'TRANSACTIONS':
787
+ await handleTransactionsWebhook(webhook);
788
+ break;
789
+ case 'ITEM':
790
+ await handleItemWebhook(webhook);
791
+ break;
792
+ case 'AUTH':
793
+ await handleAuthWebhook(webhook);
794
+ break;
795
+ default:
796
+ console.log('Unknown webhook type:', webhook.webhook_type);
797
+ }
798
+
799
+ res.json({ status: 'received' });
800
+ });
801
+
802
+ async function handleTransactionsWebhook(webhook: any) {
803
+ if (webhook.webhook_code === 'SYNC_UPDATES_AVAILABLE') {
804
+ const itemId = webhook.item_id;
805
+ console.log('New transactions available for item:', itemId);
806
+ // Fetch new transactions using transactionsSync
807
+ }
808
+ }
809
+
810
+ async function handleItemWebhook(webhook: any) {
811
+ if (webhook.webhook_code === 'ERROR') {
812
+ console.log('Item error:', webhook.error);
813
+ // Handle item error
814
+ }
815
+ }
816
+
817
+ async function handleAuthWebhook(webhook: any) {
818
+ if (webhook.webhook_code === 'AUTOMATICALLY_VERIFIED') {
819
+ console.log('Account automatically verified:', webhook.account_id);
820
+ }
821
+ }
822
+ ```
823
+
824
+ ## Sandbox Testing
825
+
826
+ The Sandbox environment provides test data and utilities for development.
827
+
828
+ ### Fire a Test Webhook
829
+
830
+ ```typescript
831
+ async function fireSandboxWebhook(accessToken: string) {
832
+ try {
833
+ const request = {
834
+ access_token: accessToken,
835
+ webhook_code: 'SYNC_UPDATES_AVAILABLE',
836
+ };
837
+
838
+ const response = await plaidClient.sandboxItemFireWebhook(request);
839
+ console.log('Webhook fired:', response.data);
840
+ return response.data;
841
+ } catch (error) {
842
+ console.error('Error firing webhook:', error);
843
+ throw error;
844
+ }
845
+ }
846
+ ```
847
+
848
+ ### Reset Sandbox Item Login
849
+
850
+ ```typescript
851
+ async function resetSandboxItem(accessToken: string) {
852
+ try {
853
+ const request = {
854
+ access_token: accessToken,
855
+ };
856
+
857
+ const response = await plaidClient.sandboxItemResetLogin(request);
858
+ console.log('Item login reset');
859
+ return response.data;
860
+ } catch (error) {
861
+ console.error('Error resetting item:', error);
862
+ throw error;
863
+ }
864
+ }
865
+ ```
866
+
867
+ ### Set Verification Status (Sandbox)
868
+
869
+ ```typescript
870
+ async function setVerificationStatus(
871
+ accessToken: string,
872
+ accountId: string,
873
+ verificationStatus: string
874
+ ) {
875
+ try {
876
+ const request = {
877
+ access_token: accessToken,
878
+ account_id: accountId,
879
+ verification_status: verificationStatus,
880
+ };
881
+
882
+ const response = await plaidClient.sandboxItemSetVerificationStatus(request);
883
+ console.log('Verification status set');
884
+ return response.data;
885
+ } catch (error) {
886
+ console.error('Error setting verification status:', error);
887
+ throw error;
888
+ }
889
+ }
890
+ ```
891
+
892
+ ## Error Handling
893
+
894
+ Plaid errors include an `error_type`, `error_code`, and HTTP status code.
895
+
896
+ ```typescript
897
+ import { PlaidError } from 'plaid';
898
+
899
+ async function makeApiCall(accessToken: string) {
900
+ try {
901
+ const response = await plaidClient.accountsGet({
902
+ access_token: accessToken,
903
+ });
904
+ return response.data;
905
+ } catch (error: any) {
906
+ if (error.response) {
907
+ const plaidError = error.response.data;
908
+
909
+ console.log('Error Type:', plaidError.error_type);
910
+ console.log('Error Code:', plaidError.error_code);
911
+ console.log('Error Message:', plaidError.error_message);
912
+ console.log('Display Message:', plaidError.display_message);
913
+ console.log('HTTP Status:', error.response.status);
914
+
915
+ switch (plaidError.error_type) {
916
+ case 'ITEM_ERROR':
917
+ if (plaidError.error_code === 'ITEM_LOGIN_REQUIRED') {
918
+ console.log('User needs to re-authenticate');
919
+ // Trigger Link update mode
920
+ }
921
+ break;
922
+ case 'RATE_LIMIT_EXCEEDED':
923
+ console.log('Rate limit exceeded, retry after delay');
924
+ // Implement exponential backoff
925
+ break;
926
+ case 'API_ERROR':
927
+ console.log('Plaid API error, retry request');
928
+ // Retry with idempotency key if available
929
+ break;
930
+ case 'INVALID_REQUEST':
931
+ console.log('Invalid request parameters');
932
+ break;
933
+ case 'INVALID_INPUT':
934
+ console.log('Invalid input data');
935
+ break;
936
+ case 'INSTITUTION_ERROR':
937
+ console.log('Institution is down or experiencing issues');
938
+ break;
939
+ default:
940
+ console.log('Unexpected error type');
941
+ }
942
+ } else {
943
+ console.error('Network or unexpected error:', error);
944
+ }
945
+ throw error;
946
+ }
947
+ }
948
+ ```
949
+
950
+ ### Retry Logic with Exponential Backoff
951
+
952
+ ```typescript
953
+ async function makeApiCallWithRetry<T>(
954
+ apiCall: () => Promise<T>,
955
+ maxRetries = 3
956
+ ): Promise<T> {
957
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
958
+ try {
959
+ return await apiCall();
960
+ } catch (error: any) {
961
+ const isLastAttempt = attempt === maxRetries;
962
+ const shouldRetry = error.response?.data?.error_type === 'RATE_LIMIT_EXCEEDED' ||
963
+ error.response?.data?.error_type === 'API_ERROR';
964
+
965
+ if (!shouldRetry || isLastAttempt) {
966
+ throw error;
967
+ }
968
+
969
+ const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
970
+ console.log(`Retrying after ${delay}ms (attempt ${attempt}/${maxRetries})`);
971
+ await new Promise(resolve => setTimeout(resolve, delay));
972
+ }
973
+ }
974
+ throw new Error('Max retries exceeded');
975
+ }
976
+
977
+ // Usage
978
+ const accounts = await makeApiCallWithRetry(() =>
979
+ plaidClient.accountsGet({ access_token: accessToken })
980
+ );
981
+ ```
982
+
983
+ ## Rate Limits
984
+
985
+ Plaid enforces rate limits to ensure API stability:
986
+
987
+ - `/auth/get`: 15 requests per Item per minute (Production)
988
+ - `/institutions/get`: 25 requests per client per minute (Production), 10 requests per client per minute (Sandbox)
989
+ - Most other endpoints: Custom limits based on your account
990
+
991
+ To reduce rate limit errors:
992
+
993
+ - Increase the `count` parameter in `/transactions/sync` to the maximum of 500
994
+ - Cache responses when appropriate
995
+ - Implement exponential backoff retry logic
996
+ - Use webhooks instead of polling for updates
997
+
998
+ ```typescript
999
+ // Efficient transactions sync with maximum count
1000
+ async function efficientTransactionSync(accessToken: string, cursor?: string) {
1001
+ const request = {
1002
+ access_token: accessToken,
1003
+ cursor: cursor,
1004
+ count: 500, // Use maximum to reduce number of requests
1005
+ };
1006
+
1007
+ const response = await plaidClient.transactionsSync(request);
1008
+ return response.data;
1009
+ }
1010
+ ```
1011
+
1012
+ ## Complete Integration Example
1013
+
1014
+ ```typescript
1015
+ import express from 'express';
1016
+ import { Configuration, PlaidApi, PlaidEnvironments, Products, CountryCode } from 'plaid';
1017
+ import dotenv from 'dotenv';
1018
+
1019
+ dotenv.config();
1020
+
1021
+ const app = express();
1022
+ app.use(express.json());
1023
+
1024
+ // Initialize Plaid client
1025
+ const configuration = new Configuration({
1026
+ basePath: PlaidEnvironments.sandbox,
1027
+ baseOptions: {
1028
+ headers: {
1029
+ 'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
1030
+ 'PLAID-SECRET': process.env.PLAID_SANDBOX_SECRET,
1031
+ 'Plaid-Version': '2020-09-14',
1032
+ },
1033
+ },
1034
+ });
1035
+
1036
+ const plaidClient = new PlaidApi(configuration);
1037
+
1038
+ // Store access tokens (use a database in production)
1039
+ const accessTokenStore = new Map<string, string>();
1040
+
1041
+ // Create link token
1042
+ app.post('/api/create_link_token', async (req, res) => {
1043
+ try {
1044
+ const { userId } = req.body;
1045
+
1046
+ const response = await plaidClient.linkTokenCreate({
1047
+ user: {
1048
+ client_user_id: userId,
1049
+ },
1050
+ client_name: 'My Financial App',
1051
+ products: [Products.Auth, Products.Transactions],
1052
+ country_codes: [CountryCode.Us],
1053
+ language: 'en',
1054
+ webhook: 'https://your-domain.com/plaid/webhook',
1055
+ });
1056
+
1057
+ res.json({ link_token: response.data.link_token });
1058
+ } catch (error) {
1059
+ console.error('Error creating link token:', error);
1060
+ res.status(500).json({ error: 'Failed to create link token' });
1061
+ }
1062
+ });
1063
+
1064
+ // Exchange public token
1065
+ app.post('/api/exchange_public_token', async (req, res) => {
1066
+ try {
1067
+ const { public_token, userId } = req.body;
1068
+
1069
+ const response = await plaidClient.itemPublicTokenExchange({
1070
+ public_token: public_token,
1071
+ });
1072
+
1073
+ const accessToken = response.data.access_token;
1074
+ const itemId = response.data.item_id;
1075
+
1076
+ // Store access token securely (use database in production)
1077
+ accessTokenStore.set(userId, accessToken);
1078
+
1079
+ res.json({ success: true, item_id: itemId });
1080
+ } catch (error) {
1081
+ console.error('Error exchanging public token:', error);
1082
+ res.status(500).json({ error: 'Failed to exchange public token' });
1083
+ }
1084
+ });
1085
+
1086
+ // Get accounts
1087
+ app.get('/api/accounts/:userId', async (req, res) => {
1088
+ try {
1089
+ const { userId } = req.params;
1090
+ const accessToken = accessTokenStore.get(userId);
1091
+
1092
+ if (!accessToken) {
1093
+ return res.status(404).json({ error: 'No access token found' });
1094
+ }
1095
+
1096
+ const response = await plaidClient.accountsGet({
1097
+ access_token: accessToken,
1098
+ });
1099
+
1100
+ res.json({ accounts: response.data.accounts });
1101
+ } catch (error) {
1102
+ console.error('Error getting accounts:', error);
1103
+ res.status(500).json({ error: 'Failed to get accounts' });
1104
+ }
1105
+ });
1106
+
1107
+ // Get transactions
1108
+ app.get('/api/transactions/:userId', async (req, res) => {
1109
+ try {
1110
+ const { userId } = req.params;
1111
+ const accessToken = accessTokenStore.get(userId);
1112
+
1113
+ if (!accessToken) {
1114
+ return res.status(404).json({ error: 'No access token found' });
1115
+ }
1116
+
1117
+ const response = await plaidClient.transactionsSync({
1118
+ access_token: accessToken,
1119
+ count: 100,
1120
+ });
1121
+
1122
+ res.json({
1123
+ added: response.data.added,
1124
+ modified: response.data.modified,
1125
+ removed: response.data.removed,
1126
+ next_cursor: response.data.next_cursor,
1127
+ has_more: response.data.has_more,
1128
+ });
1129
+ } catch (error) {
1130
+ console.error('Error getting transactions:', error);
1131
+ res.status(500).json({ error: 'Failed to get transactions' });
1132
+ }
1133
+ });
1134
+
1135
+ // Webhook handler
1136
+ app.post('/plaid/webhook', async (req, res) => {
1137
+ const webhook = req.body;
1138
+ console.log('Received webhook:', webhook);
1139
+
1140
+ if (webhook.webhook_type === 'TRANSACTIONS') {
1141
+ if (webhook.webhook_code === 'SYNC_UPDATES_AVAILABLE') {
1142
+ // Fetch new transactions
1143
+ console.log('New transactions available for item:', webhook.item_id);
1144
+ }
1145
+ }
1146
+
1147
+ res.json({ status: 'received' });
1148
+ });
1149
+
1150
+ const PORT = process.env.PORT || 3000;
1151
+ app.listen(PORT, () => {
1152
+ console.log(`Server running on port ${PORT}`);
1153
+ });
1154
+ ```
1155
+
1156
+ ## Useful Links
1157
+
1158
+ - **Documentation:** https://plaid.com/docs/
1159
+ - **API Reference:** https://plaid.com/docs/api/
1160
+ - **Dashboard:** https://dashboard.plaid.com/
1161
+ - **GitHub Repository:** https://github.com/plaid/plaid-node
1162
+ - **Quickstart Guide:** https://plaid.com/docs/quickstart/
1163
+ - **Changelog:** https://plaid.com/docs/changelog/