@sylphx/sdk 0.3.0 → 0.3.2

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.
@@ -0,0 +1,2061 @@
1
+ import { NextResponse, NextRequest } from 'next/server';
2
+
3
+ /**
4
+ * Sylphx Unified Middleware — State of the Art
5
+ *
6
+ * ONE middleware function handles EVERYTHING:
7
+ * - Auth routes (mounted automatically, zero manual API routes)
8
+ * - Token refresh (automatic, every request)
9
+ * - Route protection
10
+ * - Cookie management
11
+ *
12
+ * This follows Auth0 v4 / Clerk / Supabase patterns where middleware
13
+ * handles all auth concerns. Apps don't need to create any /api/auth/* routes.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // middleware.ts (or proxy.ts for Next.js 16)
18
+ * import { createSylphxMiddleware } from '@sylphx/sdk/nextjs'
19
+ *
20
+ * export default createSylphxMiddleware({
21
+ * publicRoutes: ['/', '/about', '/pricing'],
22
+ * })
23
+ *
24
+ * export const config = {
25
+ * matcher: ['/((?!_next|.*\\..*).*)', '/'],
26
+ * }
27
+ * ```
28
+ *
29
+ * That's it. No /api/auth/* routes needed.
30
+ */
31
+
32
+ interface SylphxMiddlewareConfig {
33
+ /**
34
+ * Routes that don't require authentication.
35
+ * Supports exact paths and wildcards.
36
+ *
37
+ * @example ['/', '/about', '/pricing', '/blog/*']
38
+ * @default ['/']
39
+ */
40
+ publicRoutes?: string[];
41
+ /**
42
+ * Routes that middleware should completely ignore.
43
+ * Use for webhooks, static assets, or third-party integrations.
44
+ *
45
+ * @default []
46
+ */
47
+ ignoredRoutes?: string[];
48
+ /**
49
+ * URL to redirect unauthenticated users.
50
+ * @default '/login'
51
+ */
52
+ signInUrl?: string;
53
+ /**
54
+ * URL to redirect after sign out.
55
+ * @default '/'
56
+ */
57
+ afterSignOutUrl?: string;
58
+ /**
59
+ * URL to redirect after successful sign in.
60
+ * @default '/dashboard'
61
+ */
62
+ afterSignInUrl?: string;
63
+ /**
64
+ * Auth routes prefix. Routes are mounted at:
65
+ * - {prefix}/callback — OAuth callback handler
66
+ * - {prefix}/signout — Sign out handler
67
+ *
68
+ * @default '/auth'
69
+ */
70
+ authPrefix?: string;
71
+ /**
72
+ * Enable debug logging.
73
+ * @default false
74
+ */
75
+ debug?: boolean;
76
+ /**
77
+ * Secret key for authentication.
78
+ * Override to use a programmatic key instead of SYLPHX_SECRET_KEY env var.
79
+ *
80
+ * Use case: Platform Console uses dynamically generated keys.
81
+ *
82
+ * @default process.env.SYLPHX_SECRET_KEY
83
+ */
84
+ secretKey?: string;
85
+ /**
86
+ * Platform URL for API calls.
87
+ * Override for self-hosted or same-origin deployments.
88
+ *
89
+ * @default process.env.SYLPHX_PLATFORM_URL || 'https://sylphx.com'
90
+ */
91
+ platformUrl?: string;
92
+ /**
93
+ * Callback to add custom headers/logic to responses.
94
+ * Called for every non-auth-route request after SDK processing.
95
+ *
96
+ * Use case: Add security headers, tracking cookies, etc.
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * onResponse: (response, request) => {
101
+ * response.headers.set('X-Custom-Header', 'value')
102
+ * }
103
+ * ```
104
+ */
105
+ onResponse?: (response: NextResponse, request: NextRequest) => void | Promise<void>;
106
+ }
107
+ /**
108
+ * Create Sylphx middleware — State of the Art
109
+ *
110
+ * ONE function handles everything:
111
+ * - Auth routes ({authPrefix}/callback, {authPrefix}/signout)
112
+ * - Token refresh (automatic, every request)
113
+ * - Route protection
114
+ * - Cookie management
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * // middleware.ts (or proxy.ts for Next.js 16)
119
+ * import { createSylphxMiddleware } from '@sylphx/sdk/nextjs'
120
+ *
121
+ * export default createSylphxMiddleware({
122
+ * publicRoutes: ['/', '/about', '/pricing'],
123
+ * })
124
+ *
125
+ * export const config = {
126
+ * matcher: ['/((?!_next|.*\\..*).*)', '/'],
127
+ * }
128
+ * ```
129
+ */
130
+ declare function createSylphxMiddleware(userConfig?: SylphxMiddlewareConfig): (request: NextRequest) => Promise<NextResponse>;
131
+ /**
132
+ * Create recommended matcher config
133
+ */
134
+ declare function createMatcher(): {
135
+ matcher: string[];
136
+ };
137
+ /**
138
+ * Get cookie namespace from secret key (for advanced use)
139
+ */
140
+ declare function getNamespace(secretKey: string): string;
141
+
142
+ interface components {
143
+ schemas: {
144
+ ErrorResponse: {
145
+ error: {
146
+ message: string
147
+ code: string
148
+ }
149
+ }
150
+ User: {
151
+ /** Format: uuid */
152
+ id: string
153
+ /** Format: email */
154
+ email: string
155
+ name: string | null
156
+ /** Format: uri */
157
+ image?: string | null
158
+ emailVerified: boolean
159
+ }
160
+ TokenResponse: {
161
+ accessToken: string
162
+ refreshToken: string
163
+ /** @description Token expiry in seconds */
164
+ expiresIn: number
165
+ user: components['schemas']['User']
166
+ }
167
+ /** @enum {string} */
168
+ OrgRole: 'super_admin' | 'admin' | 'billing' | 'analytics' | 'developer' | 'viewer'
169
+ Organization: {
170
+ /** Format: uuid */
171
+ id: string
172
+ name: string
173
+ slug: string
174
+ /** Format: uri */
175
+ logoUrl: string | null
176
+ /** Format: email */
177
+ email: string | null
178
+ /** Format: email */
179
+ billingEmail: string | null
180
+ /** Format: date-time */
181
+ createdAt: string
182
+ }
183
+ OrganizationMember: {
184
+ /** Format: uuid */
185
+ id: string
186
+ /** Format: uuid */
187
+ userId: string
188
+ /** Format: email */
189
+ email: string
190
+ name: string | null
191
+ /** Format: uri */
192
+ image: string | null
193
+ role: components['schemas']['OrgRole']
194
+ /** Format: date-time */
195
+ joinedAt: string
196
+ }
197
+ OrganizationInvitation: {
198
+ /** Format: uuid */
199
+ id: string
200
+ /** Format: email */
201
+ email: string
202
+ role: components['schemas']['OrgRole']
203
+ /** @enum {string} */
204
+ status: 'pending' | 'accepted' | 'expired' | 'revoked'
205
+ /** Format: uuid */
206
+ invitedById: string
207
+ invitedByName: string | null
208
+ /** Format: date-time */
209
+ expiresAt: string
210
+ /** Format: date-time */
211
+ createdAt: string
212
+ }
213
+ OrganizationMembership: {
214
+ role: components['schemas']['OrgRole']
215
+ /** Format: date-time */
216
+ joinedAt: string
217
+ }
218
+ CreateOrgRequest: {
219
+ name: string
220
+ slug?: string
221
+ /** Format: email */
222
+ email?: string
223
+ }
224
+ UpdateOrgRequest: {
225
+ name?: string
226
+ slug?: string
227
+ /** Format: uri */
228
+ logoUrl?: string | null
229
+ /** Format: email */
230
+ email?: string | null
231
+ /** Format: email */
232
+ billingEmail?: string | null
233
+ }
234
+ InviteMemberRequest: {
235
+ /** Format: email */
236
+ email: string
237
+ role: components['schemas']['OrgRole']
238
+ }
239
+ UpdateMemberRoleRequest: {
240
+ role: components['schemas']['OrgRole']
241
+ }
242
+ AcceptInvitationRequest: {
243
+ token: string
244
+ }
245
+ OrganizationsResponse: {
246
+ organizations: components['schemas']['Organization'][]
247
+ }
248
+ OrganizationResponse: {
249
+ organization: components['schemas']['Organization']
250
+ membership: components['schemas']['OrganizationMembership'] & unknown
251
+ }
252
+ CreateOrgResponse: {
253
+ organization: components['schemas']['Organization']
254
+ }
255
+ OrganizationMembersResponse: {
256
+ members: components['schemas']['OrganizationMember'][]
257
+ }
258
+ OrganizationInvitationsResponse: {
259
+ invitations: components['schemas']['OrganizationInvitation'][]
260
+ }
261
+ InviteMemberResponse: {
262
+ invitation: components['schemas']['OrganizationInvitation']
263
+ }
264
+ UpdateMemberRoleResponse: {
265
+ member: components['schemas']['OrganizationMember']
266
+ }
267
+ AcceptInvitationResponse: {
268
+ organization: components['schemas']['Organization']
269
+ }
270
+ LoginResponse:
271
+ | {
272
+ /** @enum {boolean} */
273
+ requiresTwoFactor: false
274
+ accessToken: string
275
+ refreshToken: string
276
+ expiresIn: number
277
+ user: components['schemas']['User']
278
+ }
279
+ | {
280
+ /** @enum {boolean} */
281
+ requiresTwoFactor: true
282
+ /** Format: uuid */
283
+ userId: string
284
+ /** Format: email */
285
+ email: string
286
+ }
287
+ LoginRequest: {
288
+ /** Format: email */
289
+ email: string
290
+ password: string
291
+ }
292
+ RegisterResponse: {
293
+ requiresVerification: boolean
294
+ message: string
295
+ user: {
296
+ /** Format: uuid */
297
+ id: string
298
+ /** Format: email */
299
+ email: string
300
+ name: string | null
301
+ }
302
+ }
303
+ RegisterRequest: {
304
+ name: string
305
+ /** Format: email */
306
+ email: string
307
+ password: string
308
+ }
309
+ TwoFactorVerifyRequest: {
310
+ /** Format: uuid */
311
+ userId: string
312
+ code: string
313
+ }
314
+ SuccessResponse: {
315
+ success: boolean
316
+ message?: string
317
+ }
318
+ ForgotPasswordRequest: {
319
+ /** Format: email */
320
+ email: string
321
+ }
322
+ ResetPasswordRequest: {
323
+ token: string
324
+ password: string
325
+ }
326
+ VerifyEmailRequest: {
327
+ token: string
328
+ }
329
+ LogoutRequest: {
330
+ refreshToken?: string
331
+ }
332
+ MeResponse: {
333
+ /** Format: uuid */
334
+ id: string
335
+ /** Format: email */
336
+ email: string
337
+ name: string | null
338
+ /** Format: uri */
339
+ image?: string | null
340
+ emailVerified: boolean
341
+ twoFactorEnabled: boolean
342
+ }
343
+ OAuthProvidersResponse: {
344
+ providers: string[]
345
+ }
346
+ UserProfile: {
347
+ /** Format: uuid */
348
+ id: string
349
+ /** Format: email */
350
+ email: string
351
+ name: string | null
352
+ /** Format: uri */
353
+ image?: string | null
354
+ emailVerified: boolean
355
+ /** Format: date-time */
356
+ createdAt: string
357
+ }
358
+ UpdateProfileRequest: {
359
+ name?: string
360
+ /** Format: uri */
361
+ image?: string | null
362
+ }
363
+ SecuritySettings: {
364
+ twoFactorEnabled: boolean
365
+ hasPassword: boolean
366
+ emailVerified: boolean
367
+ }
368
+ SessionsResponse: components['schemas']['Session'][]
369
+ Session: {
370
+ /** Format: uuid */
371
+ id: string
372
+ isCurrent: boolean
373
+ deviceName?: string | null
374
+ browser?: string | null
375
+ os?: string | null
376
+ deviceType?: string | null
377
+ ipAddress?: string | null
378
+ country?: string | null
379
+ city?: string | null
380
+ /** Format: date-time */
381
+ lastActiveAt: string
382
+ /** Format: date-time */
383
+ createdAt: string
384
+ }
385
+ LoginHistoryEntry: {
386
+ /** Format: uuid */
387
+ id: string
388
+ ipAddress?: string | null
389
+ userAgent?: string | null
390
+ country?: string | null
391
+ city?: string | null
392
+ device?: string | null
393
+ success: boolean
394
+ /** Format: date-time */
395
+ createdAt: string
396
+ }
397
+ ConnectedAccount: {
398
+ provider: string
399
+ /** Format: date-time */
400
+ connectedAt: string
401
+ }
402
+ PlansResponse: components['schemas']['Plan'][]
403
+ Plan: {
404
+ /** Format: uuid */
405
+ id: string
406
+ slug: string
407
+ name: string
408
+ description: string | null
409
+ features: string[]
410
+ priceMonthly: number | null
411
+ priceAnnual: number | null
412
+ priceLifetime: number | null
413
+ }
414
+ Subscription: {
415
+ /** Format: uuid */
416
+ id: string
417
+ /** Format: uuid */
418
+ userId: string
419
+ /** Format: uuid */
420
+ planId: string
421
+ planSlug: string
422
+ status: string
423
+ /** @enum {string} */
424
+ interval: 'monthly' | 'annual' | 'lifetime'
425
+ /** Format: date-time */
426
+ currentPeriodStart: string | null
427
+ /** Format: date-time */
428
+ currentPeriodEnd: string | null
429
+ cancelAtPeriodEnd: boolean
430
+ /** Format: date-time */
431
+ trialEnd: string | null
432
+ } | null
433
+ CheckoutResponse: {
434
+ /** Format: uri */
435
+ checkoutUrl: string | null
436
+ sessionId: string
437
+ }
438
+ CheckoutRequest: {
439
+ /** Format: uuid */
440
+ userId: string
441
+ planSlug: string
442
+ /** @enum {string} */
443
+ interval: 'monthly' | 'annual' | 'lifetime'
444
+ /** Format: uri */
445
+ successUrl: string
446
+ /** Format: uri */
447
+ cancelUrl: string
448
+ }
449
+ PortalResponse: {
450
+ /** Format: uri */
451
+ portalUrl: string
452
+ }
453
+ PortalRequest: {
454
+ /** Format: uuid */
455
+ userId: string
456
+ /** Format: uri */
457
+ returnUrl: string
458
+ }
459
+ BalanceResponse: {
460
+ balance: {
461
+ current: number
462
+ currentFormatted: string
463
+ }
464
+ status: {
465
+ level: string
466
+ isHealthy: boolean
467
+ isLow: boolean
468
+ alertThreshold: number
469
+ }
470
+ /** @enum {string} */
471
+ billingType: 'prepaid' | 'postpaid'
472
+ trustLevel: string
473
+ spendingCap: number | null
474
+ currentMonthSpend: number
475
+ spendingCapPercent: number | null
476
+ /** Format: date-time */
477
+ gracePeriodEndsAt: string | null
478
+ isAdminOrg: boolean
479
+ }
480
+ UsageResponse: {
481
+ period: {
482
+ type: string
483
+ /** Format: date-time */
484
+ start: string
485
+ /** Format: date-time */
486
+ end: string
487
+ }
488
+ metrics: {
489
+ aiCostMicrodollars: number
490
+ storageBytesUsed: number
491
+ storageEgressBytes: number
492
+ storageUploads: number
493
+ dbStorageBytes: number
494
+ dbComputeSeconds: number
495
+ emailSentCount: number
496
+ jobInvocationCount: number
497
+ cronActiveCount: number
498
+ notificationsSentCount: number
499
+ analyticsEventCount: number
500
+ webhookDeliveryCount: number
501
+ errorEventCount: number
502
+ authMau: number
503
+ } | null
504
+ }
505
+ BatchTrackResponse: {
506
+ success: boolean
507
+ /** @description Number of new events tracked */
508
+ count: number
509
+ /** @description Number of duplicate events filtered out */
510
+ deduplicated: number
511
+ /** @description Total events in the request */
512
+ total: number
513
+ }
514
+ BatchTrackRequest: {
515
+ events: components['schemas']['TrackEventItem'][]
516
+ }
517
+ TrackEventItem: {
518
+ event: string
519
+ properties?: {
520
+ [key: string]: unknown
521
+ }
522
+ /** Format: date-time */
523
+ timestamp?: string
524
+ destinations?: ('google_ads' | 'meta' | 'tiktok')[]
525
+ skipDestinations?: boolean
526
+ conversion?: components['schemas']['ConversionData']
527
+ }
528
+ ConversionData: {
529
+ clickId?: string
530
+ value?: number
531
+ currency?: string
532
+ orderId?: string
533
+ /** Format: email */
534
+ userEmail?: string
535
+ userPhone?: string
536
+ userFirstName?: string
537
+ userLastName?: string
538
+ }
539
+ AnalyticsQueryResponse: {
540
+ data: {
541
+ eventName: string
542
+ count: number
543
+ }[]
544
+ /** @description Total count across all events */
545
+ total: number
546
+ }
547
+ AnalyticsQueryRequest: {
548
+ /**
549
+ * Format: date-time
550
+ * @description Start of date range (ISO 8601)
551
+ */
552
+ dateFrom?: string
553
+ /**
554
+ * Format: date-time
555
+ * @description End of date range (ISO 8601)
556
+ */
557
+ dateTo?: string
558
+ /** @description Filter by specific event names */
559
+ events?: string[]
560
+ /**
561
+ * @description Group by time interval
562
+ * @enum {string}
563
+ */
564
+ interval?: 'hour' | 'day' | 'week' | 'month'
565
+ /**
566
+ * @description Limit results (default 100, max 1000)
567
+ * @default 100
568
+ */
569
+ limit: number
570
+ }
571
+ JobStatusResponse: {
572
+ available: boolean
573
+ provider: string
574
+ }
575
+ ScheduleJobResponse: {
576
+ /** Format: uuid */
577
+ jobId?: string
578
+ messageId?: string
579
+ /** Format: date-time */
580
+ scheduledFor?: string | null
581
+ }
582
+ ScheduleJobRequest: {
583
+ /** Format: uri */
584
+ callbackUrl: string
585
+ payload?: {
586
+ [key: string]: unknown
587
+ }
588
+ /**
589
+ * @default POST
590
+ * @enum {string}
591
+ */
592
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE'
593
+ headers?: {
594
+ [key: string]: string
595
+ }
596
+ delay?: number
597
+ /** Format: date-time */
598
+ scheduledFor?: string
599
+ /** @default 3 */
600
+ retries: number
601
+ name?: string
602
+ type?: string
603
+ /** @default 30 */
604
+ timeout: number
605
+ }
606
+ ListJobsResponse: {
607
+ jobs: components['schemas']['Job'][]
608
+ total: number
609
+ limit: number
610
+ offset: number
611
+ }
612
+ Job: {
613
+ /** Format: uuid */
614
+ id: string
615
+ name: string | null
616
+ type: string
617
+ /** @enum {string} */
618
+ status:
619
+ | 'pending'
620
+ | 'queued'
621
+ | 'running'
622
+ | 'completed'
623
+ | 'failed'
624
+ | 'scheduled'
625
+ | 'paused'
626
+ | 'cancelled'
627
+ | 'deleted'
628
+ callbackUrl: string
629
+ payload?: {
630
+ [key: string]: unknown
631
+ }
632
+ /** Format: date-time */
633
+ scheduledFor: string | null
634
+ /** Format: date-time */
635
+ startedAt: string | null
636
+ /** Format: date-time */
637
+ completedAt: string | null
638
+ /** Format: date-time */
639
+ failedAt: string | null
640
+ retries: number
641
+ maxRetries: number
642
+ lastError: string | null
643
+ cronExpression: string | null
644
+ /** Format: date-time */
645
+ createdAt: string
646
+ }
647
+ ScheduleCronResponse: {
648
+ /** Format: uuid */
649
+ jobId?: string
650
+ scheduleId: string
651
+ cron: string
652
+ paused: boolean
653
+ }
654
+ ScheduleCronRequest: {
655
+ /** Format: uri */
656
+ callbackUrl: string
657
+ cron: string
658
+ payload?: {
659
+ [key: string]: unknown
660
+ }
661
+ /**
662
+ * @default POST
663
+ * @enum {string}
664
+ */
665
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE'
666
+ headers?: {
667
+ [key: string]: string
668
+ }
669
+ /** @default 3 */
670
+ retries: number
671
+ name: string
672
+ type?: string
673
+ /** @default false */
674
+ paused: boolean
675
+ }
676
+ ReferralCodeResponse: {
677
+ /** @description 8-character referral code */
678
+ code: string
679
+ /** @enum {string} */
680
+ status: 'pending' | 'completed' | 'expired'
681
+ }
682
+ RegenerateCodeResponse: {
683
+ /** @description New 8-character referral code */
684
+ code: string
685
+ totalReferrals: number
686
+ completedReferrals: number
687
+ pendingReferrals: number
688
+ }
689
+ ReferralStatsResponse: {
690
+ /** @description Total referrals created */
691
+ totalReferrals: number
692
+ /** @description Successfully completed referrals */
693
+ completedReferrals: number
694
+ /** @description Pending referrals */
695
+ pendingReferrals: number
696
+ }
697
+ RedeemReferralResponse: {
698
+ success: boolean
699
+ /** @description Type of reward applied */
700
+ rewardType: string
701
+ /** @description Details of reward applied to referee */
702
+ referredReward?: {
703
+ [key: string]: unknown
704
+ }
705
+ /** @description Details of reward applied to referrer */
706
+ referrerReward?: {
707
+ [key: string]: unknown
708
+ }
709
+ }
710
+ RedeemReferralRequest: {
711
+ /** @description Referral code to redeem */
712
+ code: string
713
+ defaults?: components['schemas']['ReferralRewardDefaults']
714
+ }
715
+ /** @description Optional inline defaults for reward configuration */
716
+ ReferralRewardDefaults: {
717
+ referrerReward?: components['schemas']['ReferralRewardConfig']
718
+ refereeReward?: components['schemas']['ReferralRewardConfig'] & unknown
719
+ /** @description Whether both parties get rewarded (default: true) */
720
+ doubleReward?: boolean
721
+ /** @description Minimum days before rewards are granted (anti-fraud) */
722
+ minimumDaysBeforeReward?: number
723
+ }
724
+ /** @description Reward for the person who shared the code */
725
+ ReferralRewardConfig: {
726
+ /** @enum {string} */
727
+ type: 'points' | 'premium_trial' | 'discount' | 'credit'
728
+ /** @description Points to award (for type: points) */
729
+ points?: number
730
+ /** @description Trial days to grant (for type: premium_trial) */
731
+ days?: number
732
+ /** @description Discount percentage (for type: discount) */
733
+ discountPercent?: number
734
+ /** @description Credit amount in cents (for type: credit) */
735
+ creditCents?: number
736
+ }
737
+ LeaderboardResponse: {
738
+ /** @enum {string} */
739
+ period: 'all' | 'month' | 'week'
740
+ entries: components['schemas']['LeaderboardEntry'][]
741
+ /** @description Current user rank if authenticated */
742
+ currentUserRank: number | null
743
+ }
744
+ LeaderboardEntry: {
745
+ /** @description Position in the leaderboard */
746
+ rank: number
747
+ /**
748
+ * Format: uuid
749
+ * @description User ID (only revealed if current user)
750
+ */
751
+ userId: string | null
752
+ /** @description Masked name for privacy */
753
+ displayName: string | null
754
+ /** Format: uri */
755
+ avatarUrl: string | null
756
+ completedReferrals: number
757
+ totalReferrals: number
758
+ isCurrentUser: boolean
759
+ }
760
+ PushPreferencesResponse: {
761
+ enabled: boolean
762
+ subscriptionCount: number
763
+ }
764
+ RegisterPushResponse: {
765
+ success: boolean
766
+ updated: boolean
767
+ }
768
+ RegisterPushRequest: {
769
+ subscription: {
770
+ /** Format: uri */
771
+ endpoint: string
772
+ keys: {
773
+ p256dh: string
774
+ auth: string
775
+ }
776
+ }
777
+ }
778
+ UnregisterPushRequest: {
779
+ /** Format: uri */
780
+ endpoint: string
781
+ }
782
+ MobileConfigResponse: {
783
+ ios: boolean
784
+ android: boolean
785
+ anyConfigured: boolean
786
+ }
787
+ MobilePreferencesResponse: {
788
+ enabled: boolean
789
+ devices: components['schemas']['MobileDevice'][]
790
+ }
791
+ MobileDevice: {
792
+ /** Format: uuid */
793
+ id: string
794
+ /** @enum {string} */
795
+ platform: 'ios' | 'android'
796
+ name: string | null
797
+ /** Format: date-time */
798
+ registeredAt: string
799
+ /** Format: date-time */
800
+ lastUsedAt: string | null
801
+ }
802
+ RegisterMobileDeviceResponse: {
803
+ success: boolean
804
+ updated: boolean
805
+ /** Format: uuid */
806
+ tokenId: string
807
+ }
808
+ RegisterMobileDeviceRequest: {
809
+ /** @enum {string} */
810
+ platform: 'ios' | 'android'
811
+ token: string
812
+ deviceId?: string
813
+ deviceName?: string
814
+ appVersion?: string
815
+ osVersion?: string
816
+ }
817
+ UnregisterMobileDeviceRequest: {
818
+ token: string
819
+ }
820
+ InAppMessagesResponse: components['schemas']['InAppMessage'][]
821
+ InAppMessage: {
822
+ /** Format: uuid */
823
+ id: string
824
+ /** @enum {string|null} */
825
+ type: 'info' | 'success' | 'warning' | 'error' | 'announcement' | 'system' | 'action' | null
826
+ /** @enum {string|null} */
827
+ priority: 'low' | 'normal' | 'high' | 'urgent' | null
828
+ title: string
829
+ body: string
830
+ icon: string | null
831
+ imageUrl: string | null
832
+ actionText: string | null
833
+ actionUrl: string | null
834
+ secondaryActionText: string | null
835
+ secondaryActionUrl: string | null
836
+ metadata: {
837
+ [key: string]: unknown
838
+ } | null
839
+ topic: string | null
840
+ isRead: boolean
841
+ isDismissed: boolean
842
+ /** Format: date-time */
843
+ readAt: string | null
844
+ /** Format: date-time */
845
+ createdAt: string
846
+ }
847
+ UnreadCountResponse: {
848
+ count: number
849
+ }
850
+ MarkAllReadResponse: {
851
+ success: boolean
852
+ markedCount: number
853
+ }
854
+ RecordClickRequest: {
855
+ /** @enum {string} */
856
+ action: 'primary' | 'secondary'
857
+ }
858
+ MessagePreferencesResponse: {
859
+ enabled: boolean
860
+ mutedTopics: string[]
861
+ highPriorityOnly: boolean
862
+ }
863
+ UpdateMessagePreferencesRequest: {
864
+ enabled?: boolean
865
+ mutedTopics?: string[]
866
+ highPriorityOnly?: boolean
867
+ }
868
+ StorageFile: {
869
+ /** Format: uuid */
870
+ id: string
871
+ filename: string
872
+ path: string
873
+ /** Format: uri */
874
+ url: string | null
875
+ mimeType: string
876
+ /** @description File size in bytes */
877
+ size: number
878
+ metadata: {
879
+ [key: string]: unknown
880
+ } | null
881
+ /** Format: date-time */
882
+ createdAt: string
883
+ }
884
+ UploadUrlResponse: {
885
+ /**
886
+ * Format: uri
887
+ * @description Endpoint URL for the upload
888
+ */
889
+ uploadEndpoint: string
890
+ /** @description JSON payload to include in the upload request */
891
+ clientPayload: string
892
+ /** @description Maximum file size in bytes */
893
+ maxSize: number
894
+ /** @description List of allowed MIME types */
895
+ allowedContentTypes: string[]
896
+ /** @description Instructions for making the upload request */
897
+ instructions: {
898
+ method: string
899
+ headers: {
900
+ [key: string]: string
901
+ }
902
+ body: {
903
+ type: string
904
+ payload: {
905
+ pathname: string
906
+ clientPayload: string
907
+ }
908
+ }
909
+ }
910
+ }
911
+ UploadUrlRequest: {
912
+ /** @description Name of the file to upload */
913
+ filename: string
914
+ /** @description MIME type of the file */
915
+ contentType?: string
916
+ /** @description Optional folder path within app storage */
917
+ folder?: string
918
+ /** @description Custom metadata to attach to the file */
919
+ metadata?: {
920
+ [key: string]: unknown
921
+ }
922
+ }
923
+ CaptureExceptionResponse: {
924
+ /**
925
+ * Format: uuid
926
+ * @description Unique event ID
927
+ */
928
+ eventId: string
929
+ /** @description Whether this is a new unique error or a duplicate */
930
+ isNewError: boolean
931
+ }
932
+ CaptureExceptionRequest: {
933
+ /** @description The exception to capture */
934
+ exception: {
935
+ values: components['schemas']['MonitoringExceptionValue'][]
936
+ }
937
+ level?: components['schemas']['MonitoringErrorLevel']
938
+ /** @description Tags for filtering */
939
+ tags?: {
940
+ [key: string]: string
941
+ }
942
+ /** @description Extra context data */
943
+ extra?: {
944
+ [key: string]: unknown
945
+ }
946
+ /** @description Breadcrumbs leading up to the error */
947
+ breadcrumbs?: components['schemas']['MonitoringBreadcrumb'][]
948
+ /** @description Current page/screen */
949
+ route?: string
950
+ /** @description User agent */
951
+ userAgent?: string
952
+ /** @description Custom fingerprint for grouping */
953
+ fingerprint?: string[]
954
+ /** @description Release version */
955
+ release?: string
956
+ /** @description Environment (dev/staging/prod) */
957
+ environment?: string
958
+ }
959
+ MonitoringExceptionValue: {
960
+ /** @description Exception type (e.g., "TypeError", "ReferenceError") */
961
+ type: string
962
+ /** @description Exception message */
963
+ value: string
964
+ stacktrace?: {
965
+ frames: {
966
+ filename?: string
967
+ function?: string
968
+ lineno?: number
969
+ colno?: number
970
+ in_app?: boolean
971
+ }[]
972
+ }
973
+ }
974
+ /**
975
+ * @description Error level
976
+ * @enum {string}
977
+ */
978
+ MonitoringErrorLevel: 'fatal' | 'error' | 'warning' | 'info'
979
+ MonitoringBreadcrumb: {
980
+ type: string
981
+ category: string
982
+ message: string
983
+ timestamp: string
984
+ }
985
+ CaptureMessageResponse: {
986
+ /**
987
+ * Format: uuid
988
+ * @description Unique event ID
989
+ */
990
+ eventId: string
991
+ /** @description Whether this is a new unique message or a duplicate */
992
+ isNewError: boolean
993
+ }
994
+ CaptureMessageRequest: {
995
+ /** @description Message to capture */
996
+ message: string
997
+ level?: components['schemas']['MonitoringErrorLevel'] & unknown
998
+ /** @description Tags for filtering */
999
+ tags?: {
1000
+ [key: string]: string
1001
+ }
1002
+ /** @description Extra context data */
1003
+ extra?: {
1004
+ [key: string]: unknown
1005
+ }
1006
+ /** @description Current page/screen */
1007
+ route?: string
1008
+ }
1009
+ AIUsageResponse: {
1010
+ /** @enum {string} */
1011
+ period: 'day' | 'week' | 'month'
1012
+ /** @description Total number of AI requests */
1013
+ requests: number
1014
+ tokens: {
1015
+ /** @description Total input tokens */
1016
+ input: number
1017
+ /** @description Total output tokens */
1018
+ output: number
1019
+ /** @description Total tokens (input + output) */
1020
+ total: number
1021
+ }
1022
+ cost: {
1023
+ /** @description Cost in microdollars (1/1,000,000 USD) */
1024
+ microdollars: number
1025
+ /** @description Human-readable cost string */
1026
+ formatted: string
1027
+ }
1028
+ /** @description Usage breakdown by model */
1029
+ byModel: {
1030
+ [key: string]: {
1031
+ requests: number
1032
+ tokens: number
1033
+ cost: number
1034
+ }
1035
+ }
1036
+ /** @description Usage breakdown by request type (chat, embedding, vision) */
1037
+ byType: {
1038
+ [key: string]: {
1039
+ requests: number
1040
+ tokens: number
1041
+ }
1042
+ }
1043
+ }
1044
+ AIRateLimitResponse: {
1045
+ /** @description Max requests per minute (null if unlimited) */
1046
+ requestsPerMinute: number | null
1047
+ /** @description Max requests per day (null if unlimited) */
1048
+ requestsPerDay: number | null
1049
+ /** @description Max tokens per minute (null if unlimited) */
1050
+ tokensPerMinute: number | null
1051
+ /** @description Max tokens per day (null if unlimited) */
1052
+ tokensPerDay: number | null
1053
+ /** @description Max cost per day in microdollars (null if unlimited) */
1054
+ costPerDayMicrodollars: number | null
1055
+ current: {
1056
+ /** @description Requests made in the last minute */
1057
+ requestsThisMinute: number
1058
+ /** @description Requests made today */
1059
+ requestsToday: number
1060
+ /** @description Tokens used in the last minute */
1061
+ tokensThisMinute: number
1062
+ /** @description Tokens used today */
1063
+ tokensToday: number
1064
+ /** @description Cost today in microdollars */
1065
+ costToday: number
1066
+ }
1067
+ }
1068
+ AIModelsResponse: {
1069
+ models: components['schemas']['AIModel'][]
1070
+ /** @description Total number of matching models */
1071
+ total: number
1072
+ /** @description Whether more results are available */
1073
+ hasMore: boolean
1074
+ }
1075
+ AIModel: {
1076
+ /** @description Model ID (e.g., openai/gpt-4) */
1077
+ id: string
1078
+ /** @description Human-readable model name */
1079
+ name: string
1080
+ /** @description Model description */
1081
+ description?: string | null
1082
+ /** @description Model capabilities */
1083
+ capabilities: ('chat' | 'vision' | 'tool' | 'embedding')[]
1084
+ /** @description Maximum context window size */
1085
+ contextWindow?: number
1086
+ pricing?: {
1087
+ /** @description Cost per input token in USD */
1088
+ inputPerToken?: number
1089
+ /** @description Cost per output token in USD */
1090
+ outputPerToken?: number
1091
+ }
1092
+ }
1093
+ ConsentTypesResponse: components['schemas']['ConsentType'][]
1094
+ ConsentType: {
1095
+ /** Format: uuid */
1096
+ id: string
1097
+ slug: string
1098
+ /** @enum {string} */
1099
+ category: 'necessary' | 'analytics' | 'marketing' | 'preferences' | 'functional'
1100
+ name: string
1101
+ description: string
1102
+ required: boolean
1103
+ defaultEnabled: boolean
1104
+ }
1105
+ UserConsentsResponse: components['schemas']['UserConsent'][]
1106
+ UserConsent: {
1107
+ slug: string
1108
+ /** @enum {string} */
1109
+ category: 'necessary' | 'analytics' | 'marketing' | 'preferences' | 'functional'
1110
+ name: string
1111
+ required: boolean
1112
+ granted: boolean
1113
+ /** Format: date-time */
1114
+ grantedAt: string | null
1115
+ version: number | null
1116
+ }
1117
+ SetConsentResponse: {
1118
+ consents: {
1119
+ slug: string
1120
+ granted: boolean
1121
+ }[]
1122
+ }
1123
+ SetConsentRequest: {
1124
+ /**
1125
+ * Format: uuid
1126
+ * @description User ID (for logged-in users)
1127
+ */
1128
+ userId?: string
1129
+ /** @description Anonymous ID (for non-logged-in users) */
1130
+ anonymousId?: string
1131
+ /** @description Consents to set */
1132
+ consents: {
1133
+ /** @description Consent type slug */
1134
+ slug: string
1135
+ /** @description Whether consent is granted */
1136
+ granted: boolean
1137
+ }[]
1138
+ /**
1139
+ * @description Source of consent
1140
+ * @default banner
1141
+ * @enum {string}
1142
+ */
1143
+ source: 'banner' | 'settings' | 'api'
1144
+ /** @description User agent string */
1145
+ userAgent?: string
1146
+ }
1147
+ AcceptAllConsentRequest: {
1148
+ /**
1149
+ * Format: uuid
1150
+ * @description User ID (for logged-in users)
1151
+ */
1152
+ userId?: string
1153
+ /** @description Anonymous ID (for non-logged-in users) */
1154
+ anonymousId?: string
1155
+ /**
1156
+ * @description Source of consent
1157
+ * @default banner
1158
+ * @enum {string}
1159
+ */
1160
+ source: 'banner' | 'settings' | 'api'
1161
+ /** @description User agent string */
1162
+ userAgent?: string
1163
+ }
1164
+ DeclineOptionalConsentRequest: {
1165
+ /**
1166
+ * Format: uuid
1167
+ * @description User ID (for logged-in users)
1168
+ */
1169
+ userId?: string
1170
+ /** @description Anonymous ID (for non-logged-in users) */
1171
+ anonymousId?: string
1172
+ /**
1173
+ * @description Source of consent
1174
+ * @default banner
1175
+ * @enum {string}
1176
+ */
1177
+ source: 'banner' | 'settings' | 'api'
1178
+ /** @description User agent string */
1179
+ userAgent?: string
1180
+ }
1181
+ SendEmailResponse: {
1182
+ success: boolean
1183
+ /** @description Email ID from provider */
1184
+ id?: string
1185
+ }
1186
+ SendEmailRequest: {
1187
+ /**
1188
+ * Format: email
1189
+ * @description Recipient email address
1190
+ */
1191
+ to: string
1192
+ /** @description Email subject */
1193
+ subject: string
1194
+ /** @description Email HTML content */
1195
+ html: string
1196
+ /** @description Plain text fallback */
1197
+ text?: string
1198
+ /**
1199
+ * Format: email
1200
+ * @description Reply-to email address
1201
+ */
1202
+ replyTo?: string
1203
+ }
1204
+ SendTemplatedEmailResponse: {
1205
+ success: boolean
1206
+ /** @description Template that was sent */
1207
+ template: string
1208
+ }
1209
+ SendTemplatedEmailRequest: {
1210
+ /**
1211
+ * @description Template type
1212
+ * @enum {string}
1213
+ */
1214
+ template: 'welcome' | 'verification' | 'password_reset' | 'security_alert'
1215
+ /**
1216
+ * Format: email
1217
+ * @description Recipient email address
1218
+ */
1219
+ to: string
1220
+ /** @description Template variables */
1221
+ data?: {
1222
+ [key: string]: unknown
1223
+ }
1224
+ }
1225
+ SendToUserResponse: {
1226
+ success: boolean
1227
+ /** @description Email ID from provider */
1228
+ id?: string
1229
+ }
1230
+ SendToUserRequest: {
1231
+ /**
1232
+ * Format: uuid
1233
+ * @description User ID to send email to
1234
+ */
1235
+ userId: string
1236
+ /** @description Email subject */
1237
+ subject: string
1238
+ /** @description Email HTML content */
1239
+ html: string
1240
+ /** @description Plain text fallback */
1241
+ text?: string
1242
+ }
1243
+ NewsletterSubscribeResponse: {
1244
+ success: boolean
1245
+ /** @description Human-readable result message */
1246
+ message: string
1247
+ /** @description Whether email verification is required */
1248
+ requiresVerification: boolean
1249
+ /** @description Whether email was already subscribed */
1250
+ alreadySubscribed: boolean
1251
+ }
1252
+ NewsletterSubscribeRequest: {
1253
+ /**
1254
+ * Format: email
1255
+ * @description Subscriber email address
1256
+ */
1257
+ email: string
1258
+ /**
1259
+ * @description Subscription source
1260
+ * @default website
1261
+ * @enum {string}
1262
+ */
1263
+ source: 'website' | 'waitlist' | 'referral' | 'api' | 'import'
1264
+ /** @description Subscriber name */
1265
+ name?: string
1266
+ /** @description Additional tracking metadata */
1267
+ metadata?: {
1268
+ referrer?: string
1269
+ utmSource?: string
1270
+ utmMedium?: string
1271
+ utmCampaign?: string
1272
+ referralCode?: string
1273
+ } & {
1274
+ [key: string]: unknown
1275
+ }
1276
+ /** @description Initial subscription preferences */
1277
+ preferences?: {
1278
+ newsletter?: boolean
1279
+ product_updates?: boolean
1280
+ promotions?: boolean
1281
+ changelog?: boolean
1282
+ security_alerts?: boolean
1283
+ }
1284
+ }
1285
+ NewsletterVerifyResponse: {
1286
+ success: boolean
1287
+ /** @description Human-readable result message */
1288
+ message: string
1289
+ /**
1290
+ * Format: email
1291
+ * @description Verified email address
1292
+ */
1293
+ email: string
1294
+ }
1295
+ NewsletterVerifyRequest: {
1296
+ /** @description Verification token from email */
1297
+ token: string
1298
+ }
1299
+ NewsletterUnsubscribeResponse: {
1300
+ success: boolean
1301
+ /** @description Human-readable result message */
1302
+ message: string
1303
+ /**
1304
+ * Format: email
1305
+ * @description Unsubscribed email address
1306
+ */
1307
+ email: string
1308
+ }
1309
+ NewsletterUnsubscribeRequest: {
1310
+ /**
1311
+ * Format: email
1312
+ * @description Email to unsubscribe
1313
+ */
1314
+ email: string
1315
+ /** @description Unsubscribe token from email */
1316
+ token: string
1317
+ }
1318
+ NewsletterResendVerificationResponse: {
1319
+ success: boolean
1320
+ /** @description Human-readable result message */
1321
+ message: string
1322
+ }
1323
+ NewsletterResendVerificationRequest: {
1324
+ /**
1325
+ * Format: email
1326
+ * @description Email to resend verification to
1327
+ */
1328
+ email: string
1329
+ }
1330
+ NewsletterUnsubscribeInfoResponse: {
1331
+ /**
1332
+ * Format: email
1333
+ * @description Subscriber email
1334
+ */
1335
+ email: string
1336
+ /** @description Subscriber name */
1337
+ name: string | null
1338
+ /**
1339
+ * Format: date-time
1340
+ * @description When subscription was confirmed
1341
+ */
1342
+ subscribedAt: string | null
1343
+ }
1344
+ NewsletterPreferences: {
1345
+ /** @description Newsletter preference */
1346
+ newsletter: boolean
1347
+ /** @description Product updates preference */
1348
+ product_updates: boolean
1349
+ /** @description Promotions preference */
1350
+ promotions: boolean
1351
+ /** @description Changelog preference */
1352
+ changelog: boolean
1353
+ /** @description Security alerts preference */
1354
+ security_alerts: boolean
1355
+ }
1356
+ NewsletterUpdatePreferencesResponse: {
1357
+ success: boolean
1358
+ preferences: components['schemas']['NewsletterPreferences'] & unknown
1359
+ }
1360
+ NewsletterUpdatePreferencesRequest: {
1361
+ /**
1362
+ * Format: email
1363
+ * @description Subscriber email
1364
+ */
1365
+ email: string
1366
+ /** @description Preferences to update */
1367
+ preferences: {
1368
+ newsletter?: boolean
1369
+ product_updates?: boolean
1370
+ promotions?: boolean
1371
+ changelog?: boolean
1372
+ security_alerts?: boolean
1373
+ }
1374
+ }
1375
+ WebhookConfigResponse: {
1376
+ environments: components['schemas']['WebhookEnvironmentConfig'][]
1377
+ supportedEvents: string[]
1378
+ }
1379
+ WebhookEnvironmentConfig: {
1380
+ /** Format: uuid */
1381
+ id: string
1382
+ name: string
1383
+ /** Format: uri */
1384
+ webhookUrl: string | null
1385
+ hasSecret: boolean
1386
+ /** Format: date-time */
1387
+ updatedAt: string | null
1388
+ }
1389
+ UpdateWebhookConfigResponse: {
1390
+ success: boolean
1391
+ /** Format: uri */
1392
+ webhookUrl: string | null
1393
+ secretGenerated: boolean
1394
+ /** @description Only returned on initial setup or regeneration */
1395
+ webhookSecret?: string
1396
+ }
1397
+ UpdateWebhookConfigRequest: {
1398
+ /** Format: uuid */
1399
+ environmentId: string
1400
+ /** Format: uri */
1401
+ webhookUrl: string | null
1402
+ /** @default false */
1403
+ regenerateSecret: boolean
1404
+ }
1405
+ ListWebhookDeliveriesResponse: {
1406
+ deliveries: components['schemas']['WebhookDelivery'][]
1407
+ total: number
1408
+ limit: number
1409
+ offset: number
1410
+ }
1411
+ WebhookDelivery: {
1412
+ /** Format: uuid */
1413
+ id: string
1414
+ event: string
1415
+ url: string
1416
+ /** @enum {string} */
1417
+ status: 'pending' | 'queued' | 'delivered' | 'failed'
1418
+ retryCount: number
1419
+ responseStatus: number | null
1420
+ error: string | null
1421
+ /** Format: date-time */
1422
+ createdAt: string
1423
+ /** Format: date-time */
1424
+ queuedAt: string | null
1425
+ /** Format: date-time */
1426
+ deliveredAt: string | null
1427
+ /** Format: date-time */
1428
+ failedAt: string | null
1429
+ }
1430
+ ReplayDeliveryResponse: {
1431
+ success: boolean
1432
+ /** Format: uuid */
1433
+ newDeliveryId?: string
1434
+ messageId?: string
1435
+ }
1436
+ WebhookStatsResponse: {
1437
+ /** @enum {string} */
1438
+ period: 'day' | 'week' | 'month'
1439
+ totals: {
1440
+ total: number
1441
+ delivered: number
1442
+ failed: number
1443
+ pending: number
1444
+ /** @description Percentage formatted as string (e.g., "95.5%") */
1445
+ deliveryRate: string
1446
+ }
1447
+ byEvent: {
1448
+ event: string
1449
+ count: number
1450
+ }[]
1451
+ byStatus: {
1452
+ /** @enum {string} */
1453
+ status: 'pending' | 'queued' | 'delivered' | 'failed'
1454
+ count: number
1455
+ }[]
1456
+ }
1457
+ TwoFactorSetupResponse: {
1458
+ /** @description TOTP secret key */
1459
+ secret: string
1460
+ /** @description TOTP URI for QR code generation */
1461
+ uri: string
1462
+ /** @description Base64 encoded QR code image */
1463
+ qrCode?: string
1464
+ }
1465
+ Security2FAVerifyResponse: {
1466
+ success: boolean
1467
+ /** @description One-time backup codes */
1468
+ backupCodes: string[]
1469
+ }
1470
+ Security2FAVerifyRequest: {
1471
+ /** @description 6-digit TOTP code */
1472
+ code: string
1473
+ }
1474
+ Security2FADisableResponse: {
1475
+ success: boolean
1476
+ }
1477
+ BackupCodesResponse: {
1478
+ /** @description Remaining unused backup codes */
1479
+ codes: string[]
1480
+ /** @description Number of remaining codes */
1481
+ count: number
1482
+ }
1483
+ BackupCodesRegenerateResponse: {
1484
+ /** @description New backup codes */
1485
+ codes: string[]
1486
+ }
1487
+ SetPasswordRequest: {
1488
+ /** @description New password */
1489
+ password: string
1490
+ }
1491
+ EmailChangeResponse: {
1492
+ success: boolean
1493
+ message: string
1494
+ /**
1495
+ * Format: date-time
1496
+ * @description When the verification link expires
1497
+ */
1498
+ expiresAt: string
1499
+ }
1500
+ EmailChangeRequest: {
1501
+ /**
1502
+ * Format: email
1503
+ * @description New email address
1504
+ */
1505
+ newEmail: string
1506
+ }
1507
+ EmailChangeConfirmResponse: {
1508
+ success: boolean
1509
+ /**
1510
+ * Format: email
1511
+ * @description The new email address
1512
+ */
1513
+ newEmail: string
1514
+ }
1515
+ EmailChangeConfirmRequest: {
1516
+ /** @description Verification token from email */
1517
+ token: string
1518
+ }
1519
+ PasskeysListResponse: {
1520
+ passkeys: components['schemas']['Passkey'][]
1521
+ }
1522
+ Passkey: {
1523
+ /** Format: uuid */
1524
+ id: string
1525
+ name: string | null
1526
+ /** Format: date-time */
1527
+ createdAt: string
1528
+ /** Format: date-time */
1529
+ lastUsedAt: string | null
1530
+ }
1531
+ PasskeyRegistrationOptions: {
1532
+ challenge: string
1533
+ rp: {
1534
+ name: string
1535
+ id: string
1536
+ }
1537
+ user: {
1538
+ id: string
1539
+ name: string
1540
+ displayName: string
1541
+ }
1542
+ pubKeyCredParams: {
1543
+ /** @enum {string} */
1544
+ type: 'public-key'
1545
+ alg: number
1546
+ }[]
1547
+ timeout?: number
1548
+ attestation?: string
1549
+ excludeCredentials?: {
1550
+ /** @enum {string} */
1551
+ type: 'public-key'
1552
+ id: string
1553
+ }[]
1554
+ authenticatorSelection?: {
1555
+ authenticatorAttachment?: string
1556
+ requireResidentKey?: boolean
1557
+ residentKey?: string
1558
+ userVerification?: string
1559
+ }
1560
+ } & {
1561
+ [key: string]: unknown
1562
+ }
1563
+ PasskeyRegisterVerifyResponse: {
1564
+ /** Format: uuid */
1565
+ id: string
1566
+ name: string | null
1567
+ /** Format: date-time */
1568
+ createdAt: string
1569
+ }
1570
+ PasskeyRegisterVerifyRequest: {
1571
+ /** @description WebAuthn RegistrationResponseJSON */
1572
+ credential?: unknown
1573
+ /** @description Optional device name */
1574
+ deviceName?: string
1575
+ }
1576
+ PasskeyRenameRequest: {
1577
+ name: string
1578
+ }
1579
+ OAuthDisconnectResponse: {
1580
+ success: boolean
1581
+ }
1582
+ OAuthDisconnectRequest: {
1583
+ /** @description OAuth provider name (e.g., google, github) */
1584
+ provider: string
1585
+ }
1586
+ SecurityScore: {
1587
+ /** @description Overall security score */
1588
+ score: number
1589
+ /**
1590
+ * @description Security level
1591
+ * @enum {string}
1592
+ */
1593
+ level: 'critical' | 'low' | 'medium' | 'high' | 'excellent'
1594
+ /** @description Individual security factors */
1595
+ factors: {
1596
+ name: string
1597
+ enabled: boolean
1598
+ weight: number
1599
+ description: string
1600
+ }[]
1601
+ /** @description Recommended security improvements */
1602
+ recommendations: {
1603
+ /** @enum {string} */
1604
+ priority: 'high' | 'medium' | 'low'
1605
+ title: string
1606
+ description: string
1607
+ action?: string
1608
+ }[]
1609
+ }
1610
+ ChallengeVerifyResponse: {
1611
+ verified: boolean
1612
+ /** @enum {string} */
1613
+ method: 'password' | 'email' | 'totp' | 'backup'
1614
+ /**
1615
+ * @description Type of verification completed
1616
+ * @enum {string}
1617
+ */
1618
+ type: 'identity' | 'mfa'
1619
+ }
1620
+ ChallengeVerifyRequest: {
1621
+ /**
1622
+ * @description Verification method
1623
+ * @enum {string}
1624
+ */
1625
+ method: 'password' | 'email' | 'totp' | 'backup'
1626
+ /** @description Verification value (password, code, etc.) */
1627
+ value: string
1628
+ }
1629
+ }
1630
+ responses: never
1631
+ parameters: never
1632
+ requestBodies: never
1633
+ headers: never
1634
+ pathItems: never
1635
+ }
1636
+
1637
+ /**
1638
+ * Auth Functions
1639
+ *
1640
+ * Pure functions for authentication - no hidden state.
1641
+ * Each function takes config as the first parameter.
1642
+ *
1643
+ * Uses REST API at /api/sdk/auth/* for all operations.
1644
+ *
1645
+ * Types are derived from the OpenAPI spec (generated/api.d.ts).
1646
+ * Run `bun run generate:types:local` to regenerate after API changes.
1647
+ */
1648
+
1649
+ type TokenResponse = components['schemas']['TokenResponse'];
1650
+
1651
+ /**
1652
+ * Platform SDK Types
1653
+ *
1654
+ * Type definitions for the SDK. API types are generated from OpenAPI spec (ADR-007).
1655
+ * SDK-specific types for React layer and internal use are defined here.
1656
+ *
1657
+ * ## Type Sources (ADR-007)
1658
+ *
1659
+ * | Source | Example | When to Use |
1660
+ * |--------|---------|-------------|
1661
+ * | `generated/api.d.ts` | `LoginRequest`, `Plan` | All API request/response types |
1662
+ * | SDK modules | `SessionResult`, `FlagContext` | SDK-specific types |
1663
+ * | `types.ts` | `AppConfig`, `AIMessage` | Shared/React layer types |
1664
+ *
1665
+ * ## Naming Conventions (Generated Types)
1666
+ *
1667
+ * | Suffix | Purpose | Example |
1668
+ * |--------|---------|---------|
1669
+ * | `Request` | API request payloads | `LoginRequest`, `CheckoutRequest` |
1670
+ * | `Response` | API response payloads | `LoginResponse`, `TokenResponse` |
1671
+ * | (none) | Domain objects | `Plan`, `Subscription`, `User` |
1672
+ *
1673
+ * ## Naming Conventions (SDK Types)
1674
+ *
1675
+ * | Suffix | Purpose | Example |
1676
+ * |--------|---------|---------|
1677
+ * | `Result` | SDK function return (non-API) | `SessionResult` |
1678
+ * | `Options` | Optional function parameters | `FileUploadOptions`, `RevokeTokenOptions` |
1679
+ * | `Config` | Runtime configuration | `SylphxConfig`, `AppConfig` |
1680
+ *
1681
+ * ## Sdk* Prefix (services-context.ts)
1682
+ *
1683
+ * Types prefixed with `Sdk` (e.g., `SdkUserProfile`) are SDK internal representations
1684
+ * with Date objects instead of ISO strings. API types use strings, SDK transforms to Date.
1685
+ */
1686
+
1687
+ /**
1688
+ * Base user type for SDK operations.
1689
+ *
1690
+ * Used in:
1691
+ * - Token responses (login, refresh)
1692
+ * - Cookie data for client hydration
1693
+ * - Basic user state in React context
1694
+ *
1695
+ * For authenticated user with security fields (twoFactorEnabled),
1696
+ * see AuthUser in services-context.ts.
1697
+ */
1698
+ /**
1699
+ * User type - compatible with generated User from API
1700
+ *
1701
+ * Note: `image` is optional to match generated type.
1702
+ * Extra fields (role, createdAt) are SDK extensions for internal use.
1703
+ */
1704
+ interface User {
1705
+ id: string;
1706
+ email: string;
1707
+ name: string | null;
1708
+ image?: string | null;
1709
+ emailVerified: boolean;
1710
+ role?: string;
1711
+ createdAt?: string;
1712
+ }
1713
+ /**
1714
+ * User cookie data (JS-readable for client hydration)
1715
+ *
1716
+ * Single Source of Truth for the user cookie shape.
1717
+ * Used by both server-side (nextjs/cookies.ts) and client-side (react/storage-utils.ts).
1718
+ *
1719
+ * Note: This contains NO sensitive data.
1720
+ * Tokens are stored separately in HttpOnly cookies.
1721
+ */
1722
+ interface UserCookieData {
1723
+ user: User;
1724
+ /** Timestamp when session expires (for client-side expiry check) */
1725
+ expiresAt: number;
1726
+ }
1727
+
1728
+ /**
1729
+ * Server-side Auth Helpers for Next.js
1730
+ *
1731
+ * Use in Server Components and API Routes.
1732
+ *
1733
+ * Architecture: Cookie-Centric Single Source of Truth
1734
+ * ===================================================
1735
+ *
1736
+ * All auth state lives in cookies. The auth() function reads
1737
+ * from HttpOnly cookies and refreshes if needed.
1738
+ *
1739
+ * Cookie Structure:
1740
+ * - __sylphx_{namespace}_session — HttpOnly JWT (5 min)
1741
+ * - __sylphx_{namespace}_refresh — HttpOnly refresh token (30 days)
1742
+ * - __sylphx_{namespace}_user — JS-readable user data (5 min)
1743
+ */
1744
+
1745
+ /**
1746
+ * Configure the SDK for server-side usage
1747
+ *
1748
+ * NOTE: This is optional! The SDK auto-configures from environment variables:
1749
+ * - SYLPHX_SECRET_KEY (required)
1750
+ * - SYLPHX_PLATFORM_URL (optional, defaults to https://sylphx.com)
1751
+ *
1752
+ * Use this only if you need to override the default configuration.
1753
+ *
1754
+ * @example
1755
+ * ```ts
1756
+ * // Optional: Override default configuration
1757
+ * import { configureServer } from '@sylphx/sdk/nextjs'
1758
+ *
1759
+ * configureServer({
1760
+ * secretKey: process.env.SYLPHX_SECRET_KEY!,
1761
+ * platformUrl: 'https://custom.sylphx.com',
1762
+ * })
1763
+ * ```
1764
+ */
1765
+ declare function configureServer(config: {
1766
+ secretKey: string;
1767
+ platformUrl?: string;
1768
+ }): void;
1769
+ /**
1770
+ * Auth state returned by auth()
1771
+ */
1772
+ interface AuthResult {
1773
+ userId: string | null;
1774
+ user: User | null;
1775
+ /** Session token (for internal use only - not exposed to client) */
1776
+ sessionToken: string | null;
1777
+ }
1778
+ /**
1779
+ * Get the current auth state (memoized per request)
1780
+ *
1781
+ * This is the primary way to check authentication in Server Components.
1782
+ * It reads from HttpOnly cookies and refreshes if needed.
1783
+ *
1784
+ * @example
1785
+ * ```ts
1786
+ * // In a Server Component
1787
+ * import { auth } from '@sylphx/platform-sdk/nextjs'
1788
+ *
1789
+ * export default async function Dashboard() {
1790
+ * const { userId, user } = await auth()
1791
+ *
1792
+ * if (!userId) {
1793
+ * redirect('/login')
1794
+ * }
1795
+ *
1796
+ * return <div>Hello, {user?.name}</div>
1797
+ * }
1798
+ * ```
1799
+ */
1800
+ declare const auth: () => Promise<AuthResult>;
1801
+ /**
1802
+ * Get the current user (null if not logged in)
1803
+ *
1804
+ * @example
1805
+ * ```ts
1806
+ * import { currentUser } from '@sylphx/platform-sdk/nextjs'
1807
+ *
1808
+ * export default async function Header() {
1809
+ * const user = await currentUser()
1810
+ * if (!user) return <SignIn />
1811
+ * return <span>Hello, {user.name}</span>
1812
+ * }
1813
+ * ```
1814
+ */
1815
+ declare function currentUser(): Promise<User | null>;
1816
+ /**
1817
+ * Get the current user ID (null if not logged in)
1818
+ */
1819
+ declare function currentUserId(): Promise<string | null>;
1820
+ /**
1821
+ * Handle OAuth callback - exchange code for tokens and set cookies
1822
+ *
1823
+ * NOTE: With createSylphxMiddleware(), this is handled automatically.
1824
+ * You don't need to create manual /api/auth/* routes.
1825
+ *
1826
+ * This function is exported for advanced use cases where you need
1827
+ * custom callback handling outside of the middleware flow.
1828
+ *
1829
+ * @example
1830
+ * ```ts
1831
+ * // Using middleware (recommended - zero manual routes)
1832
+ * import { createSylphxMiddleware } from '@sylphx/sdk/nextjs'
1833
+ * export default createSylphxMiddleware({ publicRoutes: ['/', '/about'] })
1834
+ *
1835
+ * // Middleware automatically handles /auth/callback
1836
+ * ```
1837
+ */
1838
+ declare function handleCallback(code: string): Promise<User>;
1839
+ /**
1840
+ * Sign out - clear cookies and optionally revoke token
1841
+ *
1842
+ * NOTE: With createSylphxMiddleware(), signout is handled automatically
1843
+ * at /auth/signout. No manual route needed.
1844
+ *
1845
+ * This function is exported for advanced use cases.
1846
+ *
1847
+ * @example
1848
+ * ```ts
1849
+ * // Using middleware (recommended)
1850
+ * // Navigate users to /auth/signout - middleware handles everything
1851
+ * <a href="/auth/signout">Sign Out</a>
1852
+ * ```
1853
+ */
1854
+ declare function signOut(): Promise<void>;
1855
+ /**
1856
+ * Sync tokens to cookies (server action)
1857
+ *
1858
+ * NOTE: With createSylphxMiddleware(), OAuth callbacks are handled
1859
+ * automatically at /auth/callback. This function is rarely needed.
1860
+ *
1861
+ * Use only for edge cases like custom OAuth providers not going
1862
+ * through the standard flow.
1863
+ */
1864
+ declare function syncAuthToCookies(tokens: TokenResponse): Promise<void>;
1865
+ /**
1866
+ * Get authorization URL for OAuth redirect
1867
+ */
1868
+ declare function getAuthorizationUrl(options?: {
1869
+ redirectUri?: string;
1870
+ mode?: 'login' | 'signup';
1871
+ state?: string;
1872
+ appId?: string;
1873
+ }): string;
1874
+ /**
1875
+ * Get the current session token for API calls
1876
+ *
1877
+ * This is for apps that need to call third-party APIs with the session token.
1878
+ * For same-origin API calls, cookies are sent automatically.
1879
+ *
1880
+ * @example
1881
+ * ```ts
1882
+ * // In an API route that needs to call external APIs
1883
+ * import { getSessionToken } from '@sylphx/platform-sdk/nextjs'
1884
+ *
1885
+ * export async function GET() {
1886
+ * const token = await getSessionToken()
1887
+ * if (!token) {
1888
+ * return new Response('Unauthorized', { status: 401 })
1889
+ * }
1890
+ *
1891
+ * // Call third-party API
1892
+ * const response = await fetch('https://api.example.com/data', {
1893
+ * headers: { Authorization: `Bearer ${token}` }
1894
+ * })
1895
+ * // ...
1896
+ * }
1897
+ * ```
1898
+ */
1899
+ declare function getSessionToken(): Promise<string | null>;
1900
+
1901
+ /**
1902
+ * Token expiry buffer in milliseconds (30 seconds)
1903
+ *
1904
+ * Refresh tokens this many milliseconds BEFORE they expire
1905
+ * to account for network latency and clock skew.
1906
+ */
1907
+ declare const TOKEN_EXPIRY_BUFFER_MS = 30000;
1908
+ /** Session token lifetime in milliseconds (for React Query staleTime) */
1909
+ declare const SESSION_TOKEN_LIFETIME_MS: number;
1910
+
1911
+ /**
1912
+ * Cookie Management for Next.js — Single Source of Truth
1913
+ *
1914
+ * Architecture: Cookie-Centric Auth (Clerk Pattern)
1915
+ * ================================================
1916
+ *
1917
+ * ALL auth state lives in cookies. Zero localStorage for auth.
1918
+ *
1919
+ * Cookie Structure:
1920
+ * - __sylphx_{namespace}_session — HttpOnly JWT, 5 min (access token)
1921
+ * - __sylphx_{namespace}_refresh — HttpOnly, 30 days (refresh token)
1922
+ * - __sylphx_{namespace}_user — JS-readable, 5 min (user data for client hydration)
1923
+ *
1924
+ * Benefits:
1925
+ * 1. Single Source of Truth — no server/client state divergence
1926
+ * 2. XSS-safe — tokens never accessible to JavaScript
1927
+ * 3. Cross-tab sync — cookies shared across tabs automatically
1928
+ * 4. SSR works — auth() in Server Components reads cookies directly
1929
+ *
1930
+ * Security:
1931
+ * - Short token lifetime (5 min) like Clerk
1932
+ * - Server-side refresh in middleware
1933
+ * - SameSite=Lax for CSRF protection
1934
+ */
1935
+
1936
+ /**
1937
+ * Get cookie names for a given namespace
1938
+ *
1939
+ * Namespace is derived from the secret key environment (dev/stg/prod).
1940
+ * This prevents cookies from different environments colliding.
1941
+ *
1942
+ * @example
1943
+ * getCookieNames('sylphx_prod')
1944
+ * // Returns:
1945
+ * // {
1946
+ * // SESSION: '__sylphx_prod_session',
1947
+ * // REFRESH: '__sylphx_prod_refresh',
1948
+ * // USER: '__sylphx_prod_user',
1949
+ * // }
1950
+ */
1951
+ declare function getCookieNames(namespace: string): {
1952
+ /** HttpOnly JWT access token (5 min) */
1953
+ SESSION: string;
1954
+ /** HttpOnly refresh token (30 days) */
1955
+ REFRESH: string;
1956
+ /** JS-readable user data for client hydration (5 min) */
1957
+ USER: string;
1958
+ };
1959
+ /**
1960
+ * Session token lifetime (5 minutes like Clerk)
1961
+ */
1962
+ declare const SESSION_TOKEN_LIFETIME: number;
1963
+ /**
1964
+ * Refresh token lifetime (30 days)
1965
+ */
1966
+ declare const REFRESH_TOKEN_LIFETIME: number;
1967
+ /**
1968
+ * Cookie options for HttpOnly tokens (session, refresh)
1969
+ *
1970
+ * Security features:
1971
+ * - httpOnly: true — Not accessible via JavaScript (XSS protection)
1972
+ * - secure: true in production — Only sent over HTTPS
1973
+ * - sameSite: 'lax' — CSRF protection while allowing navigation
1974
+ */
1975
+ declare const SECURE_COOKIE_OPTIONS: {
1976
+ httpOnly: boolean;
1977
+ secure: boolean;
1978
+ sameSite: "lax";
1979
+ path: string;
1980
+ };
1981
+ /**
1982
+ * Cookie options for JS-readable user cookie
1983
+ *
1984
+ * This cookie contains only user info (no tokens) and enables:
1985
+ * - Client-side hydration without loading states
1986
+ * - Cross-tab sync via cookie visibility
1987
+ */
1988
+ declare const USER_COOKIE_OPTIONS: {
1989
+ httpOnly: boolean;
1990
+ secure: boolean;
1991
+ sameSite: "lax";
1992
+ path: string;
1993
+ };
1994
+ /**
1995
+ * Auth cookies data returned by getAuthCookies
1996
+ */
1997
+ interface AuthCookiesData {
1998
+ /** Access token from SESSION cookie (HttpOnly) */
1999
+ sessionToken: string | null;
2000
+ /** Refresh token from REFRESH cookie (HttpOnly) */
2001
+ refreshToken: string | null;
2002
+ /** User data from USER cookie (JS-readable) */
2003
+ user: User | null;
2004
+ /** Expiry timestamp from USER cookie */
2005
+ expiresAt: number | null;
2006
+ }
2007
+ /**
2008
+ * Get auth cookies from the request
2009
+ *
2010
+ * Used by auth() to read current auth state.
2011
+ */
2012
+ declare function getAuthCookies(namespace: string): Promise<AuthCookiesData>;
2013
+ /**
2014
+ * Set auth cookies from token response
2015
+ *
2016
+ * Sets all three cookies:
2017
+ * - SESSION: HttpOnly access token (5 min)
2018
+ * - REFRESH: HttpOnly refresh token (30 days)
2019
+ * - USER: JS-readable user data (5 min)
2020
+ *
2021
+ * @param namespace - Cookie namespace (e.g., 'sylphx_prod')
2022
+ * @param response - Token response from auth endpoint
2023
+ * @param options - Optional: custom expiresIn override
2024
+ */
2025
+ declare function setAuthCookies(namespace: string, response: TokenResponse, options?: {
2026
+ sessionLifetime?: number;
2027
+ }): Promise<void>;
2028
+ /**
2029
+ * Clear all auth cookies
2030
+ *
2031
+ * Call on sign out to remove all auth state.
2032
+ */
2033
+ declare function clearAuthCookies(namespace: string): Promise<void>;
2034
+ /**
2035
+ * Check if session is expired
2036
+ *
2037
+ * Uses a 30 second buffer to account for network latency.
2038
+ */
2039
+ declare function isSessionExpired(namespace: string): Promise<boolean>;
2040
+ /**
2041
+ * Check if we have a refresh token (can potentially refresh)
2042
+ */
2043
+ declare function hasRefreshToken(namespace: string): Promise<boolean>;
2044
+
2045
+ /**
2046
+ * Set auth cookies on a NextResponse (for middleware use)
2047
+ *
2048
+ * Unlike setAuthCookies() which uses next/headers, this works with NextResponse.
2049
+ * Use this in middleware where you need to modify cookies on the response.
2050
+ */
2051
+ declare function setAuthCookiesMiddleware(response: NextResponse, namespace: string, tokens: TokenResponse): void;
2052
+ /**
2053
+ * Clear auth cookies on a NextResponse (for middleware use)
2054
+ */
2055
+ declare function clearAuthCookiesMiddleware(response: NextResponse, namespace: string): void;
2056
+ /**
2057
+ * Parse user cookie value (for client-side use)
2058
+ */
2059
+ declare function parseUserCookie(value: string): UserCookieData | null;
2060
+
2061
+ export { type AuthCookiesData, type AuthResult, REFRESH_TOKEN_LIFETIME, SECURE_COOKIE_OPTIONS, SESSION_TOKEN_LIFETIME, SESSION_TOKEN_LIFETIME_MS, type SylphxMiddlewareConfig, TOKEN_EXPIRY_BUFFER_MS, USER_COOKIE_OPTIONS, type UserCookieData, auth, clearAuthCookies, clearAuthCookiesMiddleware, configureServer, createMatcher, createSylphxMiddleware, currentUser, currentUserId, getAuthCookies, getAuthorizationUrl, getCookieNames, getNamespace, getSessionToken, handleCallback, hasRefreshToken, isSessionExpired, parseUserCookie, setAuthCookies, setAuthCookiesMiddleware, signOut, syncAuthToCookies };