@sendly/node 3.12.2 → 3.13.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.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,632 @@ 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
+ transformContact(raw) {
2512
+ return {
2513
+ id: raw.id,
2514
+ phoneNumber: raw.phone_number,
2515
+ name: raw.name,
2516
+ email: raw.email,
2517
+ metadata: raw.metadata,
2518
+ optedOut: raw.opted_out,
2519
+ createdAt: raw.created_at,
2520
+ updatedAt: raw.updated_at,
2521
+ lists: raw.lists?.map((l) => ({ id: l.id, name: l.name }))
2522
+ };
2523
+ }
2524
+ };
2525
+ var ContactListsResource = class {
2526
+ http;
2527
+ constructor(http) {
2528
+ this.http = http;
2529
+ }
2530
+ /**
2531
+ * List all contact lists
2532
+ *
2533
+ * @returns All contact lists
2534
+ *
2535
+ * @example
2536
+ * ```typescript
2537
+ * const { lists } = await sendly.contacts.lists.list();
2538
+ * for (const list of lists) {
2539
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2540
+ * }
2541
+ * ```
2542
+ */
2543
+ async list() {
2544
+ const response = await this.http.request({
2545
+ method: "GET",
2546
+ path: "/contact-lists"
2547
+ });
2548
+ return {
2549
+ lists: response.lists.map((l) => this.transformList(l))
2550
+ };
2551
+ }
2552
+ /**
2553
+ * Get a contact list by ID
2554
+ *
2555
+ * @param id - Contact list ID
2556
+ * @param options - Pagination options for contacts
2557
+ * @returns The contact list with members
2558
+ *
2559
+ * @example
2560
+ * ```typescript
2561
+ * const list = await sendly.contacts.lists.get('lst_xxx');
2562
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2563
+ *
2564
+ * // Get list with paginated contacts
2565
+ * const list = await sendly.contacts.lists.get('lst_xxx', { limit: 100 });
2566
+ * console.log(`Showing ${list.contacts?.length} of ${list.contactsTotal}`);
2567
+ * ```
2568
+ */
2569
+ async get(id, options = {}) {
2570
+ const params = new URLSearchParams();
2571
+ if (options.limit) params.set("limit", String(options.limit));
2572
+ if (options.offset) params.set("offset", String(options.offset));
2573
+ const queryString = params.toString();
2574
+ const response = await this.http.request({
2575
+ method: "GET",
2576
+ path: `/contact-lists/${id}${queryString ? `?${queryString}` : ""}`
2577
+ });
2578
+ return this.transformList(response);
2579
+ }
2580
+ /**
2581
+ * Create a new contact list
2582
+ *
2583
+ * @param request - List details
2584
+ * @returns The created list
2585
+ *
2586
+ * @example
2587
+ * ```typescript
2588
+ * const list = await sendly.contacts.lists.create({
2589
+ * name: 'VIP Customers',
2590
+ * description: 'High-value customers for priority messaging'
2591
+ * });
2592
+ * ```
2593
+ */
2594
+ async create(request) {
2595
+ const response = await this.http.request({
2596
+ method: "POST",
2597
+ path: "/contact-lists",
2598
+ body: {
2599
+ name: request.name,
2600
+ description: request.description
2601
+ }
2602
+ });
2603
+ return this.transformList(response);
2604
+ }
2605
+ /**
2606
+ * Update a contact list
2607
+ *
2608
+ * @param id - Contact list ID
2609
+ * @param request - Fields to update
2610
+ * @returns The updated list
2611
+ *
2612
+ * @example
2613
+ * ```typescript
2614
+ * const list = await sendly.contacts.lists.update('lst_xxx', {
2615
+ * name: 'Premium Subscribers',
2616
+ * description: 'Updated description'
2617
+ * });
2618
+ * ```
2619
+ */
2620
+ async update(id, request) {
2621
+ const response = await this.http.request({
2622
+ method: "PATCH",
2623
+ path: `/contact-lists/${id}`,
2624
+ body: {
2625
+ name: request.name,
2626
+ description: request.description
2627
+ }
2628
+ });
2629
+ return this.transformList(response);
2630
+ }
2631
+ /**
2632
+ * Delete a contact list
2633
+ *
2634
+ * This does not delete the contacts in the list.
2635
+ *
2636
+ * @param id - Contact list ID
2637
+ *
2638
+ * @example
2639
+ * ```typescript
2640
+ * await sendly.contacts.lists.delete('lst_xxx');
2641
+ * ```
2642
+ */
2643
+ async delete(id) {
2644
+ await this.http.request({
2645
+ method: "DELETE",
2646
+ path: `/contact-lists/${id}`
2647
+ });
2648
+ }
2649
+ /**
2650
+ * Add contacts to a list
2651
+ *
2652
+ * @param listId - Contact list ID
2653
+ * @param contactIds - Array of contact IDs to add
2654
+ * @returns Number of contacts added
2655
+ *
2656
+ * @example
2657
+ * ```typescript
2658
+ * const result = await sendly.contacts.lists.addContacts('lst_xxx', [
2659
+ * 'cnt_abc',
2660
+ * 'cnt_def',
2661
+ * 'cnt_ghi'
2662
+ * ]);
2663
+ * console.log(`Added ${result.addedCount} contacts`);
2664
+ * ```
2665
+ */
2666
+ async addContacts(listId, contactIds) {
2667
+ const response = await this.http.request({
2668
+ method: "POST",
2669
+ path: `/contact-lists/${listId}/contacts`,
2670
+ body: { contact_ids: contactIds }
2671
+ });
2672
+ return { addedCount: response.added_count };
2673
+ }
2674
+ /**
2675
+ * Remove a contact from a list
2676
+ *
2677
+ * @param listId - Contact list ID
2678
+ * @param contactId - Contact ID to remove
2679
+ *
2680
+ * @example
2681
+ * ```typescript
2682
+ * await sendly.contacts.lists.removeContact('lst_xxx', 'cnt_abc');
2683
+ * ```
2684
+ */
2685
+ async removeContact(listId, contactId) {
2686
+ await this.http.request({
2687
+ method: "DELETE",
2688
+ path: `/contact-lists/${listId}/contacts/${contactId}`
2689
+ });
2690
+ }
2691
+ transformList(raw) {
2692
+ return {
2693
+ id: raw.id,
2694
+ name: raw.name,
2695
+ description: raw.description,
2696
+ contactCount: raw.contact_count || 0,
2697
+ createdAt: raw.created_at,
2698
+ updatedAt: raw.updated_at,
2699
+ contacts: raw.contacts?.map((c) => ({
2700
+ id: c.id,
2701
+ phoneNumber: c.phone_number,
2702
+ name: c.name,
2703
+ email: c.email
2704
+ })),
2705
+ contactsTotal: raw.contacts_total
2706
+ };
2707
+ }
2708
+ };
2709
+
1994
2710
  // src/client.ts
