@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.mjs CHANGED
@@ -1460,6 +1460,65 @@ var AccountResource = class {
1460
1460
  path: `/account/keys/${encodeURIComponent(id)}`
1461
1461
  });
1462
1462
  }
1463
+ /**
1464
+ * Rename an API key
1465
+ *
1466
+ * @param id - API key ID
1467
+ * @param name - New name for the API key
1468
+ * @returns Updated API key
1469
+ *
1470
+ * @example
1471
+ * ```typescript
1472
+ * const key = await sendly.account.renameApiKey('key_xxx', 'Production Key');
1473
+ * console.log(`Renamed to: ${key.name}`);
1474
+ * ```
1475
+ */
1476
+ async renameApiKey(id, name) {
1477
+ if (!id) {
1478
+ throw new Error("API key ID is required");
1479
+ }
1480
+ if (!name) {
1481
+ throw new Error("New name is required");
1482
+ }
1483
+ const key = await this.http.request({
1484
+ method: "PATCH",
1485
+ path: `/account/keys/${encodeURIComponent(id)}/rename`,
1486
+ body: { name }
1487
+ });
1488
+ return key;
1489
+ }
1490
+ /**
1491
+ * Rotate an API key
1492
+ *
1493
+ * Creates a new key while optionally keeping the old one active for a grace period.
1494
+ *
1495
+ * @param id - API key ID to rotate
1496
+ * @param options - Rotation options
1497
+ * @returns New API key with the full key value
1498
+ *
1499
+ * @example
1500
+ * ```typescript
1501
+ * // Rotate immediately (old key stops working instantly)
1502
+ * const { newKey, key } = await sendly.account.rotateApiKey('key_xxx');
1503
+ * console.log(`New key: ${key}`); // Save this!
1504
+ *
1505
+ * // Rotate with 24-hour grace period (both keys work for 24h)
1506
+ * const { newKey, key } = await sendly.account.rotateApiKey('key_xxx', {
1507
+ * gracePeriodHours: 24
1508
+ * });
1509
+ * ```
1510
+ */
1511
+ async rotateApiKey(id, options) {
1512
+ if (!id) {
1513
+ throw new Error("API key ID is required");
1514
+ }
1515
+ const response = await this.http.request({
1516
+ method: "POST",
1517
+ path: `/account/keys/${encodeURIComponent(id)}/rotate`,
1518
+ body: options?.gracePeriodHours ? { gracePeriodHours: options.gracePeriodHours } : {}
1519
+ });
1520
+ return response;
1521
+ }
1463
1522
  };
1464
1523
 
1465
1524
  // src/resources/verify.ts
@@ -1910,6 +1969,37 @@ var TemplatesResource = class {
1910
1969
  path: `/templates/${id}`
1911
1970
  });
1912
1971
  }
