perspectapi-ts-sdk 1.1.1 → 1.2.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/dist/index.d.mts +262 -1
- package/dist/index.d.ts +262 -1
- package/dist/index.js +183 -0
- package/dist/index.mjs +182 -0
- package/package.json +1 -1
- package/src/client/newsletter-client.ts +360 -0
- package/src/index.ts +9 -0
- package/src/perspect-api-client.ts +3 -0
- package/src/types/index.ts +77 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Newsletter subscription client for PerspectAPI SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { BaseClient } from './base-client';
|
|
6
|
+
import type {
|
|
7
|
+
NewsletterSubscription,
|
|
8
|
+
CreateNewsletterSubscriptionRequest,
|
|
9
|
+
NewsletterList,
|
|
10
|
+
NewsletterPreferences,
|
|
11
|
+
NewsletterStatusResponse,
|
|
12
|
+
NewsletterSubscribeResponse,
|
|
13
|
+
NewsletterConfirmResponse,
|
|
14
|
+
NewsletterUnsubscribeRequest,
|
|
15
|
+
PaginatedResponse,
|
|
16
|
+
ApiResponse,
|
|
17
|
+
} from '../types';
|
|
18
|
+
|
|
19
|
+
export class NewsletterClient extends BaseClient {
|
|
20
|
+
constructor(http: any) {
|
|
21
|
+
super(http, '/api/v1');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build a newsletter endpoint scoped to a site (without /sites prefix)
|
|
26
|
+
*/
|
|
27
|
+
private newsletterEndpoint(siteName: string, endpoint: string): string {
|
|
28
|
+
return this.siteScopedEndpoint(siteName, endpoint, { includeSitesSegment: false });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Subscribe to newsletter
|
|
33
|
+
*/
|
|
34
|
+
async subscribe(
|
|
35
|
+
siteName: string,
|
|
36
|
+
data: CreateNewsletterSubscriptionRequest
|
|
37
|
+
): Promise<ApiResponse<NewsletterSubscribeResponse>> {
|
|
38
|
+
return this.create<CreateNewsletterSubscriptionRequest, NewsletterSubscribeResponse>(
|
|
39
|
+
this.newsletterEndpoint(siteName, '/newsletter/subscribe'),
|
|
40
|
+
data
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Confirm newsletter subscription via token
|
|
46
|
+
*/
|
|
47
|
+
async confirmSubscription(
|
|
48
|
+
siteName: string,
|
|
49
|
+
token: string
|
|
50
|
+
): Promise<ApiResponse<NewsletterConfirmResponse>> {
|
|
51
|
+
return this.getSingle(
|
|
52
|
+
this.newsletterEndpoint(siteName, `/newsletter/confirm/${encodeURIComponent(token)}`)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Unsubscribe from newsletter
|
|
58
|
+
*/
|
|
59
|
+
async unsubscribe(
|
|
60
|
+
siteName: string,
|
|
61
|
+
data: NewsletterUnsubscribeRequest
|
|
62
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
63
|
+
return this.create<NewsletterUnsubscribeRequest, { message: string }>(
|
|
64
|
+
this.newsletterEndpoint(siteName, '/newsletter/unsubscribe'),
|
|
65
|
+
data
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* One-click unsubscribe via token (GET request)
|
|
71
|
+
*/
|
|
72
|
+
async unsubscribeByToken(
|
|
73
|
+
siteName: string,
|
|
74
|
+
token: string
|
|
75
|
+
): Promise<ApiResponse<string>> {
|
|
76
|
+
// This returns HTML, so we return the raw response
|
|
77
|
+
return this.http.get(
|
|
78
|
+
this.buildPath(this.newsletterEndpoint(siteName, `/newsletter/unsubscribe/${encodeURIComponent(token)}`))
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Update subscription preferences
|
|
84
|
+
*/
|
|
85
|
+
async updatePreferences(
|
|
86
|
+
siteName: string,
|
|
87
|
+
email: string,
|
|
88
|
+
preferences: NewsletterPreferences
|
|
89
|
+
): Promise<ApiResponse<{ message: string; preferences: NewsletterPreferences }>> {
|
|
90
|
+
return this.patch(
|
|
91
|
+
this.newsletterEndpoint(siteName, '/newsletter/preferences'),
|
|
92
|
+
{ email, ...preferences }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get available newsletter lists
|
|
98
|
+
*/
|
|
99
|
+
async getLists(siteName: string): Promise<ApiResponse<{
|
|
100
|
+
lists: NewsletterList[];
|
|
101
|
+
total: number;
|
|
102
|
+
}>> {
|
|
103
|
+
return this.getSingle(
|
|
104
|
+
this.newsletterEndpoint(siteName, '/newsletter/lists')
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check subscription status by email
|
|
110
|
+
*/
|
|
111
|
+
async getStatus(
|
|
112
|
+
siteName: string,
|
|
113
|
+
email: string
|
|
114
|
+
): Promise<ApiResponse<NewsletterStatusResponse>> {
|
|
115
|
+
return this.http.get(
|
|
116
|
+
this.buildPath(this.newsletterEndpoint(siteName, '/newsletter/status')),
|
|
117
|
+
{ email }
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Admin methods (require authentication)
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get all newsletter subscriptions (admin only)
|
|
125
|
+
*/
|
|
126
|
+
async getSubscriptions(
|
|
127
|
+
siteName: string,
|
|
128
|
+
params?: {
|
|
129
|
+
page?: number;
|
|
130
|
+
limit?: number;
|
|
131
|
+
status?: string;
|
|
132
|
+
list_id?: string;
|
|
133
|
+
search?: string;
|
|
134
|
+
startDate?: string;
|
|
135
|
+
endDate?: string;
|
|
136
|
+
}
|
|
137
|
+
): Promise<PaginatedResponse<NewsletterSubscription>> {
|
|
138
|
+
return this.getPaginated<NewsletterSubscription>(
|
|
139
|
+
this.newsletterEndpoint(siteName, '/newsletter/subscriptions'),
|
|
140
|
+
params
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get subscription by ID (admin only)
|
|
146
|
+
*/
|
|
147
|
+
async getSubscriptionById(
|
|
148
|
+
siteName: string,
|
|
149
|
+
id: string
|
|
150
|
+
): Promise<ApiResponse<NewsletterSubscription>> {
|
|
151
|
+
return this.getSingle(
|
|
152
|
+
this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`)
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Update subscription status (admin only)
|
|
158
|
+
*/
|
|
159
|
+
async updateSubscriptionStatus(
|
|
160
|
+
siteName: string,
|
|
161
|
+
id: string,
|
|
162
|
+
status: 'confirmed' | 'unsubscribed' | 'bounced' | 'complained',
|
|
163
|
+
notes?: string
|
|
164
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
165
|
+
return this.patch(
|
|
166
|
+
this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`),
|
|
167
|
+
{ status, notes }
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Delete subscription (admin only)
|
|
173
|
+
*/
|
|
174
|
+
async deleteSubscription(
|
|
175
|
+
siteName: string,
|
|
176
|
+
id: string
|
|
177
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
178
|
+
return this.delete<{ message: string }>(
|
|
179
|
+
this.newsletterEndpoint(siteName, `/newsletter/subscriptions/${encodeURIComponent(id)}`)
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Bulk update subscriptions (admin only)
|
|
185
|
+
*/
|
|
186
|
+
async bulkUpdateSubscriptions(
|
|
187
|
+
siteName: string,
|
|
188
|
+
data: {
|
|
189
|
+
ids: string[];
|
|
190
|
+
action: 'confirm' | 'unsubscribe' | 'delete' | 'add_to_list' | 'remove_from_list';
|
|
191
|
+
list_id?: string;
|
|
192
|
+
}
|
|
193
|
+
): Promise<ApiResponse<{
|
|
194
|
+
success: boolean;
|
|
195
|
+
updatedCount: number;
|
|
196
|
+
failedCount: number;
|
|
197
|
+
}>> {
|
|
198
|
+
return this.create(
|
|
199
|
+
this.newsletterEndpoint(siteName, '/newsletter/subscriptions/bulk-update'),
|
|
200
|
+
data
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Create newsletter list (admin only)
|
|
206
|
+
*/
|
|
207
|
+
async createList(
|
|
208
|
+
siteName: string,
|
|
209
|
+
data: {
|
|
210
|
+
list_name: string;
|
|
211
|
+
slug: string;
|
|
212
|
+
description?: string;
|
|
213
|
+
is_public?: boolean;
|
|
214
|
+
is_default?: boolean;
|
|
215
|
+
double_opt_in?: boolean;
|
|
216
|
+
welcome_email_enabled?: boolean;
|
|
217
|
+
}
|
|
218
|
+
): Promise<ApiResponse<NewsletterList>> {
|
|
219
|
+
return this.create<any, NewsletterList>(
|
|
220
|
+
this.newsletterEndpoint(siteName, '/newsletter/lists'),
|
|
221
|
+
data
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Update newsletter list (admin only)
|
|
227
|
+
*/
|
|
228
|
+
async updateList(
|
|
229
|
+
siteName: string,
|
|
230
|
+
listId: string,
|
|
231
|
+
data: Partial<{
|
|
232
|
+
list_name: string;
|
|
233
|
+
description: string;
|
|
234
|
+
is_public: boolean;
|
|
235
|
+
is_default: boolean;
|
|
236
|
+
double_opt_in: boolean;
|
|
237
|
+
welcome_email_enabled: boolean;
|
|
238
|
+
}>
|
|
239
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
240
|
+
return this.update(
|
|
241
|
+
this.newsletterEndpoint(siteName, `/newsletter/lists/${encodeURIComponent(listId)}`),
|
|
242
|
+
data
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Delete newsletter list (admin only)
|
|
248
|
+
*/
|
|
249
|
+
async deleteList(
|
|
250
|
+
siteName: string,
|
|
251
|
+
listId: string
|
|
252
|
+
): Promise<ApiResponse<{ message: string }>> {
|
|
253
|
+
return this.delete<{ message: string }>(
|
|
254
|
+
this.newsletterEndpoint(siteName, `/newsletter/lists/${encodeURIComponent(listId)}`)
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get newsletter statistics (admin only)
|
|
260
|
+
*/
|
|
261
|
+
async getStatistics(
|
|
262
|
+
siteName: string,
|
|
263
|
+
params?: {
|
|
264
|
+
startDate?: string;
|
|
265
|
+
endDate?: string;
|
|
266
|
+
list_id?: string;
|
|
267
|
+
}
|
|
268
|
+
): Promise<ApiResponse<{
|
|
269
|
+
totalSubscribers: number;
|
|
270
|
+
confirmedSubscribers: number;
|
|
271
|
+
pendingSubscribers: number;
|
|
272
|
+
unsubscribedCount: number;
|
|
273
|
+
bouncedCount: number;
|
|
274
|
+
subscribersByDay: Array<{
|
|
275
|
+
date: string;
|
|
276
|
+
count: number;
|
|
277
|
+
}>;
|
|
278
|
+
subscribersByList: Array<{
|
|
279
|
+
list_id: string;
|
|
280
|
+
list_name: string;
|
|
281
|
+
count: number;
|
|
282
|
+
}>;
|
|
283
|
+
engagementMetrics: {
|
|
284
|
+
averageOpenRate: number;
|
|
285
|
+
averageClickRate: number;
|
|
286
|
+
};
|
|
287
|
+
}>> {
|
|
288
|
+
return this.http.get(
|
|
289
|
+
this.buildPath(this.newsletterEndpoint(siteName, '/newsletter/statistics')),
|
|
290
|
+
params
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Export newsletter subscriptions (admin only)
|
|
296
|
+
*/
|
|
297
|
+
async exportSubscriptions(
|
|
298
|
+
siteName: string,
|
|
299
|
+
params?: {
|
|
300
|
+
format?: 'csv' | 'json' | 'xlsx';
|
|
301
|
+
status?: string;
|
|
302
|
+
list_id?: string;
|
|
303
|
+
startDate?: string;
|
|
304
|
+
endDate?: string;
|
|
305
|
+
}
|
|
306
|
+
): Promise<ApiResponse<{
|
|
307
|
+
downloadUrl: string;
|
|
308
|
+
expiresAt: string;
|
|
309
|
+
}>> {
|
|
310
|
+
return this.create(
|
|
311
|
+
this.newsletterEndpoint(siteName, '/newsletter/export'),
|
|
312
|
+
params || {}
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Import newsletter subscriptions (admin only)
|
|
318
|
+
*/
|
|
319
|
+
async importSubscriptions(
|
|
320
|
+
siteName: string,
|
|
321
|
+
data: {
|
|
322
|
+
subscriptions: Array<{
|
|
323
|
+
email: string;
|
|
324
|
+
name?: string;
|
|
325
|
+
status?: string;
|
|
326
|
+
lists?: string[];
|
|
327
|
+
}>;
|
|
328
|
+
skip_confirmation?: boolean;
|
|
329
|
+
update_existing?: boolean;
|
|
330
|
+
}
|
|
331
|
+
): Promise<ApiResponse<{
|
|
332
|
+
imported: number;
|
|
333
|
+
updated: number;
|
|
334
|
+
failed: number;
|
|
335
|
+
errors?: Array<{ email: string; error: string }>;
|
|
336
|
+
}>> {
|
|
337
|
+
return this.create(
|
|
338
|
+
this.newsletterEndpoint(siteName, '/newsletter/import'),
|
|
339
|
+
data
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Send test newsletter (admin only)
|
|
345
|
+
*/
|
|
346
|
+
async sendTestNewsletter(
|
|
347
|
+
siteName: string,
|
|
348
|
+
data: {
|
|
349
|
+
to: string;
|
|
350
|
+
subject: string;
|
|
351
|
+
html_content: string;
|
|
352
|
+
text_content?: string;
|
|
353
|
+
}
|
|
354
|
+
): Promise<ApiResponse<{ message: string; sent: boolean }>> {
|
|
355
|
+
return this.create(
|
|
356
|
+
this.newsletterEndpoint(siteName, '/newsletter/test'),
|
|
357
|
+
data
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,7 @@ export { CategoriesClient } from './client/categories-client';
|
|
|
18
18
|
export { WebhooksClient } from './client/webhooks-client';
|
|
19
19
|
export { CheckoutClient } from './client/checkout-client';
|
|
20
20
|
export { ContactClient } from './client/contact-client';
|
|
21
|
+
export { NewsletterClient } from './client/newsletter-client';
|
|
21
22
|
|
|
22
23
|
// Base classes
|
|
23
24
|
export { BaseClient } from './client/base-client';
|
|
@@ -71,4 +72,12 @@ export type {
|
|
|
71
72
|
ContactSubmission,
|
|
72
73
|
CheckoutSession,
|
|
73
74
|
PaymentGateway,
|
|
75
|
+
NewsletterSubscription,
|
|
76
|
+
CreateNewsletterSubscriptionRequest,
|
|
77
|
+
NewsletterList,
|
|
78
|
+
NewsletterPreferences,
|
|
79
|
+
NewsletterStatusResponse,
|
|
80
|
+
NewsletterSubscribeResponse,
|
|
81
|
+
NewsletterConfirmResponse,
|
|
82
|
+
NewsletterUnsubscribeRequest,
|
|
74
83
|
} from './types';
|
|
@@ -14,6 +14,7 @@ import { CategoriesClient } from './client/categories-client';
|
|
|
14
14
|
import { WebhooksClient } from './client/webhooks-client';
|
|
15
15
|
import { CheckoutClient } from './client/checkout-client';
|
|
16
16
|
import { ContactClient } from './client/contact-client';
|
|
17
|
+
import { NewsletterClient } from './client/newsletter-client';
|
|
17
18
|
|
|
18
19
|
import type { PerspectApiConfig, ApiResponse } from './types';
|
|
19
20
|
|
|
@@ -31,6 +32,7 @@ export class PerspectApiClient {
|
|
|
31
32
|
public readonly webhooks: WebhooksClient;
|
|
32
33
|
public readonly checkout: CheckoutClient;
|
|
33
34
|
public readonly contact: ContactClient;
|
|
35
|
+
public readonly newsletter: NewsletterClient;
|
|
34
36
|
|
|
35
37
|
constructor(config: PerspectApiConfig) {
|
|
36
38
|
// Validate required configuration
|
|
@@ -52,6 +54,7 @@ export class PerspectApiClient {
|
|
|
52
54
|
this.webhooks = new WebhooksClient(this.http);
|
|
53
55
|
this.checkout = new CheckoutClient(this.http);
|
|
54
56
|
this.contact = new ContactClient(this.http);
|
|
57
|
+
this.newsletter = new NewsletterClient(this.http);
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
/**
|
package/src/types/index.ts
CHANGED
|
@@ -119,6 +119,83 @@ export interface CreateSiteRequest {
|
|
|
119
119
|
organizationId: number;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
// Newsletter Subscriptions
|
|
123
|
+
export interface NewsletterSubscription {
|
|
124
|
+
id: string;
|
|
125
|
+
email: string;
|
|
126
|
+
name?: string;
|
|
127
|
+
status: 'pending' | 'confirmed' | 'unsubscribed' | 'bounced' | 'complained';
|
|
128
|
+
frequency: 'instant' | 'daily' | 'weekly' | 'monthly';
|
|
129
|
+
topics?: string[];
|
|
130
|
+
language: string;
|
|
131
|
+
confirmedAt?: string;
|
|
132
|
+
unsubscribedAt?: string;
|
|
133
|
+
createdAt: string;
|
|
134
|
+
updatedAt: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface CreateNewsletterSubscriptionRequest {
|
|
138
|
+
email: string;
|
|
139
|
+
name?: string;
|
|
140
|
+
list_ids?: string[];
|
|
141
|
+
frequency?: 'instant' | 'daily' | 'weekly' | 'monthly';
|
|
142
|
+
topics?: string[];
|
|
143
|
+
language?: string;
|
|
144
|
+
source?: string;
|
|
145
|
+
source_url?: string;
|
|
146
|
+
double_opt_in?: boolean;
|
|
147
|
+
turnstile_token?: string;
|
|
148
|
+
metadata?: Record<string, any>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface NewsletterList {
|
|
152
|
+
id: string;
|
|
153
|
+
list_name: string;
|
|
154
|
+
slug: string;
|
|
155
|
+
description?: string;
|
|
156
|
+
is_default: boolean;
|
|
157
|
+
subscriber_count?: number;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface NewsletterPreferences {
|
|
161
|
+
frequency?: 'instant' | 'daily' | 'weekly' | 'monthly';
|
|
162
|
+
topics?: string[];
|
|
163
|
+
language?: string;
|
|
164
|
+
email_format?: 'html' | 'text' | 'both';
|
|
165
|
+
timezone?: string;
|
|
166
|
+
track_opens?: boolean;
|
|
167
|
+
track_clicks?: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface NewsletterStatusResponse {
|
|
171
|
+
subscribed: boolean;
|
|
172
|
+
status: string;
|
|
173
|
+
frequency?: string;
|
|
174
|
+
created_at?: string;
|
|
175
|
+
confirmed_at?: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface NewsletterSubscribeResponse {
|
|
179
|
+
message: string;
|
|
180
|
+
status: string;
|
|
181
|
+
subscription_id?: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface NewsletterConfirmResponse {
|
|
185
|
+
message: string;
|
|
186
|
+
subscription?: {
|
|
187
|
+
email: string;
|
|
188
|
+
name?: string;
|
|
189
|
+
frequency: string;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface NewsletterUnsubscribeRequest {
|
|
194
|
+
token?: string;
|
|
195
|
+
email?: string;
|
|
196
|
+
reason?: string;
|
|
197
|
+
}
|
|
198
|
+
|
|
122
199
|
// Content Management
|
|
123
200
|
export type ContentStatus = 'draft' | 'publish' | 'private' | 'trash';
|
|
124
201
|
export type ContentType = 'post' | 'page';
|