1995
2711
  var DEFAULT_BASE_URL2 = "https://sendly.live/api/v1";
1996
2712
  var DEFAULT_TIMEOUT2 = 3e4;
@@ -2088,6 +2804,47 @@ var Sendly = class {
2088
2804
  * ```
2089
2805
  */
2090
2806
  templates;
2807
+ /**
2808
+ * Campaigns API resource - Bulk SMS campaign management
2809
+ *
2810
+ * @example
2811
+ * ```typescript
2812
+ * // Create a campaign
2813
+ * const campaign = await sendly.campaigns.create({
2814
+ * name: 'Welcome Campaign',
2815
+ * text: 'Hello {{name}}!',
2816
+ * contactListIds: ['lst_xxx']
2817
+ * });
2818
+ *
2819
+ * // Preview cost
2820
+ * const preview = await sendly.campaigns.preview(campaign.id);
2821
+ * console.log(`Cost: ${preview.estimatedCredits} credits`);
2822
+ *
2823
+ * // Send or schedule
2824
+ * await sendly.campaigns.send(campaign.id);
2825
+ * ```
2826
+ */
2827
+ campaigns;
2828
+ /**
2829
+ * Contacts API resource - Contact and list management
2830
+ *
2831
+ * @example
2832
+ * ```typescript
2833
+ * // Create a contact
2834
+ * const contact = await sendly.contacts.create({
2835
+ * phoneNumber: '+15551234567',
2836
+ * name: 'John Doe'
2837
+ * });
2838
+ *
2839
+ * // Create a list and add contacts
2840
+ * const list = await sendly.contacts.lists.create({ name: 'VIPs' });
2841
+ * await sendly.contacts.lists.addContacts(list.id, [contact.id]);
2842
+ *
2843
+ * // List all contacts
2844
+ * const { contacts } = await sendly.contacts.list();
2845
+ * ```
2846
+ */
2847
+ contacts;
2091
2848
  http;
2092
2849
  config;
2093
2850
  /**
@@ -2122,6 +2879,8 @@ var Sendly = class {
2122
2879
  this.account = new AccountResource(this.http);
2123
2880
  this.verify = new VerifyResource(this.http);
2124
2881
  this.templates = new TemplatesResource(this.http);
2882
+ this.campaigns = new CampaignsResource(this.http);
2883
+ this.contacts = new ContactsResource(this.http);
2125
2884
  }
2126
2885
  /**
2127
2886
  * Check if the client is using a test API key