perspectapi-ts-sdk 1.1.1 → 1.3.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.
package/README.md CHANGED
@@ -12,6 +12,7 @@ A comprehensive TypeScript SDK for PerspectAPI, designed to work seamlessly with
12
12
  - 🔑 **Multiple Auth Methods** - Support for JWT tokens and API keys
13
13
  - 📊 **Comprehensive Coverage** - All PerspectAPI endpoints supported
14
14
  - 🧩 **High-Level Loaders** - Drop-in helpers for products, content, and checkout flows with fallbacks
15
+ - 📧 **Newsletter Management** - Complete newsletter subscription system with double opt-in, preferences, and lists
15
16
 
16
17
  ## Installation
17
18
 
@@ -376,6 +377,132 @@ const submissions = await client.contact.getContactSubmissions(siteName, {
376
377
  await client.contact.updateContactStatus(siteName, submission.data.id, 'read');
377
378
  ```
378
379
 
380
+ ### Newsletter Subscriptions
381
+
382
+ ```typescript
383
+ const siteName = 'your-site-name';
384
+
385
+ // Subscribe to newsletter (with double opt-in)
386
+ const subscription = await client.newsletter.subscribe(siteName, {
387
+ email: 'subscriber@example.com',
388
+ name: 'Jane Doe',
389
+ list_ids: ['list_default'], // Optional: subscribe to specific lists
390
+ frequency: 'weekly', // instant, daily, weekly, monthly
391
+ topics: ['news', 'updates'], // Optional: topic preferences
392
+ double_opt_in: true, // Default: true for GDPR compliance
393
+ turnstile_token: 'token' // Optional: Turnstile verification
394
+ });
395
+
396
+ // Confirm subscription via email token
397
+ const confirmed = await client.newsletter.confirmSubscription(
398
+ siteName,
399
+ 'confirmation-token-from-email'
400
+ );
401
+
402
+ // Unsubscribe
403
+ const unsubscribed = await client.newsletter.unsubscribe(siteName, {
404
+ email: 'subscriber@example.com',
405
+ reason: 'Too many emails' // Optional feedback
406
+ });
407
+
408
+ // One-click unsubscribe (from email link)
409
+ await client.newsletter.unsubscribeByToken(siteName, 'unsubscribe-token');
410
+
411
+ // Update preferences
412
+ await client.newsletter.updatePreferences(siteName, 'subscriber@example.com', {
413
+ frequency: 'monthly',
414
+ topics: ['product-updates'],
415
+ email_format: 'html', // html, text, or both
416
+ timezone: 'America/New_York',
417
+ track_opens: false,
418
+ track_clicks: false
419
+ });
420
+
421
+ // Check subscription status
422
+ const status = await client.newsletter.getStatus(siteName, 'subscriber@example.com');
423
+ console.log('Subscribed:', status.data.subscribed);
424
+ console.log('Status:', status.data.status); // pending, confirmed, unsubscribed
425
+
426
+ // Get available newsletter lists
427
+ const lists = await client.newsletter.getLists(siteName);
428
+ console.log('Available lists:', lists.data.lists);
429
+ ```
430
+
431
+ #### Newsletter Admin Functions
432
+
433
+ ```typescript
434
+ // Get all subscriptions (admin only)
435
+ const subscriptions = await client.newsletter.getSubscriptions(siteName, {
436
+ page: 1,
437
+ limit: 100,
438
+ status: 'confirmed',
439
+ list_id: 'list_weekly',
440
+ search: 'john',
441
+ startDate: '2024-01-01',
442
+ endDate: '2024-12-31'
443
+ });
444
+
445
+ // Update subscription status
446
+ await client.newsletter.updateSubscriptionStatus(
447
+ siteName,
448
+ 'sub_123',
449
+ 'unsubscribed',
450
+ 'Admin action: User requested via support'
451
+ );
452
+
453
+ // Bulk operations
454
+ await client.newsletter.bulkUpdateSubscriptions(siteName, {
455
+ ids: ['sub_123', 'sub_456'],
456
+ action: 'add_to_list',
457
+ list_id: 'list_special_offers'
458
+ });
459
+
460
+ // List management
461
+ const newList = await client.newsletter.createList(siteName, {
462
+ list_name: 'VIP Customers',
463
+ slug: 'vip-customers',
464
+ description: 'Exclusive updates for VIP customers',
465
+ is_public: false,
466
+ is_default: false,
467
+ double_opt_in: true,
468
+ welcome_email_enabled: true
469
+ });
470
+
471
+ await client.newsletter.updateList(siteName, 'list_123', {
472
+ description: 'Updated description',
473
+ is_public: true
474
+ });
475
+
476
+ // Get statistics
477
+ const stats = await client.newsletter.getStatistics(siteName, {
478
+ startDate: '2024-01-01',
479
+ endDate: '2024-12-31',
480
+ list_id: 'list_weekly'
481
+ });
482
+ console.log('Total subscribers:', stats.data.totalSubscribers);
483
+ console.log('Open rate:', stats.data.engagementMetrics.averageOpenRate);
484
+
485
+ // Export subscriptions
486
+ const exportData = await client.newsletter.exportSubscriptions(siteName, {
487
+ format: 'csv', // csv, json, or xlsx
488
+ status: 'confirmed',
489
+ list_id: 'list_weekly'
490
+ });
491
+ console.log('Download URL:', exportData.data.downloadUrl);
492
+
493
+ // Import subscriptions
494
+ const importResult = await client.newsletter.importSubscriptions(siteName, {
495
+ subscriptions: [
496
+ { email: 'user1@example.com', name: 'User One', lists: ['list_weekly'] },
497
+ { email: 'user2@example.com', name: 'User Two', lists: ['list_daily'] }
498
+ ],
499
+ skip_confirmation: false, // Skip double opt-in for imported users
500
+ update_existing: true // Update if email already exists
501
+ });
502
+ console.log('Imported:', importResult.data.imported);
503
+ console.log('Failed:', importResult.data.failed);
504
+ ```
505
+
379
506
  ## Configuration Options
380
507
 
381
508
  ```typescript
package/dist/index.d.mts CHANGED
@@ -98,6 +98,74 @@ interface CreateSiteRequest {
98
98
  description?: string;
99
99
  organizationId: number;
100
100
  }
101
+ interface NewsletterSubscription {
102
+ id: string;
103
+ email: string;
104
+ name?: string;
105
+ status: 'pending' | 'confirmed' | 'unsubscribed' | 'bounced' | 'complained';
106
+ frequency: 'instant' | 'daily' | 'weekly' | 'monthly';
107
+ topics?: string[];
108
+ language: string;
109
+ confirmedAt?: string;
110
+ unsubscribedAt?: string;
111
+ createdAt: string;
112
+ updatedAt: string;
113
+ }
114
+ interface CreateNewsletterSubscriptionRequest {
115
+ email: string;
116
+ name?: string;
117
+ list_ids?: string[];
118
+ frequency?: 'instant' | 'daily' | 'weekly' | 'monthly';
119
+ topics?: string[];
120
+ language?: string;
121
+ source?: string;
122
+ source_url?: string;
123
+ double_opt_in?: boolean;
124
+ turnstile_token?: string;
125
+ metadata?: Record<string, any>;
126
+ }
127
+ interface NewsletterList {
128
+ id: string;
129
+ list_name: string;
130
+ slug: string;
131
+ description?: string;
132
+ is_default: boolean;
133
+ subscriber_count?: number;
134
+ }
135
+ interface NewsletterPreferences {
136
+ frequency?: 'instant' | 'daily' | 'weekly' | 'monthly';
137
+ topics?: string[];
138
+ language?: string;
139
+ email_format?: 'html' | 'text' | 'both';
140
+ timezone?: string;
141
+ track_opens?: boolean;
142
+ track_clicks?: boolean;
143
+ }
144
+ interface NewsletterStatusResponse {
145
+ subscribed: boolean;
146
+ status: string;
147
+ frequency?: string;
148
+ created_at?: string;
149
+ confirmed_at?: string;
150
+ }
151
+ interface NewsletterSubscribeResponse {
152
+ message: string;
153
+ status: string;
154
+ subscription_id?: string;
155
+ }
156
+ interface NewsletterConfirmResponse {
157
+ message: string;
158
+ subscription?: {
159
+ email: string;
160
+ name?: string;
161
+ frequency: string;
162
+ };
163
+ }
164
+ interface NewsletterUnsubscribeRequest {
165
+ token?: string;
166
+ email?: string;
167
+ reason?: string;
168
+ }
101
169
  type ContentStatus = 'draft' | 'publish' | 'private' | 'trash';
102
170
  type ContentType = 'post' | 'page';
103
171
  interface Content {
@@ -329,6 +397,7 @@ interface RequestOptions {
329
397
  body?: any;
330
398
  params?: Record<string, string | number | boolean>;
331
399
  timeout?: number;
400
+ csrfToken?: string;
332
401
  }
333
402
 
334
403
  /**
@@ -365,19 +434,19 @@ declare class HttpClient {
365
434
  /**
366
435
  * POST request
367
436
  */
368
- post<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
437
+ post<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
369
438
  /**
370
439
  * PUT request
371
440
  */
372
- put<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
441
+ put<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
373
442
  /**
374
443
  * DELETE request
375
444
  */
376
- delete<T = any>(endpoint: string): Promise<ApiResponse<T>>;
445
+ delete<T = any>(endpoint: string, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
377
446
  /**
378
447
  * PATCH request
379
448
  */
380
- patch<T = any>(endpoint: string, body?: any): Promise<ApiResponse<T>>;
449
+ patch<T = any>(endpoint: string, body?: any, options?: Partial<RequestOptions>): Promise<ApiResponse<T>>;
381
450
  /**
382
451
  * Build full URL with query parameters
383
452
  */
@@ -433,19 +502,19 @@ declare abstract class BaseClient {
433
502
  /**
434
503
  * Handle create operations
435
504
  */
436
- protected create<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
505
+ protected create<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
437
506
  /**
438
507
  * Handle update operations
439
508
  */
440
- protected update<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
509
+ protected update<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
441
510
  /**
442
511
  * Handle partial update operations
443
512
  */
444
- protected patch<T, R = T>(endpoint: string, data: T): Promise<ApiResponse<R>>;
513
+ protected patch<T, R = T>(endpoint: string, data: T, csrfToken?: string): Promise<ApiResponse<R>>;
445
514
  /**
446
515
  * Handle delete operations
447
516
  */
448
- protected delete<T = any>(endpoint: string): Promise<ApiResponse<T>>;
517
+ protected delete<T = any>(endpoint: string, csrfToken?: string): Promise<ApiResponse<T>>;
449
518
  }
450
519
 
451
520
  /**
@@ -1389,8 +1458,11 @@ declare class ContactClient extends BaseClient {
1389
1458
  private contactEndpoint;
1390
1459
  /**
1391
1460
  * Submit contact form
1461
+ * @param siteName - The site to submit contact form to
1462
+ * @param data - Contact form data
1463
+ * @param csrfToken - CSRF token (required for browser-based submissions)
1392
1464
  */
1393
- submitContact(siteName: string, data: CreateContactRequest): Promise<ApiResponse<{
1465
+ submitContact(siteName: string, data: CreateContactRequest, csrfToken?: string): Promise<ApiResponse<{
1394
1466
  id: string;
1395
1467
  message: string;
1396
1468
  status: string;
@@ -1532,6 +1604,208 @@ declare class ContactClient extends BaseClient {
1532
1604
  }>>;
1533
1605
  }
1534
1606
 
1607
+ /**
1608
+ * Newsletter subscription client for PerspectAPI SDK
1609
+ */
1610
+
1611
+ declare class NewsletterClient extends BaseClient {
1612
+ constructor(http: any);
1613
+ /**
1614
+ * Build a newsletter endpoint scoped to a site (without /sites prefix)
1615
+ */
1616
+ private newsletterEndpoint;
1617
+ /**
1618
+ * Subscribe to newsletter
1619
+ * @param siteName - The site to subscribe to
1620
+ * @param data - Subscription data
1621
+ * @param csrfToken - CSRF token (required for browser-based submissions)
1622
+ */
1623
+ subscribe(siteName: string, data: CreateNewsletterSubscriptionRequest, csrfToken?: string): Promise<ApiResponse<NewsletterSubscribeResponse>>;
1624
+ /**
1625
+ * Confirm newsletter subscription via token
1626
+ */
1627
+ confirmSubscription(siteName: string, token: string): Promise<ApiResponse<NewsletterConfirmResponse>>;
1628
+ /**
1629
+ * Unsubscribe from newsletter
1630
+ * @param siteName - The site to unsubscribe from
1631
+ * @param data - Unsubscribe data
1632
+ * @param csrfToken - CSRF token (required for browser-based submissions)
1633
+ */
1634
+ unsubscribe(siteName: string, data: NewsletterUnsubscribeRequest, csrfToken?: string): Promise<ApiResponse<{
1635
+ message: string;
1636
+ }>>;
1637
+ /**
1638
+ * One-click unsubscribe via token (GET request)
1639
+ */
1640
+ unsubscribeByToken(siteName: string, token: string): Promise<ApiResponse<string>>;
1641
+ /**
1642
+ * Update subscription preferences
1643
+ * @param siteName - The site name
1644
+ * @param email - Subscriber email
1645
+ * @param preferences - New preferences
1646
+ * @param csrfToken - CSRF token (required for browser-based submissions)
1647
+ */
1648
+ updatePreferences(siteName: string, email: string, preferences: NewsletterPreferences, csrfToken?: string): Promise<ApiResponse<{
1649
+ message: string;
1650
+ preferences: NewsletterPreferences;
1651
+ }>>;
1652
+ /**
1653
+ * Get available newsletter lists
1654
+ */
1655
+ getLists(siteName: string): Promise<ApiResponse<{
1656
+ lists: NewsletterList[];
1657
+ total: number;
1658
+ }>>;
1659
+ /**
1660
+ * Check subscription status by email
1661
+ */
1662
+ getStatus(siteName: string, email: string): Promise<ApiResponse<NewsletterStatusResponse>>;
1663
+ /**
1664
+ * Get all newsletter subscriptions (admin only)
1665
+ */
1666
+ getSubscriptions(siteName: string, params?: {
1667
+ page?: number;
1668
+ limit?: number;
1669
+ status?: string;
1670
+ list_id?: string;
1671
+ search?: string;
1672
+ startDate?: string;
1673
+ endDate?: string;
1674
+ }): Promise<PaginatedResponse<NewsletterSubscription>>;
1675
+ /**
1676
+ * Get subscription by ID (admin only)
1677
+ */
1678
+ getSubscriptionById(siteName: string, id: string): Promise<ApiResponse<NewsletterSubscription>>;
1679
+ /**
1680
+ * Update subscription status (admin only)
1681
+ */
1682
+ updateSubscriptionStatus(siteName: string, id: string, status: 'confirmed' | 'unsubscribed' | 'bounced' | 'complained', notes?: string): Promise<ApiResponse<{
1683
+ message: string;
1684
+ }>>;
1685
+ /**
1686
+ * Delete subscription (admin only)
1687
+ */
1688
+ deleteSubscription(siteName: string, id: string): Promise<ApiResponse<{
1689
+ message: string;
1690
+ }>>;
1691
+ /**
1692
+ * Bulk update subscriptions (admin only)
1693
+ */
1694
+ bulkUpdateSubscriptions(siteName: string, data: {
1695
+ ids: string[];
1696
+ action: 'confirm' | 'unsubscribe' | 'delete' | 'add_to_list' | 'remove_from_list';
1697
+ list_id?: string;
1698
+ }): Promise<ApiResponse<{
1699
+ success: boolean;
1700
+ updatedCount: number;
1701
+ failedCount: number;
1702
+ }>>;
1703
+ /**
1704
+ * Create newsletter list (admin only)
1705
+ */
1706
+ createList(siteName: string, data: {
1707
+ list_name: string;
1708
+ slug: string;
1709
+ description?: string;
1710
+ is_public?: boolean;
1711
+ is_default?: boolean;
1712
+ double_opt_in?: boolean;
1713
+ welcome_email_enabled?: boolean;
1714
+ }): Promise<ApiResponse<NewsletterList>>;
1715
+ /**
1716
+ * Update newsletter list (admin only)
1717
+ */
1718
+ updateList(siteName: string, listId: string, data: Partial<{
1719
+ list_name: string;
1720
+ description: string;
1721
+ is_public: boolean;
1722
+ is_default: boolean;
1723
+ double_opt_in: boolean;
1724
+ welcome_email_enabled: boolean;
1725
+ }>): Promise<ApiResponse<{
1726
+ message: string;
1727
+ }>>;
1728
+ /**
1729
+ * Delete newsletter list (admin only)
1730
+ */
1731
+ deleteList(siteName: string, listId: string): Promise<ApiResponse<{
1732
+ message: string;
1733
+ }>>;
1734
+ /**
1735
+ * Get newsletter statistics (admin only)
1736
+ */
1737
+ getStatistics(siteName: string, params?: {
1738
+ startDate?: string;
1739
+ endDate?: string;
1740
+ list_id?: string;
1741
+ }): Promise<ApiResponse<{
1742
+ totalSubscribers: number;
1743
+ confirmedSubscribers: number;
1744
+ pendingSubscribers: number;
1745
+ unsubscribedCount: number;
1746
+ bouncedCount: number;
1747
+ subscribersByDay: Array<{
1748
+ date: string;
1749
+ count: number;
1750
+ }>;
1751
+ subscribersByList: Array<{
1752
+ list_id: string;
1753
+ list_name: string;
1754
+ count: number;
1755
+ }>;
1756
+ engagementMetrics: {
1757
+ averageOpenRate: number;
1758
+ averageClickRate: number;
1759
+ };
1760
+ }>>;
1761
+ /**
1762
+ * Export newsletter subscriptions (admin only)
1763
+ */
1764
+ exportSubscriptions(siteName: string, params?: {
1765
+ format?: 'csv' | 'json' | 'xlsx';
1766
+ status?: string;
1767
+ list_id?: string;
1768
+ startDate?: string;
1769
+ endDate?: string;
1770
+ }): Promise<ApiResponse<{
1771
+ downloadUrl: string;
1772
+ expiresAt: string;
1773
+ }>>;
1774
+ /**
1775
+ * Import newsletter subscriptions (admin only)
1776
+ */
1777
+ importSubscriptions(siteName: string, data: {
1778
+ subscriptions: Array<{
1779
+ email: string;
1780
+ name?: string;
1781
+ status?: string;
1782
+ lists?: string[];
1783
+ }>;
1784
+ skip_confirmation?: boolean;
1785
+ update_existing?: boolean;
1786
+ }): Promise<ApiResponse<{
1787
+ imported: number;
1788
+ updated: number;
1789
+ failed: number;
1790
+ errors?: Array<{
1791
+ email: string;
1792
+ error: string;
1793
+ }>;
1794
+ }>>;
1795
+ /**
1796
+ * Send test newsletter (admin only)
1797
+ */
1798
+ sendTestNewsletter(siteName: string, data: {
1799
+ to: string;
1800
+ subject: string;
1801
+ html_content: string;
1802
+ text_content?: string;
1803
+ }): Promise<ApiResponse<{
1804
+ message: string;
1805
+ sent: boolean;
1806
+ }>>;
1807
+ }
1808
+
1535
1809
  /**
1536
1810
  * Main PerspectAPI SDK Client
1537
1811
  * Cloudflare Workers compatible TypeScript SDK
@@ -1549,6 +1823,7 @@ declare class PerspectApiClient {
1549
1823
  readonly webhooks: WebhooksClient;
1550
1824
  readonly checkout: CheckoutClient;
1551
1825
  readonly contact: ContactClient;
1826
+ readonly newsletter: NewsletterClient;
1552
1827
  constructor(config: PerspectApiConfig);
1553
1828
  /**
1554
1829
  * Update authentication token
@@ -1767,4 +2042,4 @@ declare function createCheckoutSession(options: CheckoutSessionOptions): Promise
1767
2042
  error: string;
1768
2043
  }>;
1769
2044
 
1770
- export { type ApiError, type ApiKey, ApiKeysClient, type ApiResponse, AuthClient, type AuthResponse, BaseClient, type BlogPost, CategoriesClient, type Category, CheckoutClient, type CheckoutMetadata, type CheckoutMetadataValue, type CheckoutSession, type CheckoutSessionOptions, ContactClient, type ContactSubmission, type Content, ContentClient, type ContentQueryParams, type ContentStatus, type ContentType, type CreateApiKeyRequest, type CreateCategoryRequest, type CreateCheckoutSessionRequest, type CreateContactRequest, type CreateContentRequest, type CreateOrganizationRequest, type CreatePaymentGatewayRequest, type CreateProductRequest, type CreateSiteRequest, type CreateWebhookRequest, HttpClient, type HttpMethod, type LoadContentBySlugOptions, type LoadContentOptions, type LoadProductBySlugOptions, type LoadProductsOptions, type LoaderLogger, type LoaderOptions, type MediaItem, type Organization, OrganizationsClient, type PaginatedResponse, type PaginationParams, type PaymentGateway, PerspectApiClient, type PerspectApiConfig, type Product, type ProductQueryParams, ProductsClient, type RequestOptions, type SignInRequest, type SignUpRequest, type Site, SitesClient, type UpdateApiKeyRequest, type UpdateContentRequest, type User, type Webhook, WebhooksClient, createApiError, createCheckoutSession, createPerspectApiClient, PerspectApiClient as default, loadAllContent, loadContentBySlug, loadPages, loadPosts, loadProductBySlug, loadProducts, transformContent, transformProduct };
2045
+ export { type ApiError, type ApiKey, ApiKeysClient, type ApiResponse, AuthClient, type AuthResponse, BaseClient, type BlogPost, CategoriesClient, type Category, CheckoutClient, type CheckoutMetadata, type CheckoutMetadataValue, type CheckoutSession, type CheckoutSessionOptions, ContactClient, type ContactSubmission, type Content, ContentClient, type ContentQueryParams, type ContentStatus, type ContentType, type CreateApiKeyRequest, type CreateCategoryRequest, type CreateCheckoutSessionRequest, type CreateContactRequest, type CreateContentRequest, type CreateNewsletterSubscriptionRequest, type CreateOrganizationRequest, type CreatePaymentGatewayRequest, type CreateProductRequest, type CreateSiteRequest, type CreateWebhookRequest, HttpClient, type HttpMethod, type LoadContentBySlugOptions, type LoadContentOptions, type LoadProductBySlugOptions, type LoadProductsOptions, type LoaderLogger, type LoaderOptions, type MediaItem, NewsletterClient, type NewsletterConfirmResponse, type NewsletterList, type NewsletterPreferences, type NewsletterStatusResponse, type NewsletterSubscribeResponse, type NewsletterSubscription, type NewsletterUnsubscribeRequest, type Organization, OrganizationsClient, type PaginatedResponse, type PaginationParams, type PaymentGateway, PerspectApiClient, type PerspectApiConfig, type Product, type ProductQueryParams, ProductsClient, type RequestOptions, type SignInRequest, type SignUpRequest, type Site, SitesClient, type UpdateApiKeyRequest, type UpdateContentRequest, type User, type Webhook, WebhooksClient, createApiError, createCheckoutSession, createPerspectApiClient, PerspectApiClient as default, loadAllContent, loadContentBySlug, loadPages, loadPosts, loadProductBySlug, loadProducts, transformContent, transformProduct };