@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.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,632 @@ 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
+ transformContact(raw) {
2452
+ return {
2453
+ id: raw.id,
2454
+ phoneNumber: raw.phone_number,
2455
+ name: raw.name,
2456
+ email: raw.email,
2457
+ metadata: raw.metadata,
2458
+ optedOut: raw.opted_out,
2459
+ createdAt: raw.created_at,
2460
+ updatedAt: raw.updated_at,
2461
+ lists: raw.lists?.map((l) => ({ id: l.id, name: l.name }))
2462
+ };
2463
+ }
2464
+ };
2465
+ var ContactListsResource = class {
2466
+ http;
2467
+ constructor(http) {
2468
+ this.http = http;
2469
+ }
2470
+ /**
2471
+ * List all contact lists
2472
+ *
2473
+ * @returns All contact lists
2474
+ *
2475
+ * @example
2476
+ * ```typescript
2477
+ * const { lists } = await sendly.contacts.lists.list();
2478
+ * for (const list of lists) {
2479
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2480
+ * }
2481
+ * ```
2482
+ */
2483
+ async list() {
2484
+ const response = await this.http.request({
2485
+ method: "GET",
2486
+ path: "/contact-lists"
2487
+ });
2488
+ return {
2489
+ lists: response.lists.map((l) => this.transformList(l))
2490
+ };
2491
+ }
2492
+ /**
2493
+ * Get a contact list by ID
2494
+ *
2495
+ * @param id - Contact list ID
2496
+ * @param options - Pagination options for contacts
2497
+ * @returns The contact list with members
2498
+ *
2499
+ * @example
2500
+ * ```typescript
2501
+ * const list = await sendly.contacts.lists.get('lst_xxx');
2502
+ * console.log(`${list.name}: ${list.contactCount} contacts`);
2503
+ *
2504
+ * // Get list with paginated contacts
2505
+ * const list = await sendly.contacts.lists.get('lst_xxx', { limit: 100 });
2506
+ * console.log(`Showing ${list.contacts?.length} of ${list.contactsTotal}`);
2507
+ * ```
2508
+ */
2509
+ async get(id, options = {}) {
2510
+ const params = new URLSearchParams();
2511
+ if (options.limit) params.set("limit", String(options.limit));
2512
+ if (options.offset) params.set("offset", String(options.offset));
2513
+ const queryString = params.toString();
2514
+ const response = await this.http.request({
2515
+ method: "GET",
2516
+ path: `/contact-lists/${id}${queryString ? `?${queryString}` : ""}`
2517
+ });
2518
+ return this.transformList(response);
2519
+ }
2520
+ /**
2521
+ * Create a new contact list
2522
+ *
2523
+ * @param request - List details
2524
+ * @returns The created list
2525
+ *
2526
+ * @example
2527
+ * ```typescript
2528
+ * const list = await sendly.contacts.lists.create({
2529
+ * name: 'VIP Customers',
2530
+ * description: 'High-value customers for priority messaging'
2531
+ * });
2532
+ * ```
2533
+ */
2534
+ async create(request) {
2535
+ const response = await this.http.request({
2536
+ method: "POST",
2537
+ path: "/contact-lists",
2538
+ body: {
2539
+ name: request.name,
2540
+ description: request.description
2541
+ }
2542
+ });
2543
+ return this.transformList(response);
2544
+ }
2545
+ /**
2546
+ * Update a contact list
2547
+ *
2548
+ * @param id - Contact list ID
2549
+ * @param request - Fields to update
2550
+ * @returns The updated list
2551
+ *
2552
+ * @example
2553
+ * ```typescript
2554
+ * const list = await sendly.contacts.lists.update('lst_xxx', {
2555
+ * name: 'Premium Subscribers',
2556
+ * description: 'Updated description'
2557
+ * });
2558
+ * ```
2559
+ */
2560
+ async update(id, request) {
2561
+ const response = await this.http.request({
2562
+ method: "PATCH",
2563
+ path: `/contact-lists/${id}`,
2564
+ body: {
2565
+ name: request.name,
2566
+ description: request.description
2567
+ }
2568
+ });
2569
+ return this.transformList(response);
2570
+ }
2571
+ /**
2572
+ * Delete a contact list
2573
+ *
2574
+ * This does not delete the contacts in the list.
2575
+ *
2576
+ * @param id - Contact list ID
2577
+ *
2578
+ * @example
2579
+ * ```typescript
2580
+ * await sendly.contacts.lists.delete('lst_xxx');
2581
+ * ```
2582
+ */
2583
+ async delete(id) {
2584
+ await this.http.request({
2585
+ method: "DELETE",
2586
+ path: `/contact-lists/${id}`
2587
+ });
2588
+ }
2589
+ /**
2590
+ * Add contacts to a list
2591
+ *
2592
+ * @param listId - Contact list ID
2593
+ * @param contactIds - Array of contact IDs to add
2594
+ * @returns Number of contacts added
2595
+ *
2596
+ * @example
2597
+ * ```typescript
2598
+ * const result = await sendly.contacts.lists.addContacts('lst_xxx', [
2599
+ * 'cnt_abc',
2600
+ * 'cnt_def',
2601
+ * 'cnt_ghi'
2602
+ * ]);
2603
+ * console.log(`Added ${result.addedCount} contacts`);
2604
+ * ```
2605
+ */
2606
+ async addContacts(listId, contactIds) {
2607
+ const response = await this.http.request({
2608
+ method: "POST",
2609
+ path: `/contact-lists/${listId}/contacts`,
2610
+ body: { contact_ids: contactIds }
2611
+ });
2612
+ return { addedCount: response.added_count };
2613
+ }
2614
+ /**
2615
+ * Remove a contact from a list
2616
+ *
2617
+ * @param listId - Contact list ID
2618
+ * @param contactId - Contact ID to remove
2619
+ *
2620
+ * @example
2621
+ * ```typescript
2622
+ * await sendly.contacts.lists.removeContact('lst_xxx', 'cnt_abc');
2623
+ * ```
2624
+ */
2625
+ async removeContact(listId, contactId) {
2626
+ await this.http.request({
2627
+ method: "DELETE",
2628
+ path: `/contact-lists/${listId}/contacts/${contactId}`
2629
+ });
2630
+ }
2631
+ transformList(raw) {
2632
+ return {
2633
+ id: raw.id,
2634
+ name: raw.name,
2635
+ description: raw.description,
2636
+ contactCount: raw.contact_count || 0,
2637
+ createdAt: raw.created_at,
2638
+ updatedAt: raw.updated_at,
2639
+ contacts: raw.contacts?.map((c) => ({
2640
+ id: c.id,
2641
+ phoneNumber: c.phone_number,
2642
+ name: c.name,
2643
+ email: c.email
2644
+ })),
2645
+ contactsTotal: raw.contacts_total
2646
+ };
2647
+ }
2648
+ };
2649
+
1934
2650
  // src/client.ts
