@sendly/node 3.12.3 → 3.13.1

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.js CHANGED
@@ -1520,6 +1520,65 @@ var AccountResource = class {
1520
1520
  path: `/account/keys/${encodeURIComponent(id)}`
1521
1521
  });
1522
1522
  }
1523
+ /**
1524
+ * Rename an API key
1525
+ *
1526
+ * @param id - API key ID
1527
+ * @param name - New name for the API key
1528
+ * @returns Updated API key
1529
+ *
1530
+ * @example
1531
+ * ```typescript
1532
+ * const key = await sendly.account.renameApiKey('key_xxx', 'Production Key');
1533
+ * console.log(`Renamed to: ${key.name}`);
1534
+ * ```
1535
+ */
1536
+ async renameApiKey(id, name) {
1537
+ if (!id) {
1538
+ throw new Error("API key ID is required");
1539
+ }
1540
+ if (!name) {
1541
+ throw new Error("New name is required");
1542
+ }
1543
+ const key = await this.http.request({
1544
+ method: "PATCH",
1545
+ path: `/account/keys/${encodeURIComponent(id)}/rename`,
1546
+ body: { name }
1547
+ });
1548
+ return key;
1549
+ }
1550
+ /**
1551
+ * Rotate an API key
1552
+ *
1553
+ * Creates a new key while optionally keeping the old one active for a grace period.
1554
+ *
1555
+ * @param id - API key ID to rotate
1556
+ * @param options - Rotation options
1557
+ * @returns New API key with the full key value
1558
+ *
1559
+ * @example
1560
+ * ```typescript
1561
+ * // Rotate immediately (old key stops working instantly)
1562
+ * const { newKey, key } = await sendly.account.rotateApiKey('key_xxx');
1563
+ * console.log(`New key: ${key}`); // Save this!
1564
+ *
1565
+ * // Rotate with 24-hour grace period (both keys work for 24h)
1566
+ * const { newKey, key } = await sendly.account.rotateApiKey('key_xxx', {
1567
+ * gracePeriodHours: 24
1568
+ * });
1569
+ * ```
1570
+ */
1571
+ async rotateApiKey(id, options) {
1572
+ if (!id) {
1573
+ throw new Error("API key ID is required");
1574
+ }
1575
+ const response = await this.http.request({
1576
+ method: "POST",
1577
+ path: `/account/keys/${encodeURIComponent(id)}/rotate`,
1578
+ body: options?.gracePeriodHours ? { gracePeriodHours: options.gracePeriodHours } : {}
1579
+ });
1580
+ return response;
1581
+ }
1523
1582
  };
1524
1583
 
1525
1584
  // src/resources/verify.ts
@@ -1970,6 +2029,37 @@ var TemplatesResource = class {
1970
2029
  path: `/templates/${id}`
1971
2030
  });
1972
2031
  }
