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,1813 @@
1
+ ---
2
+ name: auth
3
+ description: "Stytch Node.js SDK authentication guide for passwordless and OTP-based authentication"
4
+ metadata:
5
+ languages: "javascript"
6
+ versions: "12.43.0"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "stytch,auth,authentication,passwordless,otp"
10
+ ---
11
+
12
+ # Stytch Node.js SDK - Authentication Guide
13
+
14
+ ## Golden Rule
15
+
16
+ **ALWAYS use the official `stytch` package from npm.**
17
+
18
+ ```bash
19
+ npm install stytch
20
+ ```
21
+
22
+ **Current version: 12.43.0**
23
+
24
+ **DO NOT use:**
25
+ - Deprecated `@stytch/stytch-js` (deprecated in favor of `@stytch/vanilla-js` for frontend)
26
+ - Any unofficial or outdated Stytch packages
27
+ - Frontend packages (`@stytch/vanilla-js`, `@stytch/react`, `@stytch/nextjs`) when building backend services
28
+
29
+ The `stytch` package is the official backend SDK for Node.js applications. It supports TypeScript and all current LTS versions of Node.js (18+).
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ### Install the SDK
36
+
37
+ ```bash
38
+ npm install stytch
39
+ ```
40
+
41
+ or
42
+
43
+ ```bash
44
+ yarn add stytch
45
+ ```
46
+
47
+ ### Environment Variables
48
+
49
+ Set up your Stytch credentials in your environment:
50
+
51
+ ```bash
52
+ STYTCH_PROJECT_ID=project-live-your-project-id
53
+ STYTCH_SECRET=secret-live-your-secret-key
54
+ ```
55
+
56
+ **Get credentials from:** [Stytch Dashboard](https://stytch.com/dashboard)
57
+
58
+ For testing, use test environment credentials:
59
+
60
+ ```bash
61
+ STYTCH_PROJECT_ID=project-test-your-project-id
62
+ STYTCH_SECRET=secret-test-your-secret-key
63
+ ```
64
+
65
+ ### .env File Example
66
+
67
+ ```env
68
+ STYTCH_PROJECT_ID=project-live-c60c0abe-c25a-4472-a9ed-320c6667d317
69
+ STYTCH_SECRET=secret-live-80JASucyk7z_G8Z-7dVwZVGXL5NT_qGAQ2I=
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Initialization
75
+
76
+ ### Basic B2C Client
77
+
78
+ ```javascript
79
+ const stytch = require('stytch');
80
+
81
+ const client = new stytch.Client({
82
+ project_id: process.env.STYTCH_PROJECT_ID,
83
+ secret: process.env.STYTCH_SECRET,
84
+ });
85
+ ```
86
+
87
+ ### ES6 Module Import
88
+
89
+ ```javascript
90
+ import * as stytch from 'stytch';
91
+
92
+ const client = new stytch.Client({
93
+ project_id: process.env.STYTCH_PROJECT_ID,
94
+ secret: process.env.STYTCH_SECRET,
95
+ });
96
+ ```
97
+
98
+ ### B2B Client (for organizations)
99
+
100
+ ```javascript
101
+ const stytch = require('stytch');
102
+
103
+ const b2bClient = new stytch.B2BClient({
104
+ project_id: process.env.STYTCH_PROJECT_ID,
105
+ secret: process.env.STYTCH_SECRET,
106
+ });
107
+ ```
108
+
109
+ ### Client with Environment Override
110
+
111
+ ```javascript
112
+ const client = new stytch.Client({
113
+ project_id: process.env.STYTCH_PROJECT_ID,
114
+ secret: process.env.STYTCH_SECRET,
115
+ environment: 'test', // or 'live' (default)
116
+ });
117
+ ```
118
+
119
+ ### Full Configuration Options
120
+
121
+ ```javascript
122
+ const client = new stytch.Client({
123
+ project_id: process.env.STYTCH_PROJECT_ID,
124
+ secret: process.env.STYTCH_SECRET,
125
+ environment: 'live',
126
+ timeout: 30000, // Request timeout in milliseconds
127
+ });
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Core API Surfaces
133
+
134
+ ### 1. Magic Links
135
+
136
+ Magic links provide passwordless authentication via email.
137
+
138
+ #### Send Magic Link (Login or Create)
139
+
140
+ ```javascript
141
+ // Minimal example
142
+ const response = await client.magicLinks.email.loginOrCreate({
143
+ email: 'user@example.com',
144
+ });
145
+
146
+ console.log(response.user_id);
147
+ console.log(response.email_id);
148
+ ```
149
+
150
+ #### Send Magic Link with Custom URLs
151
+
152
+ ```javascript
153
+ const response = await client.magicLinks.email.loginOrCreate({
154
+ email: 'user@example.com',
155
+ login_magic_link_url: 'https://example.com/authenticate?token={{token}}',
156
+ signup_magic_link_url: 'https://example.com/authenticate?token={{token}}',
157
+ login_expiration_minutes: 15,
158
+ signup_expiration_minutes: 60,
159
+ });
160
+ ```
161
+
162
+ #### Advanced Magic Link with Options
163
+
164
+ ```javascript
165
+ const response = await client.magicLinks.email.loginOrCreate({
166
+ email: 'user@example.com',
167
+ login_magic_link_url: 'https://example.com/authenticate',
168
+ signup_magic_link_url: 'https://example.com/authenticate',
169
+ login_expiration_minutes: 15,
170
+ signup_expiration_minutes: 60,
171
+ login_template_id: 'custom-login-template',
172
+ signup_template_id: 'custom-signup-template',
173
+ attributes: {
174
+ ip_address: '192.168.1.1',
175
+ },
176
+ code_challenge: 'challenge_string', // For PKCE flow
177
+ user_id: 'user-123', // Associate with existing user
178
+ session_duration_minutes: 60,
179
+ });
180
+ ```
181
+
182
+ #### Authenticate Magic Link
183
+
184
+ ```javascript
185
+ // Minimal example
186
+ const response = await client.magicLinks.authenticate({
187
+ token: 'DOYoip3rvIMMW5lgItikFK-Ak1CfMsgjuiCyI7uuU94=',
188
+ });
189
+
190
+ console.log(response.user);
191
+ console.log(response.session_token);
192
+ console.log(response.session_jwt);
193
+ ```
194
+
195
+ #### Authenticate with Session Options
196
+
197
+ ```javascript
198
+ const response = await client.magicLinks.authenticate({
199
+ token: 'DOYoip3rvIMMW5lgItikFK-Ak1CfMsgjuiCyI7uuU94=',
200
+ session_duration_minutes: 60,
201
+ session_custom_claims: {
202
+ custom_claim: 'value',
203
+ },
204
+ attributes: {
205
+ ip_address: '192.168.1.1',
206
+ user_agent: 'Mozilla/5.0...',
207
+ },
208
+ });
209
+ ```
210
+
211
+ #### Send Magic Link Only (No Auto-Create)
212
+
213
+ ```javascript
214
+ const response = await client.magicLinks.email.send({
215
+ email: 'user@example.com',
216
+ login_magic_link_url: 'https://example.com/authenticate',
217
+ login_expiration_minutes: 15,
218
+ });
219
+ ```
220
+
221
+ #### Create Embeddable Magic Link
222
+
223
+ ```javascript
224
+ const response = await client.magicLinks.email.createEmbeddable({
225
+ user_id: 'user-live-123',
226
+ embeddable_magic_link_url: 'https://example.com/authenticate',
227
+ expiration_minutes: 10,
228
+ });
229
+
230
+ console.log(response.token); // Use in your own emails
231
+ ```
232
+
233
+ ---
234
+
235
+ ### 2. One-Time Passcodes (OTP)
236
+
237
+ #### SMS OTP
238
+
239
+ **Send OTP via SMS:**
240
+
241
+ ```javascript
242
+ // Minimal example
243
+ const response = await client.otps.sms.send({
244
+ phone_number: '+15555555555',
245
+ });
246
+
247
+ console.log(response.phone_id);
248
+ ```
249
+
250
+ **Send OTP with Options:**
251
+
252
+ ```javascript
253
+ const response = await client.otps.sms.send({
254
+ phone_number: '+15555555555',
255
+ expiration_minutes: 10,
256
+ attributes: {
257
+ ip_address: '192.168.1.1',
258
+ },
259
+ user_id: 'user-123', // Associate with existing user
260
+ });
261
+ ```
262
+
263
+ **Authenticate SMS OTP:**
264
+
265
+ ```javascript
266
+ // Minimal example
267
+ const response = await client.otps.authenticate({
268
+ method_id: 'phone-id-123',
269
+ code: '123456',
270
+ });
271
+ ```
272
+
273
+ **Authenticate with Session:**
274
+
275
+ ```javascript
276
+ const response = await client.otps.authenticate({
277
+ method_id: 'phone-id-123',
278
+ code: '123456',
279
+ session_duration_minutes: 60,
280
+ attributes: {
281
+ ip_address: '192.168.1.1',
282
+ },
283
+ });
284
+
285
+ console.log(response.user);
286
+ console.log(response.session_token);
287
+ ```
288
+
289
+ #### Email OTP
290
+
291
+ **Send OTP via Email:**
292
+
293
+ ```javascript
294
+ // Minimal example
295
+ const response = await client.otps.email.send({
296
+ email: 'user@example.com',
297
+ });
298
+ ```
299
+
300
+ **Send with Options:**
301
+
302
+ ```javascript
303
+ const response = await client.otps.email.send({
304
+ email: 'user@example.com',
305
+ expiration_minutes: 10,
306
+ login_template_id: 'custom-template',
307
+ user_id: 'user-123',
308
+ });
309
+ ```
310
+
311
+ **Authenticate Email OTP:**
312
+
313
+ ```javascript
314
+ const response = await client.otps.authenticate({
315
+ method_id: 'email-id-123',
316
+ code: '123456',
317
+ session_duration_minutes: 60,
318
+ });
319
+ ```
320
+
321
+ #### WhatsApp OTP
322
+
323
+ **Send OTP via WhatsApp:**
324
+
325
+ ```javascript
326
+ const response = await client.otps.whatsapp.send({
327
+ phone_number: '+15555555555',
328
+ });
329
+ ```
330
+
331
+ **Authenticate WhatsApp OTP:**
332
+
333
+ ```javascript
334
+ const response = await client.otps.authenticate({
335
+ method_id: 'phone-id-123',
336
+ code: '123456',
337
+ });
338
+ ```
339
+
340
+ #### Login or Create User with SMS
341
+
342
+ ```javascript
343
+ const response = await client.otps.sms.loginOrCreate({
344
+ phone_number: '+15555555555',
345
+ expiration_minutes: 10,
346
+ });
347
+ ```
348
+
349
+ #### Login or Create User with Email
350
+
351
+ ```javascript
352
+ const response = await client.otps.email.loginOrCreate({
353
+ email: 'user@example.com',
354
+ expiration_minutes: 10,
355
+ });
356
+ ```
357
+
358
+ ---
359
+
360
+ ### 3. OAuth
361
+
362
+ OAuth enables authentication via third-party providers.
363
+
364
+ #### Start OAuth Flow
365
+
366
+ ```javascript
367
+ // Get OAuth authorization URL
368
+ const response = await client.oauth.start({
369
+ provider: 'google',
370
+ signup_redirect_url: 'https://example.com/authenticate',
371
+ login_redirect_url: 'https://example.com/authenticate',
372
+ });
373
+
374
+ console.log(response.oauth_url); // Redirect user here
375
+ ```
376
+
377
+ #### Start OAuth with Options
378
+
379
+ ```javascript
380
+ const response = await client.oauth.start({
381
+ provider: 'google',
382
+ signup_redirect_url: 'https://example.com/authenticate',
383
+ login_redirect_url: 'https://example.com/authenticate',
384
+ custom_scopes: ['https://www.googleapis.com/auth/calendar.readonly'],
385
+ provider_params: {
386
+ access_type: 'offline',
387
+ },
388
+ code_challenge: 'challenge_string', // For PKCE
389
+ });
390
+ ```
391
+
392
+ #### Authenticate OAuth
393
+
394
+ ```javascript
395
+ // Minimal example
396
+ const response = await client.oauth.authenticate({
397
+ token: 'oauth-token-from-callback',
398
+ });
399
+
400
+ console.log(response.user);
401
+ console.log(response.session_token);
402
+ ```
403
+
404
+ #### Authenticate with Session Options
405
+
406
+ ```javascript
407
+ const response = await client.oauth.authenticate({
408
+ token: 'oauth-token-from-callback',
409
+ session_duration_minutes: 60,
410
+ session_custom_claims: {
411
+ custom_claim: 'value',
412
+ },
413
+ });
414
+ ```
415
+
416
+ #### Supported OAuth Providers
417
+
418
+ - `google`
419
+ - `microsoft`
420
+ - `facebook`
421
+ - `github`
422
+ - `gitlab`
423
+ - `slack`
424
+ - `linkedin`
425
+ - `amazon`
426
+ - `bitbucket`
427
+ - `coinbase`
428
+ - `discord`
429
+ - `figma`
430
+ - `hubspot`
431
+ - `salesforce`
432
+ - `shopify`
433
+ - `snapchat`
434
+ - `tiktok`
435
+ - `twitch`
436
+ - `twitter`
437
+ - `yahoo`
438
+
439
+ ---
440
+
441
+ ### 4. Sessions
442
+
443
+ Sessions manage authenticated user state.
444
+
445
+ #### Authenticate Session
446
+
447
+ ```javascript
448
+ // Minimal example
449
+ const response = await client.sessions.authenticate({
450
+ session_token: 'session-token-here',
451
+ });
452
+
453
+ console.log(response.user);
454
+ console.log(response.session); // Contains session data
455
+ ```
456
+
457
+ #### Authenticate with JWT
458
+
459
+ ```javascript
460
+ const response = await client.sessions.authenticateJwt({
461
+ session_jwt: 'jwt-token-here',
462
+ });
463
+ ```
464
+
465
+ #### Authenticate Session with Refresh
466
+
467
+ ```javascript
468
+ const response = await client.sessions.authenticate({
469
+ session_token: 'session-token-here',
470
+ session_duration_minutes: 60, // Extend session
471
+ });
472
+
473
+ // Store new token
474
+ const newSessionToken = response.session_token;
475
+ ```
476
+
477
+ #### Get Session
478
+
479
+ ```javascript
480
+ const response = await client.sessions.get({
481
+ user_id: 'user-123',
482
+ });
483
+
484
+ console.log(response.sessions); // All active sessions
485
+ ```
486
+
487
+ #### Revoke Session
488
+
489
+ ```javascript
490
+ await client.sessions.revoke({
491
+ session_token: 'session-token-to-revoke',
492
+ });
493
+ ```
494
+
495
+ #### Revoke Session by ID
496
+
497
+ ```javascript
498
+ await client.sessions.revoke({
499
+ session_id: 'session-id-123',
500
+ });
501
+ ```
502
+
503
+ #### Migrate Session
504
+
505
+ ```javascript
506
+ const response = await client.sessions.migrate({
507
+ session_token: 'old-session-token',
508
+ session_duration_minutes: 60,
509
+ });
510
+
511
+ console.log(response.session_token); // New token
512
+ ```
513
+
514
+ ---
515
+
516
+ ### 5. Users
517
+
518
+ Manage user accounts and data.
519
+
520
+ #### Get User
521
+
522
+ ```javascript
523
+ const response = await client.users.get({
524
+ user_id: 'user-123',
525
+ });
526
+
527
+ console.log(response.user);
528
+ console.log(response.user.emails);
529
+ console.log(response.user.phone_numbers);
530
+ ```
531
+
532
+ #### Create User
533
+
534
+ ```javascript
535
+ const response = await client.users.create({
536
+ email: 'user@example.com',
537
+ name: {
538
+ first_name: 'John',
539
+ last_name: 'Doe',
540
+ },
541
+ });
542
+ ```
543
+
544
+ #### Create User with Multiple Methods
545
+
546
+ ```javascript
547
+ const response = await client.users.create({
548
+ email: 'user@example.com',
549
+ phone_number: '+15555555555',
550
+ name: {
551
+ first_name: 'John',
552
+ last_name: 'Doe',
553
+ },
554
+ attributes: {
555
+ custom_attribute: 'value',
556
+ },
557
+ });
558
+ ```
559
+
560
+ #### Update User
561
+
562
+ ```javascript
563
+ const response = await client.users.update({
564
+ user_id: 'user-123',
565
+ name: {
566
+ first_name: 'Jane',
567
+ last_name: 'Smith',
568
+ },
569
+ attributes: {
570
+ custom_field: 'new_value',
571
+ },
572
+ });
573
+ ```
574
+
575
+ #### Delete User
576
+
577
+ ```javascript
578
+ await client.users.delete({
579
+ user_id: 'user-123',
580
+ });
581
+ ```
582
+
583
+ #### Search Users
584
+
585
+ ```javascript
586
+ // Basic search
587
+ const response = await client.users.search({
588
+ query: {
589
+ operator: 'AND',
590
+ operands: [
591
+ {
592
+ filter_name: 'status',
593
+ filter_value: ['active'],
594
+ },
595
+ ],
596
+ },
597
+ });
598
+
599
+ console.log(response.results);
600
+ ```
601
+
602
+ #### Advanced User Search
603
+
604
+ ```javascript
605
+ const response = await client.users.search({
606
+ query: {
607
+ operator: 'AND',
608
+ operands: [
609
+ {
610
+ filter_name: 'email_verified',
611
+ filter_value: [true],
612
+ },
613
+ {
614
+ filter_name: 'created_at',
615
+ filter_value: {
616
+ greater_than: '2024-01-01T00:00:00Z',
617
+ },
618
+ },
619
+ ],
620
+ },
621
+ limit: 100,
622
+ cursor: 'cursor-from-previous-request',
623
+ });
624
+ ```
625
+
626
+ #### Delete Email
627
+
628
+ ```javascript
629
+ await client.users.deleteEmail({
630
+ email_id: 'email-id-123',
631
+ });
632
+ ```
633
+
634
+ #### Delete Phone Number
635
+
636
+ ```javascript
637
+ await client.users.deletePhoneNumber({
638
+ phone_id: 'phone-id-123',
639
+ });
640
+ ```
641
+
642
+ ---
643
+
644
+ ### 6. Passwords
645
+
646
+ Traditional password authentication.
647
+
648
+ #### Create Password
649
+
650
+ ```javascript
651
+ const response = await client.passwords.create({
652
+ email: 'user@example.com',
653
+ password: 'SecurePassword123!',
654
+ session_duration_minutes: 60,
655
+ });
656
+
657
+ console.log(response.user);
658
+ console.log(response.session_token);
659
+ ```
660
+
661
+ #### Authenticate Password
662
+
663
+ ```javascript
664
+ // Minimal example
665
+ const response = await client.passwords.authenticate({
666
+ email: 'user@example.com',
667
+ password: 'SecurePassword123!',
668
+ });
669
+ ```
670
+
671
+ #### Authenticate with Session
672
+
673
+ ```javascript
674
+ const response = await client.passwords.authenticate({
675
+ email: 'user@example.com',
676
+ password: 'SecurePassword123!',
677
+ session_duration_minutes: 60,
678
+ session_custom_claims: {
679
+ custom_claim: 'value',
680
+ },
681
+ });
682
+
683
+ console.log(response.session_token);
684
+ ```
685
+
686
+ #### Strength Check
687
+
688
+ ```javascript
689
+ const response = await client.passwords.strengthCheck({
690
+ email: 'user@example.com',
691
+ password: 'password-to-check',
692
+ });
693
+
694
+ console.log(response.valid_password);
695
+ console.log(response.score);
696
+ console.log(response.breached_password);
697
+ console.log(response.strength_policy);
698
+ console.log(response.breach_detection_on_create);
699
+ ```
700
+
701
+ #### Initiate Password Reset
702
+
703
+ ```javascript
704
+ const response = await client.passwords.email.resetStart({
705
+ email: 'user@example.com',
706
+ reset_password_redirect_url: 'https://example.com/reset',
707
+ reset_password_expiration_minutes: 30,
708
+ });
709
+ ```
710
+
711
+ #### Reset Password by Email
712
+
713
+ ```javascript
714
+ const response = await client.passwords.email.reset({
715
+ token: 'reset-token-from-email',
716
+ password: 'NewSecurePassword123!',
717
+ session_duration_minutes: 60,
718
+ });
719
+
720
+ console.log(response.user);
721
+ console.log(response.session_token);
722
+ ```
723
+
724
+ #### Reset Password by Session
725
+
726
+ ```javascript
727
+ const response = await client.passwords.session.reset({
728
+ session_token: 'active-session-token',
729
+ password: 'NewSecurePassword123!',
730
+ });
731
+ ```
732
+
733
+ #### Migrate Password (from existing system)
734
+
735
+ ```javascript
736
+ const response = await client.passwords.migrate({
737
+ email: 'user@example.com',
738
+ hash: '$2a$10$...', // bcrypt hash
739
+ hash_type: 'bcrypt',
740
+ name: {
741
+ first_name: 'John',
742
+ last_name: 'Doe',
743
+ },
744
+ });
745
+ ```
746
+
747
+ ---
748
+
749
+ ### 7. WebAuthn
750
+
751
+ Passwordless authentication using biometrics or security keys.
752
+
753
+ #### Register WebAuthn Start
754
+
755
+ ```javascript
756
+ const response = await client.webauthn.registerStart({
757
+ user_id: 'user-123',
758
+ domain: 'example.com',
759
+ authenticator_type: 'platform', // or 'cross-platform'
760
+ });
761
+
762
+ console.log(response.public_key_credential_creation_options);
763
+ ```
764
+
765
+ #### Register WebAuthn Finish
766
+
767
+ ```javascript
768
+ const response = await client.webauthn.register({
769
+ user_id: 'user-123',
770
+ public_key_credential: 'credential-from-client',
771
+ session_duration_minutes: 60,
772
+ });
773
+
774
+ console.log(response.webauthn_registration_id);
775
+ console.log(response.session_token);
776
+ ```
777
+
778
+ #### Authenticate WebAuthn Start
779
+
780
+ ```javascript
781
+ const response = await client.webauthn.authenticateStart({
782
+ domain: 'example.com',
783
+ user_id: 'user-123', // Optional
784
+ });
785
+
786
+ console.log(response.public_key_credential_request_options);
787
+ ```
788
+
789
+ #### Authenticate WebAuthn Finish
790
+
791
+ ```javascript
792
+ const response = await client.webauthn.authenticate({
793
+ public_key_credential: 'credential-from-client',
794
+ session_duration_minutes: 60,
795
+ });
796
+
797
+ console.log(response.user);
798
+ console.log(response.session_token);
799
+ ```
800
+
801
+ #### Update WebAuthn
802
+
803
+ ```javascript
804
+ const response = await client.webauthn.update({
805
+ webauthn_registration_id: 'webauthn-reg-id-123',
806
+ name: 'My Fingerprint',
807
+ });
808
+ ```
809
+
810
+ ---
811
+
812
+ ### 8. TOTP (Time-based One-Time Passwords)
813
+
814
+ #### Create TOTP
815
+
816
+ ```javascript
817
+ const response = await client.totps.create({
818
+ user_id: 'user-123',
819
+ expiration_minutes: 10,
820
+ });
821
+
822
+ console.log(response.secret);
823
+ console.log(response.qr_code); // URL for QR code image
824
+ console.log(response.recovery_codes);
825
+ ```
826
+
827
+ #### Authenticate TOTP
828
+
829
+ ```javascript
830
+ const response = await client.totps.authenticate({
831
+ user_id: 'user-123',
832
+ totp_code: '123456',
833
+ session_duration_minutes: 60,
834
+ });
835
+
836
+ console.log(response.session_token);
837
+ ```
838
+
839
+ #### Get TOTPs
840
+
841
+ ```javascript
842
+ const response = await client.totps.get({
843
+ user_id: 'user-123',
844
+ });
845
+
846
+ console.log(response.totps);
847
+ ```
848
+
849
+ #### Recover TOTP
850
+
851
+ ```javascript
852
+ const response = await client.totps.recoveryCodes({
853
+ user_id: 'user-123',
854
+ recovery_code: 'recovery-code-string',
855
+ session_duration_minutes: 60,
856
+ });
857
+
858
+ console.log(response.session_token);
859
+ ```
860
+
861
+ ---
862
+
863
+ ### 9. Crypto Wallets (Web3)
864
+
865
+ #### Authenticate Wallet Start
866
+
867
+ ```javascript
868
+ const response = await client.cryptoWallets.authenticateStart({
869
+ crypto_wallet_address: '0x1234...',
870
+ crypto_wallet_type: 'ethereum',
871
+ });
872
+
873
+ console.log(response.challenge);
874
+ ```
875
+
876
+ #### Authenticate Wallet Finish
877
+
878
+ ```javascript
879
+ const response = await client.cryptoWallets.authenticate({
880
+ crypto_wallet_address: '0x1234...',
881
+ crypto_wallet_type: 'ethereum',
882
+ signature: 'signed-challenge',
883
+ session_duration_minutes: 60,
884
+ });
885
+
886
+ console.log(response.user);
887
+ console.log(response.session_token);
888
+ ```
889
+
890
+ ---
891
+
892
+ ### 10. M2M (Machine-to-Machine)
893
+
894
+ #### Authenticate M2M Token
895
+
896
+ ```javascript
897
+ const response = await client.m2m.authenticateToken({
898
+ access_token: 'm2m-access-token',
899
+ });
900
+
901
+ console.log(response.member_id);
902
+ console.log(response.scopes);
903
+ ```
904
+
905
+ #### Get M2M Client
906
+
907
+ ```javascript
908
+ const response = await client.m2m.clients.get({
909
+ client_id: 'client-id-123',
910
+ });
911
+
912
+ console.log(response.m2m_client);
913
+ ```
914
+
915
+ ---
916
+
917
+ ## B2B API Surfaces
918
+
919
+ ### 1. Organizations
920
+
921
+ #### Create Organization
922
+
923
+ ```javascript
924
+ const response = await b2bClient.organizations.create({
925
+ organization_name: 'Acme Corp',
926
+ organization_slug: 'acme',
927
+ email_allowed_domains: ['acme.com'],
928
+ });
929
+
930
+ console.log(response.organization);
931
+ ```
932
+
933
+ #### Get Organization
934
+
935
+ ```javascript
936
+ const response = await b2bClient.organizations.get({
937
+ organization_id: 'org-123',
938
+ });
939
+
940
+ console.log(response.organization);
941
+ ```
942
+
943
+ #### Update Organization
944
+
945
+ ```javascript
946
+ const response = await b2bClient.organizations.update({
947
+ organization_id: 'org-123',
948
+ organization_name: 'Acme Corporation',
949
+ });
950
+ ```
951
+
952
+ #### Delete Organization
953
+
954
+ ```javascript
955
+ await b2bClient.organizations.delete({
956
+ organization_id: 'org-123',
957
+ });
958
+ ```
959
+
960
+ ---
961
+
962
+ ### 2. Members
963
+
964
+ #### Create Member
965
+
966
+ ```javascript
967
+ const response = await b2bClient.organizations.members.create({
968
+ organization_id: 'org-123',
969
+ email_address: 'member@acme.com',
970
+ name: 'John Doe',
971
+ is_breakglass: false,
972
+ });
973
+
974
+ console.log(response.member);
975
+ ```
976
+
977
+ #### Get Member
978
+
979
+ ```javascript
980
+ const response = await b2bClient.organizations.members.get({
981
+ organization_id: 'org-123',
982
+ member_id: 'member-123',
983
+ });
984
+
985
+ console.log(response.member);
986
+ ```
987
+
988
+ #### Update Member
989
+
990
+ ```javascript
991
+ const response = await b2bClient.organizations.members.update({
992
+ organization_id: 'org-123',
993
+ member_id: 'member-123',
994
+ name: 'Jane Doe',
995
+ });
996
+ ```
997
+
998
+ #### Delete Member
999
+
1000
+ ```javascript
1001
+ await b2bClient.organizations.members.delete({
1002
+ organization_id: 'org-123',
1003
+ member_id: 'member-123',
1004
+ });
1005
+ ```
1006
+
1007
+ ---
1008
+
1009
+ ### 3. B2B Magic Links
1010
+
1011
+ #### Send B2B Magic Link
1012
+
1013
+ ```javascript
1014
+ const response = await b2bClient.magicLinks.email.loginOrSignup({
1015
+ organization_id: 'org-123',
1016
+ email_address: 'member@acme.com',
1017
+ login_redirect_url: 'https://acme.com/authenticate',
1018
+ signup_redirect_url: 'https://acme.com/authenticate',
1019
+ });
1020
+ ```
1021
+
1022
+ #### Authenticate B2B Magic Link
1023
+
1024
+ ```javascript
1025
+ const response = await b2bClient.magicLinks.authenticate({
1026
+ magic_links_token: 'token-from-email',
1027
+ session_duration_minutes: 60,
1028
+ });
1029
+
1030
+ console.log(response.member);
1031
+ console.log(response.organization);
1032
+ console.log(response.session_token);
1033
+ ```
1034
+
1035
+ ---
1036
+
1037
+ ### 4. B2B Sessions
1038
+
1039
+ #### Authenticate B2B Session
1040
+
1041
+ ```javascript
1042
+ const response = await b2bClient.sessions.authenticate({
1043
+ session_token: 'session-token-here',
1044
+ });
1045
+
1046
+ console.log(response.member);
1047
+ console.log(response.organization);
1048
+ console.log(response.session);
1049
+ ```
1050
+
1051
+ #### Authenticate B2B JWT
1052
+
1053
+ ```javascript
1054
+ const response = await b2bClient.sessions.authenticateJwt({
1055
+ session_jwt: 'jwt-token-here',
1056
+ });
1057
+ ```
1058
+
1059
+ #### Get B2B Session
1060
+
1061
+ ```javascript
1062
+ const response = await b2bClient.sessions.get({
1063
+ organization_id: 'org-123',
1064
+ member_id: 'member-123',
1065
+ });
1066
+
1067
+ console.log(response.sessions);
1068
+ ```
1069
+
1070
+ #### Revoke B2B Session
1071
+
1072
+ ```javascript
1073
+ await b2bClient.sessions.revoke({
1074
+ session_token: 'session-token-to-revoke',
1075
+ });
1076
+ ```
1077
+
1078
+ ---
1079
+
1080
+ ### 5. B2B OAuth
1081
+
1082
+ #### Start B2B OAuth
1083
+
1084
+ ```javascript
1085
+ const response = await b2bClient.oauth.start({
1086
+ organization_id: 'org-123',
1087
+ provider: 'google',
1088
+ login_redirect_url: 'https://acme.com/authenticate',
1089
+ signup_redirect_url: 'https://acme.com/authenticate',
1090
+ });
1091
+
1092
+ console.log(response.oauth_url);
1093
+ ```
1094
+
1095
+ #### Authenticate B2B OAuth
1096
+
1097
+ ```javascript
1098
+ const response = await b2bClient.oauth.authenticate({
1099
+ oauth_token: 'token-from-callback',
1100
+ session_duration_minutes: 60,
1101
+ });
1102
+
1103
+ console.log(response.member);
1104
+ console.log(response.organization);
1105
+ console.log(response.session_token);
1106
+ ```
1107
+
1108
+ ---
1109
+
1110
+ ### 6. B2B SMS OTP
1111
+
1112
+ #### Send B2B SMS OTP
1113
+
1114
+ ```javascript
1115
+ const response = await b2bClient.otps.sms.send({
1116
+ organization_id: 'org-123',
1117
+ member_id: 'member-123',
1118
+ phone_number: '+15555555555',
1119
+ });
1120
+ ```
1121
+
1122
+ #### Authenticate B2B SMS OTP
1123
+
1124
+ ```javascript
1125
+ const response = await b2bClient.otps.sms.authenticate({
1126
+ organization_id: 'org-123',
1127
+ member_id: 'member-123',
1128
+ code: '123456',
1129
+ session_duration_minutes: 60,
1130
+ });
1131
+
1132
+ console.log(response.member);
1133
+ console.log(response.session_token);
1134
+ ```
1135
+
1136
+ ---
1137
+
1138
+ ### 7. SSO (Single Sign-On)
1139
+
1140
+ #### Start SSO
1141
+
1142
+ ```javascript
1143
+ const response = await b2bClient.sso.start({
1144
+ connection_id: 'sso-connection-123',
1145
+ login_redirect_url: 'https://acme.com/authenticate',
1146
+ signup_redirect_url: 'https://acme.com/authenticate',
1147
+ });
1148
+
1149
+ console.log(response.sso_url);
1150
+ ```
1151
+
1152
+ #### Authenticate SSO
1153
+
1154
+ ```javascript
1155
+ const response = await b2bClient.sso.authenticate({
1156
+ sso_token: 'token-from-sso-provider',
1157
+ session_duration_minutes: 60,
1158
+ });
1159
+
1160
+ console.log(response.member);
1161
+ console.log(response.organization);
1162
+ ```
1163
+
1164
+ #### Get SSO Connections
1165
+
1166
+ ```javascript
1167
+ const response = await b2bClient.sso.getConnections({
1168
+ organization_id: 'org-123',
1169
+ });
1170
+
1171
+ console.log(response.saml_connections);
1172
+ console.log(response.oidc_connections);
1173
+ ```
1174
+
1175
+ #### Create SAML Connection
1176
+
1177
+ ```javascript
1178
+ const response = await b2bClient.sso.saml.createConnection({
1179
+ organization_id: 'org-123',
1180
+ display_name: 'Acme SAML',
1181
+ });
1182
+
1183
+ console.log(response.connection);
1184
+ ```
1185
+
1186
+ #### Update SAML Connection
1187
+
1188
+ ```javascript
1189
+ const response = await b2bClient.sso.saml.updateConnection({
1190
+ organization_id: 'org-123',
1191
+ connection_id: 'saml-connection-123',
1192
+ idp_entity_id: 'https://idp.acme.com/entity',
1193
+ idp_sso_url: 'https://idp.acme.com/sso',
1194
+ attribute_mapping: {
1195
+ email: 'email',
1196
+ first_name: 'firstName',
1197
+ last_name: 'lastName',
1198
+ },
1199
+ x509_certificate: 'certificate-string',
1200
+ });
1201
+ ```
1202
+
1203
+ #### Create OIDC Connection
1204
+
1205
+ ```javascript
1206
+ const response = await b2bClient.sso.oidc.createConnection({
1207
+ organization_id: 'org-123',
1208
+ display_name: 'Acme OIDC',
1209
+ });
1210
+
1211
+ console.log(response.connection);
1212
+ ```
1213
+
1214
+ #### Update OIDC Connection
1215
+
1216
+ ```javascript
1217
+ const response = await b2bClient.sso.oidc.updateConnection({
1218
+ organization_id: 'org-123',
1219
+ connection_id: 'oidc-connection-123',
1220
+ issuer: 'https://idp.acme.com',
1221
+ client_id: 'client-id',
1222
+ client_secret: 'client-secret',
1223
+ authorization_url: 'https://idp.acme.com/authorize',
1224
+ token_url: 'https://idp.acme.com/token',
1225
+ userinfo_url: 'https://idp.acme.com/userinfo',
1226
+ });
1227
+ ```
1228
+
1229
+ ---
1230
+
1231
+ ### 8. Discovery
1232
+
1233
+ Discovery allows users to find and join organizations.
1234
+
1235
+ #### Create Organization via Discovery
1236
+
1237
+ ```javascript
1238
+ const response = await b2bClient.discovery.organizations.create({
1239
+ intermediate_session_token: 'token-from-discovery',
1240
+ organization_name: 'New Org',
1241
+ organization_slug: 'new-org',
1242
+ session_duration_minutes: 60,
1243
+ });
1244
+
1245
+ console.log(response.organization);
1246
+ console.log(response.session_token);
1247
+ ```
1248
+
1249
+ #### List Discovered Organizations
1250
+
1251
+ ```javascript
1252
+ const response = await b2bClient.discovery.organizations.list({
1253
+ intermediate_session_token: 'token-from-discovery',
1254
+ });
1255
+
1256
+ console.log(response.discovered_organizations);
1257
+ ```
1258
+
1259
+ ---
1260
+
1261
+ ### 9. RBAC (Role-Based Access Control)
1262
+
1263
+ #### Check Member Permissions
1264
+
1265
+ ```javascript
1266
+ const response = await b2bClient.rbac.policy({
1267
+ organization_id: 'org-123',
1268
+ member_id: 'member-123',
1269
+ resource_id: 'document-123',
1270
+ action: 'read',
1271
+ });
1272
+
1273
+ console.log(response.authorized);
1274
+ ```
1275
+
1276
+ ---
1277
+
1278
+ ## Error Handling
1279
+
1280
+ ### Basic Error Handling
1281
+
1282
+ ```javascript
1283
+ try {
1284
+ const response = await client.magicLinks.email.loginOrCreate({
1285
+ email: 'user@example.com',
1286
+ });
1287
+ console.log(response.user_id);
1288
+ } catch (error) {
1289
+ console.error('Error:', error.error_type);
1290
+ console.error('Message:', error.error_message);
1291
+ console.error('URL:', error.error_url);
1292
+ }
1293
+ ```
1294
+
1295
+ ### Detailed Error Handling
1296
+
1297
+ ```javascript
1298
+ const response = await client.magicLinks.authenticate({
1299
+ token: token,
1300
+ }).catch((error) => {
1301
+ if (error.error_type === 'unable_to_auth_magic_link') {
1302
+ // Token invalid, expired, or already used
1303
+ return { error: 'Invalid or expired magic link' };
1304
+ } else if (error.error_type === 'invalid_token') {
1305
+ // Malformed token
1306
+ return { error: 'Invalid token format' };
1307
+ } else {
1308
+ // Other errors
1309
+ return { error: 'Authentication failed' };
1310
+ }
1311
+ });
1312
+ ```
1313
+
1314
+ ### Common Error Types
1315
+
1316
+ - `unable_to_auth_magic_link` - Magic link token invalid, expired, or used
1317
+ - `invalid_token` - Token format is invalid
1318
+ - `session_not_found` - Session doesn't exist
1319
+ - `user_not_found` - User doesn't exist
1320
+ - `duplicate_email` - Email already exists
1321
+ - `invalid_credentials` - Password authentication failed
1322
+ - `rate_limit_exceeded` - Too many requests
1323
+ - `unauthorized` - API credentials invalid
1324
+
1325
+ ---
1326
+
1327
+ ## Express.js Integration Example
1328
+
1329
+ ### Complete Auth Flow
1330
+
1331
+ ```javascript
1332
+ const express = require('express');
1333
+ const stytch = require('stytch');
1334
+ const session = require('express-session');
1335
+
1336
+ const app = express();
1337
+ app.use(express.json());
1338
+ app.use(session({
1339
+ secret: 'your-session-secret',
1340
+ resave: false,
1341
+ saveUninitialized: false,
1342
+ }));
1343
+
1344
+ const client = new stytch.Client({
1345
+ project_id: process.env.STYTCH_PROJECT_ID,
1346
+ secret: process.env.STYTCH_SECRET,
1347
+ });
1348
+
1349
+ // Login endpoint
1350
+ app.post('/login', async (req, res) => {
1351
+ try {
1352
+ const { email } = req.body;
1353
+ const response = await client.magicLinks.email.loginOrCreate({
1354
+ email,
1355
+ login_magic_link_url: `${process.env.BASE_URL}/authenticate`,
1356
+ signup_magic_link_url: `${process.env.BASE_URL}/authenticate`,
1357
+ });
1358
+ res.json({ success: true, user_id: response.user_id });
1359
+ } catch (error) {
1360
+ res.status(400).json({ error: error.error_message });
1361
+ }
1362
+ });
1363
+
1364
+ // Authentication callback
1365
+ app.get('/authenticate', async (req, res) => {
1366
+ try {
1367
+ const { token } = req.query;
1368
+ const response = await client.magicLinks.authenticate({
1369
+ token,
1370
+ session_duration_minutes: 60,
1371
+ });
1372
+
1373
+ req.session.stytchSessionToken = response.session_token;
1374
+ req.session.userId = response.user.user_id;
1375
+
1376
+ res.redirect('/dashboard');
1377
+ } catch (error) {
1378
+ res.status(400).send('Authentication failed');
1379
+ }
1380
+ });
1381
+
1382
+ // Protected route middleware
1383
+ async function authenticateSession(req, res, next) {
1384
+ const sessionToken = req.session.stytchSessionToken;
1385
+
1386
+ if (!sessionToken) {
1387
+ return res.status(401).json({ error: 'Not authenticated' });
1388
+ }
1389
+
1390
+ try {
1391
+ const response = await client.sessions.authenticate({
1392
+ session_token: sessionToken,
1393
+ });
1394
+
1395
+ req.session.stytchSessionToken = response.session_token;
1396
+ req.user = response.user;
1397
+ next();
1398
+ } catch (error) {
1399
+ req.session.destroy();
1400
+ res.status(401).json({ error: 'Session expired' });
1401
+ }
1402
+ }
1403
+
1404
+ // Protected route
1405
+ app.get('/dashboard', authenticateSession, (req, res) => {
1406
+ res.json({ user: req.user });
1407
+ });
1408
+
1409
+ // Logout
1410
+ app.post('/logout', async (req, res) => {
1411
+ try {
1412
+ await client.sessions.revoke({
1413
+ session_token: req.session.stytchSessionToken,
1414
+ });
1415
+ req.session.destroy();
1416
+ res.json({ success: true });
1417
+ } catch (error) {
1418
+ res.status(400).json({ error: error.error_message });
1419
+ }
1420
+ });
1421
+
1422
+ app.listen(3000);
1423
+ ```
1424
+
1425
+ ---
1426
+
1427
+ ## TypeScript Support
1428
+
1429
+ The SDK includes full TypeScript definitions.
1430
+
1431
+ ### TypeScript Example
1432
+
1433
+ ```typescript
1434
+ import * as stytch from 'stytch';
1435
+
1436
+ const client = new stytch.Client({
1437
+ project_id: process.env.STYTCH_PROJECT_ID!,
1438
+ secret: process.env.STYTCH_SECRET!,
1439
+ });
1440
+
1441
+ async function loginUser(email: string): Promise<string> {
1442
+ const response = await client.magicLinks.email.loginOrCreate({
1443
+ email,
1444
+ });
1445
+ return response.user_id;
1446
+ }
1447
+
1448
+ async function authenticateToken(token: string): Promise<stytch.User> {
1449
+ const response = await client.magicLinks.authenticate({
1450
+ token,
1451
+ });
1452
+ return response.user;
1453
+ }
1454
+ ```
1455
+
1456
+ ### Request/Response Types
1457
+
1458
+ Types follow the pattern: `$Vertical$Product$Method(Request|Response)`
1459
+
1460
+ ```typescript
1461
+ import type {
1462
+ MagicLinksEmailLoginOrCreateRequest,
1463
+ MagicLinksEmailLoginOrCreateResponse,
1464
+ MagicLinksAuthenticateRequest,
1465
+ MagicLinksAuthenticateResponse,
1466
+ } from 'stytch';
1467
+
1468
+ const request: MagicLinksEmailLoginOrCreateRequest = {
1469
+ email: 'user@example.com',
1470
+ login_magic_link_url: 'https://example.com/authenticate',
1471
+ };
1472
+
1473
+ const response: MagicLinksEmailLoginOrCreateResponse =
1474
+ await client.magicLinks.email.loginOrCreate(request);
1475
+ ```
1476
+
1477
+ ---
1478
+
1479
+ ## Testing
1480
+
1481
+ ### Using Test Environment
1482
+
1483
+ ```javascript
1484
+ const client = new stytch.Client({
1485
+ project_id: process.env.STYTCH_TEST_PROJECT_ID,
1486
+ secret: process.env.STYTCH_TEST_SECRET,
1487
+ environment: 'test',
1488
+ });
1489
+ ```
1490
+
1491
+ ### Test Mode Magic Links
1492
+
1493
+ In test mode, use these special test emails:
1494
+ - `sandbox@stytch.com` - Always succeeds
1495
+
1496
+ ### Mock Testing
1497
+
1498
+ ```javascript
1499
+ const mockClient = {
1500
+ magicLinks: {
1501
+ email: {
1502
+ loginOrCreate: jest.fn().mockResolvedValue({
1503
+ user_id: 'user-test-123',
1504
+ email_id: 'email-test-123',
1505
+ }),
1506
+ },
1507
+ authenticate: jest.fn().mockResolvedValue({
1508
+ user: {
1509
+ user_id: 'user-test-123',
1510
+ emails: [{ email: 'test@example.com' }],
1511
+ },
1512
+ session_token: 'test-session-token',
1513
+ }),
1514
+ },
1515
+ };
1516
+ ```
1517
+
1518
+ ---
1519
+
1520
+ ## Webhooks
1521
+
1522
+ ### Verify Webhook Signature
1523
+
1524
+ ```javascript
1525
+ const crypto = require('crypto');
1526
+
1527
+ function verifyWebhookSignature(payload, signature, secret) {
1528
+ const expectedSignature = crypto
1529
+ .createHmac('sha256', secret)
1530
+ .update(payload)
1531
+ .digest('hex');
1532
+
1533
+ return signature === expectedSignature;
1534
+ }
1535
+
1536
+ // Express webhook endpoint
1537
+ app.post('/webhooks/stytch', express.raw({ type: 'application/json' }), (req, res) => {
1538
+ const signature = req.headers['stytch-signature'];
1539
+ const isValid = verifyWebhookSignature(
1540
+ req.body,
1541
+ signature,
1542
+ process.env.STYTCH_WEBHOOK_SECRET
1543
+ );
1544
+
1545
+ if (!isValid) {
1546
+ return res.status(401).send('Invalid signature');
1547
+ }
1548
+
1549
+ const event = JSON.parse(req.body);
1550
+ console.log('Webhook event:', event.event_type);
1551
+
1552
+ res.json({ received: true });
1553
+ });
1554
+ ```
1555
+
1556
+ ### Webhook Event Types
1557
+
1558
+ - `user.created` - New user created
1559
+ - `user.updated` - User data updated
1560
+ - `user.deleted` - User deleted
1561
+ - `session.created` - New session created
1562
+ - `session.revoked` - Session revoked
1563
+ - `magic_link.sent` - Magic link sent
1564
+ - `otp.sent` - OTP sent
1565
+ - `password.strength_check_failed` - Weak password detected
1566
+
1567
+ ---
1568
+
1569
+ ## Session Management Patterns
1570
+
1571
+ ### Session Token in Cookie
1572
+
1573
+ ```javascript
1574
+ app.get('/authenticate', async (req, res) => {
1575
+ const { token } = req.query;
1576
+ const response = await client.magicLinks.authenticate({
1577
+ token,
1578
+ session_duration_minutes: 60,
1579
+ });
1580
+
1581
+ res.cookie('stytch_session', response.session_token, {
1582
+ httpOnly: true,
1583
+ secure: true,
1584
+ maxAge: 60 * 60 * 1000, // 1 hour
1585
+ });
1586
+
1587
+ res.redirect('/dashboard');
1588
+ });
1589
+
1590
+ // Middleware
1591
+ async function authenticate(req, res, next) {
1592
+ const sessionToken = req.cookies.stytch_session;
1593
+ if (!sessionToken) {
1594
+ return res.status(401).json({ error: 'Not authenticated' });
1595
+ }
1596
+
1597
+ try {
1598
+ const response = await client.sessions.authenticate({
1599
+ session_token: sessionToken,
1600
+ });
1601
+ req.user = response.user;
1602
+ next();
1603
+ } catch (error) {
1604
+ res.status(401).json({ error: 'Invalid session' });
1605
+ }
1606
+ }
1607
+ ```
1608
+
1609
+ ### Session Token in Authorization Header
1610
+
1611
+ ```javascript
1612
+ // Middleware
1613
+ async function authenticate(req, res, next) {
1614
+ const authHeader = req.headers.authorization;
1615
+ if (!authHeader) {
1616
+ return res.status(401).json({ error: 'No token provided' });
1617
+ }
1618
+
1619
+ const sessionToken = authHeader.replace('Bearer ', '');
1620
+
1621
+ try {
1622
+ const response = await client.sessions.authenticate({
1623
+ session_token: sessionToken,
1624
+ });
1625
+ req.user = response.user;
1626
+ next();
1627
+ } catch (error) {
1628
+ res.status(401).json({ error: 'Invalid token' });
1629
+ }
1630
+ }
1631
+ ```
1632
+
1633
+ ---
1634
+
1635
+ ## Advanced Use Cases
1636
+
1637
+ ### Multi-Factor Authentication Flow
1638
+
1639
+ ```javascript
1640
+ // Step 1: Primary authentication (password)
1641
+ app.post('/login', async (req, res) => {
1642
+ const { email, password } = req.body;
1643
+
1644
+ try {
1645
+ const response = await client.passwords.authenticate({
1646
+ email,
1647
+ password,
1648
+ session_duration_minutes: 5, // Short session for MFA
1649
+ });
1650
+
1651
+ // Return intermediate session
1652
+ res.json({
1653
+ requires_mfa: true,
1654
+ intermediate_session_token: response.session_token,
1655
+ user_id: response.user_id,
1656
+ });
1657
+ } catch (error) {
1658
+ res.status(401).json({ error: 'Invalid credentials' });
1659
+ }
1660
+ });
1661
+
1662
+ // Step 2: MFA with TOTP
1663
+ app.post('/verify-totp', async (req, res) => {
1664
+ const { user_id, totp_code } = req.body;
1665
+
1666
+ try {
1667
+ const response = await client.totps.authenticate({
1668
+ user_id,
1669
+ totp_code,
1670
+ session_duration_minutes: 60, // Full session after MFA
1671
+ });
1672
+
1673
+ res.json({
1674
+ session_token: response.session_token,
1675
+ user: response.user,
1676
+ });
1677
+ } catch (error) {
1678
+ res.status(401).json({ error: 'Invalid TOTP code' });
1679
+ }
1680
+ });
1681
+ ```
1682
+
1683
+ ### Account Linking
1684
+
1685
+ ```javascript
1686
+ // Link OAuth account to existing user
1687
+ app.post('/link-oauth', authenticateSession, async (req, res) => {
1688
+ const { oauth_token } = req.body;
1689
+
1690
+ try {
1691
+ const response = await client.oauth.authenticate({
1692
+ token: oauth_token,
1693
+ session_token: req.session.stytchSessionToken,
1694
+ });
1695
+
1696
+ res.json({ success: true, user: response.user });
1697
+ } catch (error) {
1698
+ res.status(400).json({ error: error.error_message });
1699
+ }
1700
+ });
1701
+ ```
1702
+
1703
+ ### Custom Email Templates
1704
+
1705
+ ```javascript
1706
+ const response = await client.magicLinks.email.loginOrCreate({
1707
+ email: 'user@example.com',
1708
+ login_magic_link_url: 'https://example.com/authenticate',
1709
+ login_template_id: 'custom-login-template-id',
1710
+ signup_template_id: 'custom-signup-template-id',
1711
+ });
1712
+ ```
1713
+
1714
+ ### IP and User Agent Matching
1715
+
1716
+ ```javascript
1717
+ const response = await client.magicLinks.authenticate({
1718
+ token,
1719
+ attributes: {
1720
+ ip_address: req.ip,
1721
+ user_agent: req.headers['user-agent'],
1722
+ },
1723
+ });
1724
+ ```
1725
+
1726
+ ---
1727
+
1728
+ ## Rate Limiting
1729
+
1730
+ Stytch implements rate limiting on authentication endpoints. Handle rate limit errors:
1731
+
1732
+ ```javascript
1733
+ try {
1734
+ await client.otps.sms.send({ phone_number });
1735
+ } catch (error) {
1736
+ if (error.error_type === 'rate_limit_exceeded') {
1737
+ const retryAfter = error.retry_after; // seconds
1738
+ res.status(429).json({
1739
+ error: 'Too many requests',
1740
+ retry_after: retryAfter,
1741
+ });
1742
+ }
1743
+ }
1744
+ ```
1745
+
1746
+ ---
1747
+
1748
+ ## Migration from Other Auth Systems
1749
+
1750
+ ### Import Users with Passwords
1751
+
1752
+ ```javascript
1753
+ // Migrate user from bcrypt-based system
1754
+ await client.passwords.migrate({
1755
+ email: 'user@example.com',
1756
+ hash: '$2a$10$existing_bcrypt_hash',
1757
+ hash_type: 'bcrypt',
1758
+ name: {
1759
+ first_name: 'John',
1760
+ last_name: 'Doe',
1761
+ },
1762
+ });
1763
+ ```
1764
+
1765
+ ### Supported Hash Types
1766
+
1767
+ - `bcrypt`
1768
+ - `md_5`
1769
+ - `argon_2i`
1770
+ - `argon_2id`
1771
+ - `sha_1`
1772
+ - `scrypt`
1773
+ - `phpass`
1774
+ - `pbkdf_2`
1775
+
1776
+ ---
1777
+
1778
+ ## Async/Await vs Promises
1779
+
1780
+ All SDK methods support both patterns:
1781
+
1782
+ ### Async/Await
1783
+
1784
+ ```javascript
1785
+ async function authenticateUser(token) {
1786
+ const response = await client.magicLinks.authenticate({ token });
1787
+ return response.user;
1788
+ }
1789
+ ```
1790
+
1791
+ ### Promises
1792
+
1793
+ ```javascript
1794
+ function authenticateUser(token) {
1795
+ return client.magicLinks.authenticate({ token })
1796
+ .then(response => response.user)
1797
+ .catch(error => {
1798
+ console.error(error);
1799
+ throw error;
1800
+ });
1801
+ }
1802
+ ```
1803
+
1804
+ ---
1805
+
1806
+ ## Resources
1807
+
1808
+ - **Documentation:** https://stytch.com/docs
1809
+ - **API Reference:** https://stytch.com/docs/api
1810
+ - **GitHub:** https://github.com/stytchauth/stytch-node
1811
+ - **npm Package:** https://www.npmjs.com/package/stytch
1812
+ - **Dashboard:** https://stytch.com/dashboard
1813
+ - **Support:** support@stytch.com