1972
+ /**
1973
+ * Clone a template
1974
+ *
1975
+ * Creates a copy of an existing template (including presets).
1976
+ *
1977
+ * @param id - Template ID to clone
1978
+ * @param options - Optional clone options
1979
+ * @returns The cloned template (as draft)
1980
+ *
1981
+ * @example
1982
+ * ```typescript
1983
+ * // Clone a preset template
1984
+ * const myOtp = await sendly.templates.clone('tpl_preset_otp', {
1985
+ * name: 'My Custom OTP'
1986
+ * });
1987
+ *
1988
+ * // Modify and publish
1989
+ * await sendly.templates.update(myOtp.id, {
1990
+ * text: 'Your {{app_name}} code: {{code}}. Expires in 5 min.'
1991
+ * });
1992
+ * await sendly.templates.publish(myOtp.id);
1993
+ * ```
1994
+ */
1995
+ async clone(id, options) {
1996
+ const response = await this.http.request({
1997
+ method: "POST",
1998
+ path: `/templates/${id}/clone`,
1999
+ body: options?.name ? { name: options.name } : {}
2000
+ });
2001
+ return this.transformTemplate(response);
2002
+ }
1913
2003
  transformTemplate(t) {
1914
2004
  return {
1915
2005
  id: t.id,
@@ -1931,6 +2021,644 @@ var TemplatesResource = class {
1931
2021
  }
1932
2022
  };
1933
2023
 
2024
+ // src/resources/campaigns.ts
2025
+ var CampaignsResource = class {
2026
+ http;
2027
+ constructor(http) {
2028
+ this.http = http;
2029
+ }
2030
+ /**
2031
+ * Create a new campaign
2032
+ *
2033
+ * @param request - Campaign details
2034
+ * @returns The created campaign (as draft)
2035
+ *
2036
+ * @example
2037
+ * ```typescript
2038
+ * const campaign = await sendly.campaigns.create({
2039
+ * name: 'Black Friday Sale',
2040
+ * text: 'Hi {{name}}! 50% off everything today only. Shop now!',
2041
+ * contactListIds: ['lst_customers', 'lst_subscribers']
2042
+ * });
2043
+ * ```
2044
+ */
2045
+ async create(request) {
2046
+ const response = await this.http.request({
2047
+ method: "POST",
2048
+ path: "/campaigns",
2049
+ body: {
2050
+ name: request.name,
2051
+ text: request.text,
2052
+ templateId: request.templateId,
2053
+ contactListIds: request.contactListIds
2054
+ }
2055
+ });
2056
+ return this.transformCampaign(response);
2057
+ }
2058
+ /**
2059
+ * List campaigns with optional filtering
2060
+ *
2061
+ * @param options - Filter and pagination options
2062
+ * @returns List of campaigns
2063
+ *
2064
+ * @example
2065
+ * ```typescript
2066
+ * // List all campaigns
2067
+ * const { campaigns } = await sendly.campaigns.list();
2068
+ *
2069
+ * // List only scheduled campaigns
2070
+ * const { campaigns } = await sendly.campaigns.list({ status: 'scheduled' });
2071
+ *
2072
+ * // Paginate
2073
+ * const { campaigns, total } = await sendly.campaigns.list({ limit: 10, offset: 20 });
2074
+ * ```
2075
+ */
2076
+ async list(options = {}) {
2077
+ const params = new URLSearchParams();
2078
+ if (options.limit) params.set("limit", String(options.limit));
2079
+ if (options.offset) params.set("offset", String(options.offset));
2080
+ if (options.status) params.set("status", options.status);
2081
+ const queryString = params.toString();
2082
+ const response = await this.http.request({
2083
+ method: "GET",
2084
+ path: `/campaigns${queryString ? `?${queryString}` : ""}`
2085
+ });
2086
+ return {
2087
+ campaigns: response.campaigns.map((c) => this.transformCampaign(c)),
2088
+ total: response.total,
2089
+ limit: response.limit,
2090
+ offset: response.offset
2091
+ };
2092
+ }
2093
+ /**
2094
+ * Get a campaign by ID
2095
+ *
2096
+ * @param id - Campaign ID
2097
+ * @returns The campaign
2098
+ *
2099
+ * @example
2100
+ * ```typescript
2101
+ * const campaign = await sendly.campaigns.get('cmp_xxx');
2102
+ * console.log(`Status: ${campaign.status}`);
2103
+ * console.log(`Delivered: ${campaign.deliveredCount}/${campaign.recipientCount}`);
2104
+ * ```
2105
+ */
2106
+ async get(id) {
2107
+ const response = await this.http.request({
2108
+ method: "GET",
2109
+ path: `/campaigns/${id}`
2110
+ });
2111
+ return this.transformCampaign(response);
2112
+ }
2113
+ /**
2114
+ * Update a campaign (draft or scheduled only)
2115
+ *
2116
+ * @param id - Campaign ID
2117
+ * @param request - Fields to update
2118
+ * @returns The updated campaign
2119
+ *
2120
+ * @example
2121
+ * ```typescript
2122
+ * const campaign = await sendly.campaigns.update('cmp_xxx', {
2123
+ * name: 'Updated Campaign Name',
2124
+ * text: 'New message text with {{variable}}'
2125
+ * });
2126
+ * ```
2127
+ */
2128
+ async update(id, request) {
2129
+ const response = await this.http.request({
2130
+ method: "PATCH",
2131
+ path: `/campaigns/${id}`,
2132
+ body: {
2133
+ ...request.name && { name: request.name },
2134
+ ...request.text && { text: request.text },
2135
+ ...request.templateId !== void 0 && {
2136
+ template_id: request.templateId
2137
+ },
2138
+ ...request.contactListIds && {
2139
+ contact_list_ids: request.contactListIds
2140
+ }
2141
+ }
2142
+ });
2143
+ return this.transformCampaign(response);
2144
+ }
2145
+ /**
2146
+ * Delete a campaign
2147
+ *
2148
+ * Only draft and cancelled campaigns can be deleted.
2149
+ *
2150
+ * @param id - Campaign ID
2151
+ *
2152
+ * @example
2153
+ * ```typescript
2154
+ * await sendly.campaigns.delete('cmp_xxx');
2155
+ * ```
2156
+ */
2157
+ async delete(id) {
2158
+ await this.http.request({
2159
+ method: "DELETE",
2160
+ path: `/campaigns/${id}`
2161
+ });
2162
+ }
2163
+ /**
2164
+ * Preview a campaign before sending
2165
+ *
2166
+ * Returns recipient count, credit estimate, and breakdown by country.
2167
+ *
2168
+ * @param id - Campaign ID
2169
+ * @returns Campaign preview with cost estimate
2170
+ *
2171
+ * @example
2172
+ * ```typescript
2173
+ * const preview = await sendly.campaigns.preview('cmp_xxx');
2174
+ *
2175
+ * console.log(`Recipients: ${preview.recipientCount}`);
2176
+ * console.log(`Estimated cost: ${preview.estimatedCredits} credits`);
2177
+ * console.log(`Your balance: ${preview.currentBalance} credits`);
2178
+ *
2179
+ * if (!preview.hasEnoughCredits) {
2180
+ * console.log('Not enough credits! Please top up.');
2181
+ * }
2182
+ * ```
2183
+ */
2184
+ async preview(id) {
2185
+ const response = await this.http.request({
2186
+ method: "GET",
2187
+ path: `/campaigns/${id}/preview`
2188
+ });
2189
+ return {
2190
+ id: response.id,
2191
+ recipientCount: response.recipient_count,
2192
+ estimatedSegments: response.estimated_segments,
2193
+ estimatedCredits: response.estimated_credits,
2194
+ currentBalance: response.current_balance,
2195
+ hasEnoughCredits: response.has_enough_credits,
2196
+ breakdown: response.breakdown?.map((b) => ({
2197
+ country: b.country,
2198
+ count: b.count,
2199
+ creditsPerMessage: b.credits_per_message,
2200
+ totalCredits: b.total_credits
2201
+ }))
2202
+ };
2203
+ }
2204
+ /**
2205
+ * Send a campaign immediately
2206
+ *
2207
+ * @param id - Campaign ID
2208
+ * @returns The updated campaign (status: sending)
2209
+ *
2210
+ * @example
2211
+ * ```typescript
2212
+ * const campaign = await sendly.campaigns.send('cmp_xxx');
2213
+ * console.log(`Campaign is now ${campaign.status}`);
2214
+ * ```
2215
+ */
2216
+ async send(id) {
2217
+ const response = await this.http.request({
2218
+ method: "POST",
2219
+ path: `/campaigns/${id}/send`
2220
+ });
2221
+ return this.transformCampaign(response);
2222
+ }
2223
+ /**
2224
+ * Schedule a campaign for later
2225
+ *
2226
+ * @param id - Campaign ID
2227
+ * @param request - Schedule details
2228
+ * @returns The updated campaign (status: scheduled)
2229
+ *
2230
+ * @example
2231
+ * ```typescript
2232
+ * const campaign = await sendly.campaigns.schedule('cmp_xxx', {
2233
+ * scheduledAt: '2024-01-15T10:00:00Z',
2234
+ * timezone: 'America/New_York'
2235
+ * });
2236
+ *
2237
+ * console.log(`Scheduled for ${campaign.scheduledAt}`);
2238
+ * ```
2239
+ */
2240
+ async schedule(id, request) {
2241
+ const response = await this.http.request({
2242
+ method: "POST",
2243
+ path: `/campaigns/${id}/schedule`,
2244
+ body: {
2245
+ scheduledAt: request.scheduledAt,
2246
+ timezone: request.timezone
2247
+ }
2248
+ });
2249
+ return this.transformCampaign(response);
2250
+ }
2251
+ /**
2252
+ * Cancel a scheduled campaign
2253
+ *
2254
+ * @param id - Campaign ID
2255
+ * @returns The updated campaign (status: cancelled)
2256
+ *
2257
+ * @example
2258
+ * ```typescript
2259
+ * const campaign = await sendly.campaigns.cancel('cmp_xxx');
2260
+ * console.log(`Campaign cancelled`);
2261
+ * ```
2262
+ */
2263
+ async cancel(id) {
2264
+ const response = await this.http.request({
2265
+ method: "POST",
2266
+ path: `/campaigns/${id}/cancel`
2267
+ });
2268
+ return this.transformCampaign(response);
2269
+ }
2270
+ /**
2271
+ * Clone a campaign
2272
+ *
2273
+ * Creates a new draft campaign with the same settings.
2274
+ *
2275
+ * @param id - Campaign ID to clone
2276
+ * @returns The new campaign (as draft)
2277
+ *
2278
+ * @example
2279
+ * ```typescript
2280
+ * const cloned = await sendly.campaigns.clone('cmp_xxx');
2281
+ * console.log(`Created clone: ${cloned.id}`);
2282
+ * ```
2283
+ */
2284
+ async clone(id) {
2285
+ const response = await this.http.request({
2286
+ method: "POST",
2287
+ path: `/campaigns/${id}/clone`
2288
+ });
2289
+ return this.transformCampaign(response);
2290
+ }
2291
+ transformCampaign(raw) {
2292
+ return {
2293
+ id: raw.id,
2294
+ name: raw.name,
2295
+ text: raw.text,
2296
+ templateId: raw.template_id,
2297
+ contactListIds: raw.contact_list_ids || [],
2298
+ status: raw.status,
2299
+ recipientCount: raw.recipient_count || 0,
2300
+ sentCount: raw.sent_count || 0,
2301
+ deliveredCount: raw.delivered_count || 0,
2302
+ failedCount: raw.failed_count || 0,
2303
+ estimatedCredits: raw.estimated_credits || 0,
2304
+ creditsUsed: raw.credits_used || 0,
2305
+ scheduledAt: raw.scheduled_at,
2306
+ timezone: raw.timezone,
2307
+ startedAt: raw.started_at,
2308
+ completedAt: raw.completed_at,
2309
+ createdAt: raw.created_at,
2310
+ updatedAt: raw.updated_at
2311
+ };
2312
+ }
2313
+ };
2314
+
2315
+ // src/resources/contacts.ts
2316
+ var ContactsResource = class {
2317
+ http;
2318
+ lists;
2319
+ constructor(http) {
2320
+ this.http = http;
2321
+ this.lists = new ContactListsResource(http);
2322
+ }
2323
+ /**
2324
+ * List contacts with optional filtering
2325
+ *
2326
+ * @param options - Filter and pagination options
2327
+ * @returns List of contacts
2328
+ *
2329
+ * @example
2330
+ * ```typescript
2331
+ * // List all contacts
2332
+ * const { contacts, total } = await sendly.contacts.list();
2333
+ *
2334
+ * // Search contacts
2335
+ * const { contacts } = await sendly.contacts.list({ search: 'john' });
2336
+ *
2337
+ * // List contacts in a specific list
2338
+ * const { contacts } = await sendly.contacts.list({ listId: 'lst_xxx' });
2339
+ * ```
2340
+ */
2341
+ async list(options = {}) {
2342
+ const params = new URLSearchParams();
2343
+ if (options.limit) params.set("limit", String(options.limit));
2344
+ if (options.offset) params.set("offset", String(options.offset));
2345
+ if (options.search) params.set("search", options.search);
2346
+ if (options.listId) params.set("list_id", options.listId);
2347
+ const queryString = params.toString();
2348
+ const response = await this.http.request({
2349
+ method: "GET",
2350
+ path: `/contacts${queryString ? `?${queryString}` : ""}`
2351
+ });
2352
+ return {
2353
+ contacts: response.contacts.map((c) => this.transformContact(c)),
2354
+ total: response.total,
2355
+ limit: response.limit,
2356
+ offset: response.offset
2357
+ };
2358
+ }
2359
+ /**
2360
+ * Get a contact by ID
2361
+ *
2362
+ * @param id - Contact ID
2363
+ * @returns The contact with associated lists
2364
+ *
2365
+ * @example
2366
+ * ```typescript
2367
+ * const contact = await sendly.contacts.get('cnt_xxx');
2368
+ * console.log(`${contact.name}: ${contact.phoneNumber}`);
2369
+ * console.log(`In ${contact.lists?.length || 0} lists`);
2370
+ * ```
2371
+ */
2372
+ async get(id) {
2373
+ const response = await this.http.request({
2374
+ method: "GET",
2375
+ path: `/contacts/${id}`
2376
+ });
2377
+ return this.transformContact(response);
2378
+ }
2379
+ /**
2380
+ * Create a new contact
2381
+ *
2382
+ * @param request - Contact details
2383
+ * @returns The created contact
2384
+ *
2385
+ * @example
2386
+ * ```typescript
2387
+ * const contact = await sendly.contacts.create({
2388
+ * phoneNumber: '+15551234567',
2389
+ * name: 'Jane Smith',
2390
+ * email: 'jane@example.com',
2391
+ * metadata: { source: 'signup_form', plan: 'premium' }
2392
+ * });
2393
+ * ```
2394
+ */
2395
+ async create(request) {
2396
+ const response = await this.http.request({
2397
+ method: "POST",
2398
+ path: "/contacts",
2399
+ body: {
2400
+ phone_number: request.phoneNumber,
2401
+ name: request.name,
2402
+ email: request.email,
2403
+ metadata: request.metadata
2404
+ }
2405
+ });
2406
+ return this.transformContact(response);
2407
+ }
2408
+ /**
2409
+ * Update a contact
2410
+ *
2411
+ * @param id - Contact ID
2412
+ * @param request - Fields to update
2413
+ * @returns The updated contact
2414
+ *
2415
+ * @example
2416
+ * ```typescript
2417
+ * const contact = await sendly.contacts.update('cnt_xxx', {
2418
+ * name: 'Jane Doe',
2419
+ * metadata: { plan: 'enterprise' }
2420
+ * });
2421
+ * ```
2422
+ */
2423
+ async update(id, request) {
2424
+ const response = await this.http.request({
2425
+ method: "PATCH",
2426
+ path: `/contacts/${id}`,
2427
+ body: {
2428
+ name: request.name,
2429
+ email: request.email,
2430
+ metadata: request.metadata
2431
+ }
2432
+ });
2433
+ return this.transformContact(response);
2434
+ }
2435
+ /**
2436
+ * Delete a contact
2437
+ *
2438
+ * @param id - Contact ID
2439
+ *
2440
+ * @example
2441
+ * ```typescript
2442
+ * await sendly.contacts.delete('cnt_xxx');
2443
+ * ```
2444
+ */
2445
+ async delete(id) {
2446
+ await this.http.request({
2447
+ method: "DELETE",
2448
+ path: `/contacts/${id}`
2449
+ });
2450
+ }
2451
+ async import(request) {
2452
+ const body = {
2453
+ contacts: request.contacts
2454
+ };
2455
+ if (request.listId) body.listId = request.listId;
2456
+ if (request.optedInAt) body.optedInAt = request.optedInAt;
2457
+ return this.http.request({
2458
+ method: "POST",
2459
+ path: "/contacts/import",
2460
+ body
2461
+ });
2462
+ }
2463
+ transformContact(raw) {
2464
+ return {
2465
+ id: raw.id,
2466
+ phoneNumber: raw.phone_number,
2467
+ name: raw.name,
2468
+ email: raw.email,
2469
+ metadata: raw.metadata,
2470
+ optedOut: raw.opted_out,
2471
+ createdAt: raw.created_at,
2472
+ updatedAt: raw.updated_at,
2473
+ lists: raw.lists?.map((l) => ({ id: l.id, name: l.name }))
2474
+ };
2475
+ }
2476
+ };
2477
+ var ContactListsResource = class {
2478
+ http;
2479
+ constructor(http) {
2480
+ this.http = http;
2481
+ }
2482
+ /**
2483
+ * List all contact lists
2484
+ *
2485
+ * @returns All contact lists
2486
+ *
2487
+ * @example
2488
+ * ```typescript
2489
+ * const { lists } = await sendly.contacts.lists.list();
2490
+ * for (const list of lists) {
2491
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2492
+ * }
2493
+ * ```
2494
+ */
2495
+ async list() {
2496
+ const response = await this.http.request({
2497
+ method: "GET",
2498
+ path: "/contact-lists"
2499
+ });
2500
+ return {
2501
+ lists: response.lists.map((l) => this.transformList(l))
2502
+ };
2503
+ }
2504
+ /**
2505
+ * Get a contact list by ID
2506
+ *
2507
+ * @param id - Contact list ID
2508
+ * @param options - Pagination options for contacts
2509
+ * @returns The contact list with members
2510
+ *
2511
+ * @example
2512
+ * ```typescript
2513
+ * const list = await sendly.contacts.lists.get('lst_xxx');
2514
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2515
+ *
2516
+ * // Get list with paginated contacts
2517
+ * const list = await sendly.contacts.lists.get('lst_xxx', { limit: 100 });
2518
+ * console.log(`Showing ${list.contacts?.length} of ${list.contactsTotal}`);
2519
+ * ```
2520
+ */
2521
+ async get(id, options = {}) {
2522
+ const params = new URLSearchParams();
2523
+ if (options.limit) params.set("limit", String(options.limit));
2524
+ if (options.offset) params.set("offset", String(options.offset));
2525
+ const queryString = params.toString();
2526
+ const response = await this.http.request({
2527
+ method: "GET",
2528
+ path: `/contact-lists/${id}${queryString ? `?${queryString}` : ""}`
2529
+ });
2530
+ return this.transformList(response);
2531
+ }
2532
+ /**
2533
+ * Create a new contact list
2534
+ *
2535
+ * @param request - List details
2536
+ * @returns The created list
2537
+ *
2538
+ * @example
2539
+ * ```typescript
2540
+ * const list = await sendly.contacts.lists.create({
2541
+ * name: 'VIP Customers',
2542
+ * description: 'High-value customers for priority messaging'
2543
+ * });
2544
+ * ```
2545
+ */
2546
+ async create(request) {
2547
+ const response = await this.http.request({
2548
+ method: "POST",
2549
+ path: "/contact-lists",
2550
+ body: {
2551
+ name: request.name,
2552
+ description: request.description
2553
+ }
2554
+ });
2555
+ return this.transformList(response);
2556
+ }
2557
+ /**
2558
+ * Update a contact list
2559
+ *
2560
+ * @param id - Contact list ID
2561
+ * @param request - Fields to update
2562
+ * @returns The updated list
2563
+ *
2564
+ * @example
2565
+ * ```typescript
2566
+ * const list = await sendly.contacts.lists.update('lst_xxx', {
2567
+ * name: 'Premium Subscribers',
2568
+ * description: 'Updated description'
2569
+ * });
2570
+ * ```
2571
+ */
2572
+ async update(id, request) {
2573
+ const response = await this.http.request({
2574
+ method: "PATCH",
2575
+ path: `/contact-lists/${id}`,
2576
+ body: {
2577
+ name: request.name,
2578
+ description: request.description
2579
+ }
2580
+ });
2581
+ return this.transformList(response);
2582
+ }
2583
+ /**
2584
+ * Delete a contact list
2585
+ *
2586
+ * This does not delete the contacts in the list.
2587
+ *
2588
+ * @param id - Contact list ID
2589
+ *
2590
+ * @example
2591
+ * ```typescript
2592
+ * await sendly.contacts.lists.delete('lst_xxx');
2593
+ * ```
2594
+ */
2595
+ async delete(id) {
2596
+ await this.http.request({
2597
+ method: "DELETE",
2598
+ path: `/contact-lists/${id}`
2599
+ });
2600
+ }
2601
+ /**
2602
+ * Add contacts to a list
2603
+ *
2604
+ * @param listId - Contact list ID
2605
+ * @param contactIds - Array of contact IDs to add
2606
+ * @returns Number of contacts added
2607
+ *
2608
+ * @example
2609
+ * ```typescript
2610
+ * const result = await sendly.contacts.lists.addContacts('lst_xxx', [
2611
+ * 'cnt_abc',
2612
+ * 'cnt_def',
2613
+ * 'cnt_ghi'
2614
+ * ]);
2615
+ * console.log(`Added ${result.addedCount} contacts`);
2616
+ * ```
2617
+ */
2618
+ async addContacts(listId, contactIds) {
2619
+ const response = await this.http.request({
2620
+ method: "POST",
2621
+ path: `/contact-lists/${listId}/contacts`,
2622
+ body: { contact_ids: contactIds }
2623
+ });
2624
+ return { addedCount: response.added_count };
2625
+ }
2626
+ /**
2627
+ * Remove a contact from a list
2628
+ *
2629
+ * @param listId - Contact list ID
2630
+ * @param contactId - Contact ID to remove
2631
+ *
2632
+ * @example
2633
+ * ```typescript
2634
+ * await sendly.contacts.lists.removeContact('lst_xxx', 'cnt_abc');
2635
+ * ```
2636
+ */
2637
+ async removeContact(listId, contactId) {
2638
+ await this.http.request({
2639
+ method: "DELETE",
2640
+ path: `/contact-lists/${listId}/contacts/${contactId}`
2641
+ });
2642
+ }
2643
+ transformList(raw) {
2644
+ return {
2645
+ id: raw.id,
2646
+ name: raw.name,
2647
+ description: raw.description,
2648
+ contactCount: raw.contact_count || 0,
2649
+ createdAt: raw.created_at,
2650
+ updatedAt: raw.updated_at,
2651
+ contacts: raw.contacts?.map((c) => ({
2652
+ id: c.id,
2653
+ phoneNumber: c.phone_number,
2654
+ name: c.name,
2655
+ email: c.email
2656
+ })),
2657
+ contactsTotal: raw.contacts_total
2658
+ };
2659
+ }
2660
+ };
2661
+
1934
2662
  // src/client.ts
1935
2663
  var DEFAULT_BASE_URL2 = "https://sendly.live/api/v1";
1936
2664
  var DEFAULT_TIMEOUT2 = 3e4;
@@ -2028,6 +2756,47 @@ var Sendly = class {
2028
2756
  * ```
2029
2757
  */
2030
2758
  templates;
2759
+ /**
2760
+ * Campaigns API resource - Bulk SMS campaign management
2761
+ *
2762
+ * @example
2763
+ * ```typescript
2764
+ * // Create a campaign
2765
+ * const campaign = await sendly.campaigns.create({
2766
+ * name: 'Welcome Campaign',
2767
+ * text: 'Hello {{name}}!',
2768
+ * contactListIds: ['lst_xxx']
2769
+ * });
2770
+ *
2771
+ * // Preview cost
2772
+ * const preview = await sendly.campaigns.preview(campaign.id);
2773
+ * console.log(`Cost: ${preview.estimatedCredits} credits`);
2774
+ *
2775
+ * // Send or schedule
2776
+ * await sendly.campaigns.send(campaign.id);
2777
+ * ```
2778
+ */
2779
+ campaigns;
2780
+ /**
2781
+ * Contacts API resource - Contact and list management
2782
+ *
2783
+ * @example
2784
+ * ```typescript
2785
+ * // Create a contact
2786
+ * const contact = await sendly.contacts.create({
2787
+ * phoneNumber: '+15551234567',
2788
+ * name: 'John Doe'
2789
+ * });
2790
+ *
2791
+ * // Create a list and add contacts
2792
+ * const list = await sendly.contacts.lists.create({ name: 'VIPs' });
2793
+ * await sendly.contacts.lists.addContacts(list.id, [contact.id]);
2794
+ *
2795
+ * // List all contacts
2796
+ * const { contacts } = await sendly.contacts.list();
2797
+ * ```
2798
+ */
2799
+ contacts;
2031
2800
  http;
2032
2801
  config;
2033
2802
  /**
@@ -2062,6 +2831,8 @@ var Sendly = class {
2062
2831
  this.account = new AccountResource(this.http);
2063
2832
  this.verify = new VerifyResource(this.http);
2064
2833
  this.templates = new TemplatesResource(this.http);
2834
+ this.campaigns = new CampaignsResource(this.http);
2835
+ this.contacts = new ContactsResource(this.http);
2065
2836
  }
2066
2837
  /**
2067
2838
  * Check if the client is using a test API key