2032
+ /**
2033
+ * Clone a template
2034
+ *
2035
+ * Creates a copy of an existing template (including presets).
2036
+ *
2037
+ * @param id - Template ID to clone
2038
+ * @param options - Optional clone options
2039
+ * @returns The cloned template (as draft)
2040
+ *
2041
+ * @example
2042
+ * ```typescript
2043
+ * // Clone a preset template
2044
+ * const myOtp = await sendly.templates.clone('tpl_preset_otp', {
2045
+ * name: 'My Custom OTP'
2046
+ * });
2047
+ *
2048
+ * // Modify and publish
2049
+ * await sendly.templates.update(myOtp.id, {
2050
+ * text: 'Your {{app_name}} code: {{code}}. Expires in 5 min.'
2051
+ * });
2052
+ * await sendly.templates.publish(myOtp.id);
2053
+ * ```
2054
+ */
2055
+ async clone(id, options) {
2056
+ const response = await this.http.request({
2057
+ method: "POST",
2058
+ path: `/templates/${id}/clone`,
2059
+ body: options?.name ? { name: options.name } : {}
2060
+ });
2061
+ return this.transformTemplate(response);
2062
+ }
1973
2063
  transformTemplate(t) {
1974
2064
  return {
1975
2065
  id: t.id,
@@ -1991,6 +2081,644 @@ var TemplatesResource = class {
1991
2081
  }
1992
2082
  };
1993
2083
 
2084
+ // src/resources/campaigns.ts
2085
+ var CampaignsResource = class {
2086
+ http;
2087
+ constructor(http) {
2088
+ this.http = http;
2089
+ }
2090
+ /**
2091
+ * Create a new campaign
2092
+ *
2093
+ * @param request - Campaign details
2094
+ * @returns The created campaign (as draft)
2095
+ *
2096
+ * @example
2097
+ * ```typescript
2098
+ * const campaign = await sendly.campaigns.create({
2099
+ * name: 'Black Friday Sale',
2100
+ * text: 'Hi {{name}}! 50% off everything today only. Shop now!',
2101
+ * contactListIds: ['lst_customers', 'lst_subscribers']
2102
+ * });
2103
+ * ```
2104
+ */
2105
+ async create(request) {
2106
+ const response = await this.http.request({
2107
+ method: "POST",
2108
+ path: "/campaigns",
2109
+ body: {
2110
+ name: request.name,
2111
+ text: request.text,
2112
+ templateId: request.templateId,
2113
+ contactListIds: request.contactListIds
2114
+ }
2115
+ });
2116
+ return this.transformCampaign(response);
2117
+ }
2118
+ /**
2119
+ * List campaigns with optional filtering
2120
+ *
2121
+ * @param options - Filter and pagination options
2122
+ * @returns List of campaigns
2123
+ *
2124
+ * @example
2125
+ * ```typescript
2126
+ * // List all campaigns
2127
+ * const { campaigns } = await sendly.campaigns.list();
2128
+ *
2129
+ * // List only scheduled campaigns
2130
+ * const { campaigns } = await sendly.campaigns.list({ status: 'scheduled' });
2131
+ *
2132
+ * // Paginate
2133
+ * const { campaigns, total } = await sendly.campaigns.list({ limit: 10, offset: 20 });
2134
+ * ```
2135
+ */
2136
+ async list(options = {}) {
2137
+ const params = new URLSearchParams();
2138
+ if (options.limit) params.set("limit", String(options.limit));
2139
+ if (options.offset) params.set("offset", String(options.offset));
2140
+ if (options.status) params.set("status", options.status);
2141
+ const queryString = params.toString();
2142
+ const response = await this.http.request({
2143
+ method: "GET",
2144
+ path: `/campaigns${queryString ? `?${queryString}` : ""}`
2145
+ });
2146
+ return {
2147
+ campaigns: response.campaigns.map((c) => this.transformCampaign(c)),
2148
+ total: response.total,
2149
+ limit: response.limit,
2150
+ offset: response.offset
2151
+ };
2152
+ }
2153
+ /**
2154
+ * Get a campaign by ID
2155
+ *
2156
+ * @param id - Campaign ID
2157
+ * @returns The campaign
2158
+ *
2159
+ * @example
2160
+ * ```typescript
2161
+ * const campaign = await sendly.campaigns.get('cmp_xxx');
2162
+ * console.log(`Status: ${campaign.status}`);
2163
+ * console.log(`Delivered: ${campaign.deliveredCount}/${campaign.recipientCount}`);
2164
+ * ```
2165
+ */
2166
+ async get(id) {
2167
+ const response = await this.http.request({
2168
+ method: "GET",
2169
+ path: `/campaigns/${id}`
2170
+ });
2171
+ return this.transformCampaign(response);
2172
+ }
2173
+ /**
2174
+ * Update a campaign (draft or scheduled only)
2175
+ *
2176
+ * @param id - Campaign ID
2177
+ * @param request - Fields to update
2178
+ * @returns The updated campaign
2179
+ *
2180
+ * @example
2181
+ * ```typescript
2182
+ * const campaign = await sendly.campaigns.update('cmp_xxx', {
2183
+ * name: 'Updated Campaign Name',
2184
+ * text: 'New message text with {{variable}}'
2185
+ * });
2186
+ * ```
2187
+ */
2188
+ async update(id, request) {
2189
+ const response = await this.http.request({
2190
+ method: "PATCH",
2191
+ path: `/campaigns/${id}`,
2192
+ body: {
2193
+ ...request.name && { name: request.name },
2194
+ ...request.text && { text: request.text },
2195
+ ...request.templateId !== void 0 && {
2196
+ template_id: request.templateId
2197
+ },
2198
+ ...request.contactListIds && {
2199
+ contact_list_ids: request.contactListIds
2200
+ }
2201
+ }
2202
+ });
2203
+ return this.transformCampaign(response);
2204
+ }
2205
+ /**
2206
+ * Delete a campaign
2207
+ *
2208
+ * Only draft and cancelled campaigns can be deleted.
2209
+ *
2210
+ * @param id - Campaign ID
2211
+ *
2212
+ * @example
2213
+ * ```typescript
2214
+ * await sendly.campaigns.delete('cmp_xxx');
2215
+ * ```
2216
+ */
2217
+ async delete(id) {
2218
+ await this.http.request({
2219
+ method: "DELETE",
2220
+ path: `/campaigns/${id}`
2221
+ });
2222
+ }
2223
+ /**
2224
+ * Preview a campaign before sending
2225
+ *
2226
+ * Returns recipient count, credit estimate, and breakdown by country.
2227
+ *
2228
+ * @param id - Campaign ID
2229
+ * @returns Campaign preview with cost estimate
2230
+ *
2231
+ * @example
2232
+ * ```typescript
2233
+ * const preview = await sendly.campaigns.preview('cmp_xxx');
2234
+ *
2235
+ * console.log(`Recipients: ${preview.recipientCount}`);
2236
+ * console.log(`Estimated cost: ${preview.estimatedCredits} credits`);
2237
+ * console.log(`Your balance: ${preview.currentBalance} credits`);
2238
+ *
2239
+ * if (!preview.hasEnoughCredits) {
2240
+ * console.log('Not enough credits! Please top up.');
2241
+ * }
2242
+ * ```
2243
+ */
2244
+ async preview(id) {
2245
+ const response = await this.http.request({
2246
+ method: "GET",
2247
+ path: `/campaigns/${id}/preview`
2248
+ });
2249
+ return {
2250
+ id: response.id,
2251
+ recipientCount: response.recipient_count,
2252
+ estimatedSegments: response.estimated_segments,
2253
+ estimatedCredits: response.estimated_credits,
2254
+ currentBalance: response.current_balance,
2255
+ hasEnoughCredits: response.has_enough_credits,
2256
+ breakdown: response.breakdown?.map((b) => ({
2257
+ country: b.country,
2258
+ count: b.count,
2259
+ creditsPerMessage: b.credits_per_message,
2260
+ totalCredits: b.total_credits
2261
+ }))
2262
+ };
2263
+ }
2264
+ /**
2265
+ * Send a campaign immediately
2266
+ *
2267
+ * @param id - Campaign ID
2268
+ * @returns The updated campaign (status: sending)
2269
+ *
2270
+ * @example
2271
+ * ```typescript
2272
+ * const campaign = await sendly.campaigns.send('cmp_xxx');
2273
+ * console.log(`Campaign is now ${campaign.status}`);
2274
+ * ```
2275
+ */
2276
+ async send(id) {
2277
+ const response = await this.http.request({
2278
+ method: "POST",
2279
+ path: `/campaigns/${id}/send`
2280
+ });
2281
+ return this.transformCampaign(response);
2282
+ }
2283
+ /**
2284
+ * Schedule a campaign for later
2285
+ *
2286
+ * @param id - Campaign ID
2287
+ * @param request - Schedule details
2288
+ * @returns The updated campaign (status: scheduled)
2289
+ *
2290
+ * @example
2291
+ * ```typescript
2292
+ * const campaign = await sendly.campaigns.schedule('cmp_xxx', {
2293
+ * scheduledAt: '2024-01-15T10:00:00Z',
2294
+ * timezone: 'America/New_York'
2295
+ * });
2296
+ *
2297
+ * console.log(`Scheduled for ${campaign.scheduledAt}`);
2298
+ * ```
2299
+ */
2300
+ async schedule(id, request) {
2301
+ const response = await this.http.request({
2302
+ method: "POST",
2303
+ path: `/campaigns/${id}/schedule`,
2304
+ body: {
2305
+ scheduledAt: request.scheduledAt,
2306
+ timezone: request.timezone
2307
+ }
2308
+ });
2309
+ return this.transformCampaign(response);
2310
+ }
2311
+ /**
2312
+ * Cancel a scheduled campaign
2313
+ *
2314
+ * @param id - Campaign ID
2315
+ * @returns The updated campaign (status: cancelled)
2316
+ *
2317
+ * @example
2318
+ * ```typescript
2319
+ * const campaign = await sendly.campaigns.cancel('cmp_xxx');
2320
+ * console.log(`Campaign cancelled`);
2321
+ * ```
2322
+ */
2323
+ async cancel(id) {
2324
+ const response = await this.http.request({
2325
+ method: "POST",
2326
+ path: `/campaigns/${id}/cancel`
2327
+ });
2328
+ return this.transformCampaign(response);
2329
+ }
2330
+ /**
2331
+ * Clone a campaign
2332
+ *
2333
+ * Creates a new draft campaign with the same settings.
2334
+ *
2335
+ * @param id - Campaign ID to clone
2336
+ * @returns The new campaign (as draft)
2337
+ *
2338
+ * @example
2339
+ * ```typescript
2340
+ * const cloned = await sendly.campaigns.clone('cmp_xxx');
2341
+ * console.log(`Created clone: ${cloned.id}`);
2342
+ * ```
2343
+ */
2344
+ async clone(id) {
2345
+ const response = await this.http.request({
2346
+ method: "POST",
2347
+ path: `/campaigns/${id}/clone`
2348
+ });
2349
+ return this.transformCampaign(response);
2350
+ }
2351
+ transformCampaign(raw) {
2352
+ return {
2353
+ id: raw.id,
2354
+ name: raw.name,
2355
+ text: raw.text,
2356
+ templateId: raw.template_id,
2357
+ contactListIds: raw.contact_list_ids || [],
2358
+ status: raw.status,
2359
+ recipientCount: raw.recipient_count || 0,
2360
+ sentCount: raw.sent_count || 0,
2361
+ deliveredCount: raw.delivered_count || 0,
2362
+ failedCount: raw.failed_count || 0,
2363
+ estimatedCredits: raw.estimated_credits || 0,
2364
+ creditsUsed: raw.credits_used || 0,
2365
+ scheduledAt: raw.scheduled_at,
2366
+ timezone: raw.timezone,
2367
+ startedAt: raw.started_at,
2368
+ completedAt: raw.completed_at,
2369
+ createdAt: raw.created_at,
2370
+ updatedAt: raw.updated_at
2371
+ };
2372
+ }
2373
+ };
2374
+
2375
+ // src/resources/contacts.ts
2376
+ var ContactsResource = class {
2377
+ http;
2378
+ lists;
2379
+ constructor(http) {
2380
+ this.http = http;
2381
+ this.lists = new ContactListsResource(http);
2382
+ }
2383
+ /**
2384
+ * List contacts with optional filtering
2385
+ *
2386
+ * @param options - Filter and pagination options
2387
+ * @returns List of contacts
2388
+ *
2389
+ * @example
2390
+ * ```typescript
2391
+ * // List all contacts
2392
+ * const { contacts, total } = await sendly.contacts.list();
2393
+ *
2394
+ * // Search contacts
2395
+ * const { contacts } = await sendly.contacts.list({ search: 'john' });
2396
+ *
2397
+ * // List contacts in a specific list
2398
+ * const { contacts } = await sendly.contacts.list({ listId: 'lst_xxx' });
2399
+ * ```
2400
+ */
2401
+ async list(options = {}) {
2402
+ const params = new URLSearchParams();
2403
+ if (options.limit) params.set("limit", String(options.limit));
2404
+ if (options.offset) params.set("offset", String(options.offset));
2405
+ if (options.search) params.set("search", options.search);
2406
+ if (options.listId) params.set("list_id", options.listId);
2407
+ const queryString = params.toString();
2408
+ const response = await this.http.request({
2409
+ method: "GET",
2410
+ path: `/contacts${queryString ? `?${queryString}` : ""}`
2411
+ });
2412
+ return {
2413
+ contacts: response.contacts.map((c) => this.transformContact(c)),
2414
+ total: response.total,
2415
+ limit: response.limit,
2416
+ offset: response.offset
2417
+ };
2418
+ }
2419
+ /**
2420
+ * Get a contact by ID
2421
+ *
2422
+ * @param id - Contact ID
2423
+ * @returns The contact with associated lists
2424
+ *
2425
+ * @example
2426
+ * ```typescript
2427
+ * const contact = await sendly.contacts.get('cnt_xxx');
2428
+ * console.log(`${contact.name}: ${contact.phoneNumber}`);
2429
+ * console.log(`In ${contact.lists?.length || 0} lists`);
2430
+ * ```
2431
+ */
2432
+ async get(id) {
2433
+ const response = await this.http.request({
2434
+ method: "GET",
2435
+ path: `/contacts/${id}`
2436
+ });
2437
+ return this.transformContact(response);
2438
+ }
2439
+ /**
2440
+ * Create a new contact
2441
+ *
2442
+ * @param request - Contact details
2443
+ * @returns The created contact
2444
+ *
2445
+ * @example
2446
+ * ```typescript
2447
+ * const contact = await sendly.contacts.create({
2448
+ * phoneNumber: '+15551234567',
2449
+ * name: 'Jane Smith',
2450
+ * email: 'jane@example.com',
2451
+ * metadata: { source: 'signup_form', plan: 'premium' }
2452
+ * });
2453
+ * ```
2454
+ */
2455
+ async create(request) {
2456
+ const response = await this.http.request({
2457
+ method: "POST",
2458
+ path: "/contacts",
2459
+ body: {
2460
+ phone_number: request.phoneNumber,
2461
+ name: request.name,
2462
+ email: request.email,
2463
+ metadata: request.metadata
2464
+ }
2465
+ });
2466
+ return this.transformContact(response);
2467
+ }
2468
+ /**
2469
+ * Update a contact
2470
+ *
2471
+ * @param id - Contact ID
2472
+ * @param request - Fields to update
2473
+ * @returns The updated contact
2474
+ *
2475
+ * @example
2476
+ * ```typescript
2477
+ * const contact = await sendly.contacts.update('cnt_xxx', {
2478
+ * name: 'Jane Doe',
2479
+ * metadata: { plan: 'enterprise' }
2480
+ * });
2481
+ * ```
2482
+ */
2483
+ async update(id, request) {
2484
+ const response = await this.http.request({
2485
+ method: "PATCH",
2486
+ path: `/contacts/${id}`,
2487
+ body: {
2488
+ name: request.name,
2489
+ email: request.email,
2490
+ metadata: request.metadata
2491
+ }
2492
+ });
2493
+ return this.transformContact(response);
2494
+ }
2495
+ /**
2496
+ * Delete a contact
2497
+ *
2498
+ * @param id - Contact ID
2499
+ *
2500
+ * @example
2501
+ * ```typescript
2502
+ * await sendly.contacts.delete('cnt_xxx');
2503
+ * ```
2504
+ */
2505
+ async delete(id) {
2506
+ await this.http.request({
2507
+ method: "DELETE",
2508
+ path: `/contacts/${id}`
2509
+ });
2510
+ }
2511
+ async import(request) {
2512
+ const body = {
2513
+ contacts: request.contacts
2514
+ };
2515
+ if (request.listId) body.listId = request.listId;
2516
+ if (request.optedInAt) body.optedInAt = request.optedInAt;
2517
+ return this.http.request({
2518
+ method: "POST",
2519
+ path: "/contacts/import",
2520
+ body
2521
+ });
2522
+ }
2523
+ transformContact(raw) {
2524
+ return {
2525
+ id: raw.id,
2526
+ phoneNumber: raw.phone_number,
2527
+ name: raw.name,
2528
+ email: raw.email,
2529
+ metadata: raw.metadata,
2530
+ optedOut: raw.opted_out,
2531
+ createdAt: raw.created_at,
2532
+ updatedAt: raw.updated_at,
2533
+ lists: raw.lists?.map((l) => ({ id: l.id, name: l.name }))
2534
+ };
2535
+ }
2536
+ };
2537
+ var ContactListsResource = class {
2538
+ http;
2539
+ constructor(http) {
2540
+ this.http = http;
2541
+ }
2542
+ /**
2543
+ * List all contact lists
2544
+ *
2545
+ * @returns All contact lists
2546
+ *
2547
+ * @example
2548
+ * ```typescript
2549
+ * const { lists } = await sendly.contacts.lists.list();
2550
+ * for (const list of lists) {
2551
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2552
+ * }
2553
+ * ```
2554
+ */
2555
+ async list() {
2556
+ const response = await this.http.request({
2557
+ method: "GET",
2558
+ path: "/contact-lists"
2559
+ });
2560
+ return {
2561
+ lists: response.lists.map((l) => this.transformList(l))
2562
+ };
2563
+ }
2564
+ /**
2565
+ * Get a contact list by ID
2566
+ *
2567
+ * @param id - Contact list ID
2568
+ * @param options - Pagination options for contacts
2569
+ * @returns The contact list with members
2570
+ *
2571
+ * @example
2572
+ * ```typescript
2573
+ * const list = await sendly.contacts.lists.get('lst_xxx');
2574
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2575
+ *
2576
+ * // Get list with paginated contacts
2577
+ * const list = await sendly.contacts.lists.get('lst_xxx', { limit: 100 });
2578
+ * console.log(`Showing ${list.contacts?.length} of ${list.contactsTotal}`);
2579
+ * ```
2580
+ */
2581
+ async get(id, options = {}) {
2582
+ const params = new URLSearchParams();
2583
+ if (options.limit) params.set("limit", String(options.limit));
2584
+ if (options.offset) params.set("offset", String(options.offset));
2585
+ const queryString = params.toString();
2586
+ const response = await this.http.request({
2587
+ method: "GET",
2588
+ path: `/contact-lists/${id}${queryString ? `?${queryString}` : ""}`
2589
+ });
2590
+ return this.transformList(response);
2591
+ }
2592
+ /**
2593
+ * Create a new contact list
2594
+ *
2595
+ * @param request - List details
2596
+ * @returns The created list
2597
+ *
2598
+ * @example
2599
+ * ```typescript
2600
+ * const list = await sendly.contacts.lists.create({
2601
+ * name: 'VIP Customers',
2602
+ * description: 'High-value customers for priority messaging'
2603
+ * });
2604
+ * ```
2605
+ */
2606
+ async create(request) {
2607
+ const response = await this.http.request({
2608
+ method: "POST",
2609
+ path: "/contact-lists",
2610
+ body: {
2611
+ name: request.name,
2612
+ description: request.description
2613
+ }
2614
+ });
2615
+ return this.transformList(response);
2616
+ }
2617
+ /**
2618
+ * Update a contact list
2619
+ *
2620
+ * @param id - Contact list ID
2621
+ * @param request - Fields to update
2622
+ * @returns The updated list
2623
+ *
2624
+ * @example
2625
+ * ```typescript
2626
+ * const list = await sendly.contacts.lists.update('lst_xxx', {
2627
+ * name: 'Premium Subscribers',
2628
+ * description: 'Updated description'
2629
+ * });
2630
+ * ```
2631
+ */
2632
+ async update(id, request) {
2633
+ const response = await this.http.request({
2634
+ method: "PATCH",
2635
+ path: `/contact-lists/${id}`,
2636
+ body: {
2637
+ name: request.name,
2638
+ description: request.description
2639
+ }
2640
+ });
2641
+ return this.transformList(response);
2642
+ }
2643
+ /**
2644
+ * Delete a contact list
2645
+ *
2646
+ * This does not delete the contacts in the list.
2647
+ *
2648
+ * @param id - Contact list ID
2649
+ *
2650
+ * @example
2651
+ * ```typescript
2652
+ * await sendly.contacts.lists.delete('lst_xxx');
2653
+ * ```
2654
+ */
2655
+ async delete(id) {
2656
+ await this.http.request({
2657
+ method: "DELETE",
2658
+ path: `/contact-lists/${id}`
2659
+ });
2660
+ }
2661
+ /**
2662
+ * Add contacts to a list
2663
+ *
2664
+ * @param listId - Contact list ID
2665
+ * @param contactIds - Array of contact IDs to add
2666
+ * @returns Number of contacts added
2667
+ *
2668
+ * @example
2669
+ * ```typescript
2670
+ * const result = await sendly.contacts.lists.addContacts('lst_xxx', [
2671
+ * 'cnt_abc',
2672
+ * 'cnt_def',
2673
+ * 'cnt_ghi'
2674
+ * ]);
2675
+ * console.log(`Added ${result.addedCount} contacts`);
2676
+ * ```
2677
+ */
2678
+ async addContacts(listId, contactIds) {
2679
+ const response = await this.http.request({
2680
+ method: "POST",
2681
+ path: `/contact-lists/${listId}/contacts`,
2682
+ body: { contact_ids: contactIds }
2683
+ });
2684
+ return { addedCount: response.added_count };
2685
+ }
2686
+ /**
2687
+ * Remove a contact from a list
2688
+ *
2689
+ * @param listId - Contact list ID
2690
+ * @param contactId - Contact ID to remove
2691
+ *
2692
+ * @example
2693
+ * ```typescript
2694
+ * await sendly.contacts.lists.removeContact('lst_xxx', 'cnt_abc');
2695
+ * ```
2696
+ */
2697
+ async removeContact(listId, contactId) {
2698
+ await this.http.request({
2699
+ method: "DELETE",
2700
+ path: `/contact-lists/${listId}/contacts/${contactId}`
2701
+ });
2702
+ }
2703
+ transformList(raw) {
2704
+ return {
2705
+ id: raw.id,
2706
+ name: raw.name,
2707
+ description: raw.description,
2708
+ contactCount: raw.contact_count || 0,
2709
+ createdAt: raw.created_at,
2710
+ updatedAt: raw.updated_at,
2711
+ contacts: raw.contacts?.map((c) => ({
2712
+ id: c.id,
2713
+ phoneNumber: c.phone_number,
2714
+ name: c.name,
2715
+ email: c.email
2716
+ })),
2717
+ contactsTotal: raw.contacts_total
2718
+ };
2719
+ }
2720
+ };
2721
+
1994
2722
  // src/client.ts
1995
2723
  var DEFAULT_BASE_URL2 = "https://sendly.live/api/v1";
1996
2724
  var DEFAULT_TIMEOUT2 = 3e4;
@@ -2088,6 +2816,47 @@ var Sendly = class {
2088
2816
  * ```
2089
2817
  */
2090
2818
  templates;
2819
+ /**
2820
+ * Campaigns API resource - Bulk SMS campaign management
2821
+ *
2822
+ * @example
2823
+ * ```typescript
2824
+ * // Create a campaign
2825
+ * const campaign = await sendly.campaigns.create({
2826
+ * name: 'Welcome Campaign',
2827
+ * text: 'Hello {{name}}!',
2828
+ * contactListIds: ['lst_xxx']
2829
+ * });
2830
+ *
2831
+ * // Preview cost
2832
+ * const preview = await sendly.campaigns.preview(campaign.id);
2833
+ * console.log(`Cost: ${preview.estimatedCredits} credits`);
2834
+ *
2835
+ * // Send or schedule
2836
+ * await sendly.campaigns.send(campaign.id);
2837
+ * ```
2838
+ */
2839
+ campaigns;
2840
+ /**
2841
+ * Contacts API resource - Contact and list management
2842
+ *
2843
+ * @example
2844
+ * ```typescript
2845
+ * // Create a contact
2846
+ * const contact = await sendly.contacts.create({
2847
+ * phoneNumber: '+15551234567',
2848
+ * name: 'John Doe'
2849
+ * });
2850
+ *
2851
+ * // Create a list and add contacts
2852
+ * const list = await sendly.contacts.lists.create({ name: 'VIPs' });
2853
+ * await sendly.contacts.lists.addContacts(list.id, [contact.id]);
2854
+ *
2855
+ * // List all contacts
2856
+ * const { contacts } = await sendly.contacts.list();
2857
+ * ```
2858
+ */
2859
+ contacts;
2091
2860
  http;
2092
2861
  config;
2093
2862
  /**
@@ -2122,6 +2891,8 @@ var Sendly = class {
2122
2891
  this.account = new AccountResource(this.http);
2123
2892
  this.verify = new VerifyResource(this.http);
2124
2893
  this.templates = new TemplatesResource(this.http);
2894
+ this.campaigns = new CampaignsResource(this.http);
2895
+ this.contacts = new ContactsResource(this.http);
2125
2896
  }
2126
2897
  /**
2127
2898
  * Check if the client is using a test API key