1935
2651
  var DEFAULT_BASE_URL2 = "https://sendly.live/api/v1";
1936
2652
  var DEFAULT_TIMEOUT2 = 3e4;
@@ -2028,6 +2744,47 @@ var Sendly = class {
2028
2744
  * ```
2029
2745
  */
2030
2746
  templates;
2747
+ /**
2748
+ * Campaigns API resource - Bulk SMS campaign management
2749
+ *
2750
+ * @example
2751
+ * ```typescript
2752
+ * // Create a campaign
2753
+ * const campaign = await sendly.campaigns.create({
2754
+ * name: 'Welcome Campaign',
2755
+ * text: 'Hello {{name}}!',
2756
+ * contactListIds: ['lst_xxx']
2757
+ * });
2758
+ *
2759
+ * // Preview cost
2760
+ * const preview = await sendly.campaigns.preview(campaign.id);
2761
+ * console.log(`Cost: ${preview.estimatedCredits} credits`);
2762
+ *
2763
+ * // Send or schedule
2764
+ * await sendly.campaigns.send(campaign.id);
2765
+ * ```
2766
+ */
2767
+ campaigns;
2768
+ /**
2769
+ * Contacts API resource - Contact and list management
2770
+ *
2771
+ * @example
2772
+ * ```typescript
2773
+ * // Create a contact
2774
+ * const contact = await sendly.contacts.create({
2775
+ * phoneNumber: '+15551234567',
2776
+ * name: 'John Doe'
2777
+ * });
2778
+ *
2779
+ * // Create a list and add contacts
2780
+ * const list = await sendly.contacts.lists.create({ name: 'VIPs' });
2781
+ * await sendly.contacts.lists.addContacts(list.id, [contact.id]);
2782
+ *
2783
+ * // List all contacts
2784
+ * const { contacts } = await sendly.contacts.list();
2785
+ * ```
2786
+ */
2787
+ contacts;
2031
2788
  http;
2032
2789
  config;
2033
2790
  /**
@@ -2062,6 +2819,8 @@ var Sendly = class {
2062
2819
  this.account = new AccountResource(this.http);
2063
2820
  this.verify = new VerifyResource(this.http);
2064
2821
  this.templates = new TemplatesResource(this.http);
2822
+ this.campaigns = new CampaignsResource(this.http);
2823
+ this.contacts = new ContactsResource(this.http);
2065
2824
  }
2066
2825
  /**
2067
2826
  * Check if the client is using a test API key