@ikatec/digisac-api-sdk 4.7.0 → 4.9.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.
@@ -3,6 +3,8 @@ import { CampaignsApi } from './CampaignsApi'
3
3
  import type { ApiClient } from '../../core/ApiClient'
4
4
  import type {
5
5
  Campaign,
6
+ CampaignContactsPage,
7
+ CampaignTimelineEvent,
6
8
  CreateCampaignPayload,
7
9
  CampaignStats,
8
10
  SetIntervalPayload,
@@ -43,11 +45,7 @@ const campaign: Campaign = {
43
45
  totalContacts: null,
44
46
  totalContactsImported: null,
45
47
  totalValidContacts: null,
46
- sentContacts: null,
47
- viewedContacts: null,
48
- contactCount: null,
49
48
  mustOpenTicket: false,
50
- marketingIntegration: false,
51
49
  accountId: 'acc-1',
52
50
  serviceId: 'svc-1',
53
51
  defaultDepartmentId: null,
@@ -188,32 +186,44 @@ describe('CampaignsApi', () => {
188
186
 
189
187
  describe('sendById', () => {
190
188
  it('calls client.post with /campaigns/:id/send', async () => {
191
- vi.mocked(client.post).mockResolvedValue(campaign)
189
+ vi.mocked(client.post).mockResolvedValue({ ...campaign, status: 'processing' })
192
190
  const result = await api.sendById('cmp-1')
193
- expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/send')
194
- expect(result).toEqual(campaign)
191
+ expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/send', undefined, undefined)
192
+ expect(result).toEqual({ ...campaign, status: 'processing' })
195
193
  })
196
194
  })
197
195
 
198
- // ── pauseById ────────────────────────────────────────────────────
196
+ // ── pauseById / resumeById / retryById ───────────────────────────
199
197
 
200
198
  describe('pauseById', () => {
201
- it('calls client.post with /campaigns/:id/pause', async () => {
202
- vi.mocked(client.post).mockResolvedValue(campaign)
199
+ it('calls client.post with /campaigns/:id/pause and resolves the boolean', async () => {
200
+ vi.mocked(client.post).mockResolvedValue(true)
203
201
  const result = await api.pauseById('cmp-1')
204
- expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/pause')
205
- expect(result).toEqual(campaign)
202
+ expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/pause', undefined, undefined)
203
+ expect(result).toBe(true)
206
204
  })
207
205
  })
208
206
 
209
- // ── resumeById ───────────────────────────────────────────────────
210
-
211
207
  describe('resumeById', () => {
212
- it('calls client.post with /campaigns/:id/resume', async () => {
213
- vi.mocked(client.post).mockResolvedValue(campaign)
208
+ it('calls client.post with /campaigns/:id/resume and resolves the boolean', async () => {
209
+ vi.mocked(client.post).mockResolvedValue(true)
214
210
  const result = await api.resumeById('cmp-1')
215
- expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/resume')
216
- expect(result).toEqual(campaign)
211
+ expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/resume', undefined, undefined)
212
+ expect(result).toBe(true)
213
+ })
214
+ })
215
+
216
+ describe('retryById', () => {
217
+ it('calls client.post with /campaigns/:id/retry', async () => {
218
+ vi.mocked(client.post).mockResolvedValue('OK')
219
+ const result = await api.retryById('cmp-1')
220
+ expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/retry', undefined, undefined)
221
+ expect(result).toBe('OK')
222
+ })
223
+
224
+ it('resolves false for an unknown campaign', async () => {
225
+ vi.mocked(client.post).mockResolvedValue(false)
226
+ await expect(api.retryById('unknown')).resolves.toBe(false)
217
227
  })
218
228
  })
219
229
 
@@ -223,7 +233,7 @@ describe('CampaignsApi', () => {
223
233
  it('calls client.post with /campaigns/:id/duplicate', async () => {
224
234
  vi.mocked(client.post).mockResolvedValue(campaign)
225
235
  const result = await api.duplicateById('cmp-1')
226
- expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/duplicate')
236
+ expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/duplicate', undefined, undefined)
227
237
  expect(result).toEqual(campaign)
228
238
  })
229
239
  })
@@ -233,6 +243,16 @@ describe('CampaignsApi', () => {
233
243
  describe('getStatsById', () => {
234
244
  it('calls client.get with /campaigns/:id/stats', async () => {
235
245
  const stats: CampaignStats = {
246
+ total: 100,
247
+ sent: 100,
248
+ failed: 10,
249
+ sending: 0,
250
+ sentToServer: 5,
251
+ received: 85,
252
+ viewed: 70,
253
+ played: 0,
254
+ answered: 30,
255
+ sentAndNotReceived: 5,
236
256
  campaignInfo: {
237
257
  totalValidContacts: 100,
238
258
  totalContacts: 110,
@@ -240,74 +260,184 @@ describe('CampaignsApi', () => {
240
260
  firedSuccessCount: 90,
241
261
  firedFailedCount: 10,
242
262
  totalMessagesCount: 100,
263
+ sentMessagesCount: 100,
243
264
  totalFiredCount: 100,
244
265
  firedBlockedByMessageRuleCount: 0,
245
266
  },
246
- messageInfo: { error: 5, received: 85, viewed: 70, answered: 30 },
267
+ messageInfo: {
268
+ error: 5,
269
+ sending: 0,
270
+ sentToServer: 5,
271
+ received: 85,
272
+ viewed: 70,
273
+ played: 0,
274
+ answered: 30,
275
+ },
247
276
  }
248
277
  vi.mocked(client.get).mockResolvedValue(stats)
249
278
  const result = await api.getStatsById('cmp-1')
250
- expect(client.get).toHaveBeenCalledWith('/campaigns/cmp-1/stats')
279
+ expect(client.get).toHaveBeenCalledWith('/campaigns/cmp-1/stats', undefined)
251
280
  expect(result).toEqual(stats)
252
281
  })
253
282
  })
254
283
 
284
+ // ── getTimeline ──────────────────────────────────────────────────
285
+
286
+ describe('getTimeline', () => {
287
+ it('calls client.get with /campaigns/:id/timeline', async () => {
288
+ const events: CampaignTimelineEvent[] = [
289
+ {
290
+ id: 'finished',
291
+ type: 'finished',
292
+ timestamp: '2026-10-14T15:02:40.770Z',
293
+ actor: 'Sistema',
294
+ triggerType: 'automatic',
295
+ },
296
+ {
297
+ id: 'created',
298
+ type: 'created',
299
+ timestamp: '2026-10-14T15:02:33.795Z',
300
+ actor: 'Ana',
301
+ triggerType: 'manual',
302
+ },
303
+ ]
304
+ vi.mocked(client.get).mockResolvedValue(events)
305
+ const result = await api.getTimeline('cmp-1')
306
+ expect(client.get).toHaveBeenCalledWith('/campaigns/cmp-1/timeline', undefined)
307
+ expect(result).toEqual(events)
308
+ })
309
+ })
310
+
311
+ // ── getContacts ──────────────────────────────────────────────────
312
+
313
+ describe('getContacts', () => {
314
+ const page: CampaignContactsPage = {
315
+ data: [],
316
+ total: 0,
317
+ limit: 15,
318
+ skip: 0,
319
+ currentPage: 1,
320
+ lastPage: 1,
321
+ from: 0,
322
+ to: 0,
323
+ }
324
+
325
+ it('calls client.get with /campaigns/:id/contacts when no query is provided', async () => {
326
+ vi.mocked(client.get).mockResolvedValue(page)
327
+ const result = await api.getContacts('cmp-1')
328
+ expect(client.get).toHaveBeenCalledWith('/campaigns/cmp-1/contacts', undefined)
329
+ expect(result).toEqual(page)
330
+ })
331
+
332
+ it('serializes the query', async () => {
333
+ vi.mocked(client.get).mockResolvedValue(page)
334
+ await api.getContacts('cmp-1', { page: 2, contactStatus: ['not_sent'], sortBy: 'sentAt' })
335
+ const url = vi.mocked(client.get).mock.calls[0]?.[0] as string
336
+ expect(url.startsWith('/campaigns/cmp-1/contacts?')).toBe(true)
337
+ expect(decodeURIComponent(url)).toContain('"contactStatus":["not_sent"]')
338
+ })
339
+ })
340
+
255
341
  // ── setIntervalById ──────────────────────────────────────────────
256
342
 
257
343
  describe('setIntervalById', () => {
258
344
  it('calls client.post with /campaigns/:id/interval and body', async () => {
259
- vi.mocked(client.post).mockResolvedValue(campaign)
260
- const data: SetIntervalPayload = { minInterval: '1000', maxInterval: '5000' }
345
+ vi.mocked(client.post).mockResolvedValue(true)
346
+ const data: SetIntervalPayload = { minInterval: 5, maxInterval: 10 }
261
347
  const result = await api.setIntervalById('cmp-1', data)
262
- expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/interval', data)
263
- expect(result).toEqual(campaign)
348
+ expect(client.post).toHaveBeenCalledWith('/campaigns/cmp-1/interval', data, undefined)
349
+ expect(result).toBe(true)
264
350
  })
265
351
  })
266
352
 
267
- // ── getContactCount ──────────────────────────────────────────────
353
+ // ── getContactCount / countReadyByService ────────────────────────
268
354
 
269
355
  describe('getContactCount', () => {
270
356
  it('calls client.get with /campaigns/:id/contactCount', async () => {
271
357
  vi.mocked(client.get).mockResolvedValue(42)
272
358
  const result = await api.getContactCount('cmp-1')
273
- expect(client.get).toHaveBeenCalledWith('/campaigns/cmp-1/contactCount')
359
+ expect(client.get).toHaveBeenCalledWith('/campaigns/cmp-1/contactCount', undefined)
274
360
  expect(result).toBe(42)
275
361
  })
276
362
  })
277
363
 
364
+ describe('countReadyByService', () => {
365
+ it('calls client.get with /campaigns/count-ready/:serviceId', async () => {
366
+ vi.mocked(client.get).mockResolvedValue(4)
367
+ const result = await api.countReadyByService('svc-1')
368
+ expect(client.get).toHaveBeenCalledWith('/campaigns/count-ready/svc-1', undefined)
369
+ expect(result).toBe(4)
370
+ })
371
+ })
372
+
278
373
  // ── setAutoPauseMode ─────────────────────────────────────────────
279
374
 
280
375
  describe('setAutoPauseMode', () => {
281
376
  it('calls client.put with /campaigns/autoPauseMode and body', async () => {
282
- vi.mocked(client.put).mockResolvedValue({ ok: true })
283
- const data: AutoPauseModePayload = { mode: 'auto' }
377
+ vi.mocked(client.put).mockResolvedValue('OK')
378
+ const data: AutoPauseModePayload = { mode: 'medium-health' }
284
379
  const result = await api.setAutoPauseMode(data)
285
- expect(client.put).toHaveBeenCalledWith('/campaigns/autoPauseMode', data)
286
- expect(result).toEqual({ ok: true })
380
+ expect(client.put).toHaveBeenCalledWith('/campaigns/autoPauseMode', data, undefined)
381
+ expect(result).toBe('OK')
287
382
  })
288
383
  })
289
384
 
290
- // ── exportCampaignResult ─────────────────────────────────────────
385
+ // ── exports ──────────────────────────────────────────────────────
291
386
 
292
387
  describe('exportCampaignResult', () => {
293
- it('calls client.post with /campaigns/export/csv and body', async () => {
294
- vi.mocked(client.post).mockResolvedValue({ ok: true })
295
- const data: ExportCampaignResultPayload = { campaignId: 'cmp-1' }
388
+ it('calls client.post with /campaigns/export/csv and the where of the campaign', async () => {
389
+ vi.mocked(client.post).mockResolvedValue('Nome do contato;Número do contato\n')
390
+ const data: ExportCampaignResultPayload = {
391
+ where: { campaignId: 'cmp-1' },
392
+ type: 'semiColon',
393
+ }
296
394
  const result = await api.exportCampaignResult(data)
297
- expect(client.post).toHaveBeenCalledWith('/campaigns/export/csv', data)
298
- expect(result).toEqual({ ok: true })
395
+ expect(client.post).toHaveBeenCalledWith('/campaigns/export/csv', data, undefined)
396
+ expect(result).toBe('Nome do contato;Número do contato\n')
299
397
  })
300
398
  })
301
399
 
302
- // ── exportTemplateCsv ────────────────────────────────────────────
303
-
304
400
  describe('exportTemplateCsv', () => {
305
401
  it('calls client.post with /campaigns/exportTemplate and body', async () => {
306
- vi.mocked(client.post).mockResolvedValue({ ok: true })
402
+ vi.mocked(client.post).mockResolvedValue('Nome;Telefone;Username\n')
307
403
  const data: ExportTemplateCsvPayload = { hsmId: 'hsm-1' }
308
404
  const result = await api.exportTemplateCsv(data)
309
- expect(client.post).toHaveBeenCalledWith('/campaigns/exportTemplate', data)
310
- expect(result).toEqual({ ok: true })
405
+ expect(client.post).toHaveBeenCalledWith('/campaigns/exportTemplate', data, undefined)
406
+ expect(result).toBe('Nome;Telefone;Username\n')
407
+ })
408
+ })
409
+
410
+ describe('exportImportErrors', () => {
411
+ it('calls client.get with /campaigns/:id/import-errors', async () => {
412
+ vi.mocked(client.get).mockResolvedValue('Linha,Nome,Número,Username,Motivo\n')
413
+ const result = await api.exportImportErrors('cmp-1')
414
+ expect(client.get).toHaveBeenCalledWith('/campaigns/cmp-1/import-errors', undefined)
415
+ expect(result).toBe('Linha,Nome,Número,Username,Motivo\n')
416
+ })
417
+
418
+ it('sends the delimiter in the query string', async () => {
419
+ vi.mocked(client.get).mockResolvedValue('Linha;Nome;Número;Username;Motivo\n')
420
+ await api.exportImportErrors('cmp-1', { delimiter: ';' })
421
+ expect(client.get).toHaveBeenCalledWith(
422
+ '/campaigns/cmp-1/import-errors?delimiter=%3B',
423
+ undefined,
424
+ )
425
+ })
426
+ })
427
+
428
+ // ── deleteMany ───────────────────────────────────────────────────
429
+
430
+ describe('deleteMany', () => {
431
+ it('calls client.request with DELETE /campaigns/delete-many and the ids', async () => {
432
+ vi.mocked(client.request).mockResolvedValue(true)
433
+ const result = await api.deleteMany(['cmp-1', 'cmp-2'])
434
+ expect(client.request).toHaveBeenCalledWith(
435
+ 'DELETE',
436
+ '/campaigns/delete-many',
437
+ { ids: ['cmp-1', 'cmp-2'] },
438
+ undefined,
439
+ )
440
+ expect(result).toBe(true)
311
441
  })
312
442
  })
313
443
 
@@ -315,10 +445,13 @@ describe('CampaignsApi', () => {
315
445
 
316
446
  describe('getHsmLimit', () => {
317
447
  it('calls client.get with /whatsapp-business-templates/:accountId/hsmLimit', async () => {
318
- const limit: HsmLimit = { hsmLimit: 1000, hsmUsedLimit: 150 }
448
+ const limit: HsmLimit = { hsmLimit: 1000, hsmUsedLimit: 150, hasLimit: 850 }
319
449
  vi.mocked(client.get).mockResolvedValue(limit)
320
450
  const result = await api.getHsmLimit('acc-1')
321
- expect(client.get).toHaveBeenCalledWith('/whatsapp-business-templates/acc-1/hsmLimit')
451
+ expect(client.get).toHaveBeenCalledWith(
452
+ '/whatsapp-business-templates/acc-1/hsmLimit',
453
+ undefined,
454
+ )
322
455
  expect(result).toEqual(limit)
323
456
  })
324
457
  })
@@ -1,10 +1,16 @@
1
+ import qs from 'qs'
1
2
  import type { ApiClient } from '../../core'
2
3
  import { BaseCrudApi } from '../../core'
3
4
  import type {
4
5
  AutoPauseModePayload,
5
6
  Campaign,
7
+ CampaignContactsPage,
8
+ CampaignContactsQuery,
9
+ CampaignListQuery,
6
10
  CampaignStats,
11
+ CampaignTimelineEvent,
7
12
  CreateCampaignPayload,
13
+ ExportCampaignImportErrorsQuery,
8
14
  ExportCampaignResultPayload,
9
15
  ExportTemplateCsvPayload,
10
16
  HsmLimit,
@@ -12,79 +18,195 @@ import type {
12
18
  UpdateCampaignPayload,
13
19
  } from './types'
14
20
 
21
+ /**
22
+ * Campaigns (mass sending). Every `/campaigns` route requires the campaigns feature in the plan of
23
+ * the account.
24
+ * `getMany` takes the `where` of a `ListQuery` and the flat filters of the Campaigns screen (see
25
+ * {@link CampaignListQuery}); `getById` of an unknown id resolves `null`. `deleteById` answers the
26
+ * text `'OK'`. `getMany({ paginate: false })` answers 400 `FILTER_PARAM_INVALID` with the default
27
+ * `'json'` query format (the route only takes `paginate` as a string): use `withQueryFormat('qs')`.
28
+ */
15
29
  export class CampaignsApi extends BaseCrudApi<
16
30
  Campaign,
17
31
  CreateCampaignPayload,
18
- UpdateCampaignPayload
32
+ UpdateCampaignPayload,
33
+ CampaignListQuery,
34
+ 'OK'
19
35
  > {
20
36
  constructor(client: ApiClient) {
21
37
  super(client, '/campaigns')
38
+ }
39
+
40
+ /**
41
+ * Starts sending the campaign: answers it as `processing` (an `import_error` campaign goes back
42
+ * to `importing_contacts`). It becomes `done` once every contact is fired.
43
+ * @permissions campaigns.view
44
+ */
45
+ sendById(id: string, headers?: Record<string, string>): Promise<Campaign> {
46
+ return this.client.post<Campaign>(`${this.basePath}/${id}/send`, undefined, headers)
47
+ }
48
+
49
+ /**
50
+ * Sets the campaign as `paused` and answers `true`. An unknown id answers 500.
51
+ * @permissions campaigns.view
52
+ */
53
+ pauseById(id: string, headers?: Record<string, string>): Promise<boolean> {
54
+ return this.client.post<boolean>(`${this.basePath}/${id}/pause`, undefined, headers)
55
+ }
56
+
57
+ /**
58
+ * Sets the campaign back to `processing` and answers `true`. An unknown id answers 500.
59
+ * @permissions campaigns.view
60
+ */
61
+ resumeById(id: string, headers?: Record<string, string>): Promise<boolean> {
62
+ return this.client.post<boolean>(`${this.basePath}/${id}/resume`, undefined, headers)
63
+ }
64
+
65
+ /**
66
+ * Fires again the messages that failed: sets them as pending and the campaign as `processing`.
67
+ * Answers the text `'OK'` (also when nothing failed, and then the status does not change); an
68
+ * unknown id answers `false` (status 200).
69
+ * @permissions campaigns.view
70
+ */
71
+ retryById(id: string, headers?: Record<string, string>): Promise<'OK' | false> {
72
+ return this.client.post<'OK' | false>(`${this.basePath}/${id}/retry`, undefined, headers)
73
+ }
74
+
75
+ /**
76
+ * Creates a copy of the campaign (`ready`, same title, messages and tags), answered without the
77
+ * messages. An unknown id answers 500.
78
+ * @permissions campaigns.view
79
+ */
80
+ duplicateById(id: string, headers?: Record<string, string>): Promise<Campaign> {
81
+ return this.client.post<Campaign>(`${this.basePath}/${id}/duplicate`, undefined, headers)
82
+ }
22
83
 
23
- this.sendById = this.sendById.bind(this)
24
- this.pauseById = this.pauseById.bind(this)
25
- this.resumeById = this.resumeById.bind(this)
26
- this.duplicateById = this.duplicateById.bind(this)
27
- this.getStatsById = this.getStatsById.bind(this)
28
- this.setIntervalById = this.setIntervalById.bind(this)
29
- this.getContactCount = this.getContactCount.bind(this)
30
- this.setAutoPauseMode = this.setAutoPauseMode.bind(this)
31
- this.exportCampaignResult = this.exportCampaignResult.bind(this)
32
- this.exportTemplateCsv = this.exportTemplateCsv.bind(this)
33
- this.getHsmLimit = this.getHsmLimit.bind(this)
84
+ /** Returns the send, delivery and interaction statistics of the campaign. */
85
+ getStatsById(id: string, headers?: Record<string, string>): Promise<CampaignStats> {
86
+ return this.client.get<CampaignStats>(`${this.basePath}/${id}/stats`, headers)
34
87
  }
35
88
 
36
- /** Starts sending messages for the given campaign. */
37
- sendById(id: string): Promise<Campaign> {
38
- return this.client.post<Campaign>(`${this.basePath}/${id}/send`)
89
+ /**
90
+ * Returns the events of the campaign (created, scheduled, started, finished and the last
91
+ * interruption), the most recent first.
92
+ */
93
+ getTimeline(id: string, headers?: Record<string, string>): Promise<CampaignTimelineEvent[]> {
94
+ return this.client.get<CampaignTimelineEvent[]>(`${this.basePath}/${id}/timeline`, headers)
39
95
  }
40
96
 
41
- /** Pauses a campaign that is currently sending. */
42
- pauseById(id: string): Promise<Campaign> {
43
- return this.client.post<Campaign>(`${this.basePath}/${id}/pause`)
97
+ /**
98
+ * Returns a page of the contacts of the campaign with their sending status (15 per page by
99
+ * default). An unknown id answers an empty page.
100
+ */
101
+ getContacts(
102
+ id: string,
103
+ query?: CampaignContactsQuery,
104
+ headers?: Record<string, string>,
105
+ ): Promise<CampaignContactsPage> {
106
+ return this.client.get<CampaignContactsPage>(
107
+ `${this.basePath}/${id}/contacts${this.toQueryString(query)}`,
108
+ headers,
109
+ )
44
110
  }
45
111
 
46
- /** Resumes a previously paused campaign. */
47
- resumeById(id: string): Promise<Campaign> {
48
- return this.client.post<Campaign>(`${this.basePath}/${id}/resume`)
112
+ /**
113
+ * Sets the min/max interval between the sends (whole numbers, `0` or more) and answers `true`.
114
+ * An unknown id answers 500.
115
+ * @permissions campaigns.view
116
+ */
117
+ setIntervalById(
118
+ id: string,
119
+ data: SetIntervalPayload,
120
+ headers?: Record<string, string>,
121
+ ): Promise<boolean> {
122
+ return this.client.post<boolean, SetIntervalPayload>(
123
+ `${this.basePath}/${id}/interval`,
124
+ data,
125
+ headers,
126
+ )
49
127
  }
50
128
 
51
- /** Creates a copy of an existing campaign. */
52
- duplicateById(id: string): Promise<Campaign> {
53
- return this.client.post<Campaign>(`${this.basePath}/${id}/duplicate`)
129
+ /** Returns the number of contacts targeted by the campaign. */
130
+ getContactCount(id: string, headers?: Record<string, string>): Promise<number> {
131
+ return this.client.get<number>(`${this.basePath}/${id}/contactCount`, headers)
54
132
  }
55
133
 
56
- /** Returns send/delivery/view statistics for a campaign. */
57
- getStatsById(id: string): Promise<CampaignStats> {
58
- return this.client.get<CampaignStats>(`${this.basePath}/${id}/stats`)
134
+ /** Counts the `ready` campaigns of a connection. An unknown id counts 0. */
135
+ countReadyByService(serviceId: string, headers?: Record<string, string>): Promise<number> {
136
+ return this.client.get<number>(`${this.basePath}/count-ready/${serviceId}`, headers)
59
137
  }
60
138
 
61
- /** Updates the min/max send interval for a campaign. */
62
- setIntervalById(id: string, data: SetIntervalPayload): Promise<Campaign> {
63
- return this.client.post<Campaign, SetIntervalPayload>(`${this.basePath}/${id}/interval`, data)
139
+ /**
140
+ * Sets the auto-pause mode of the campaigns of the account (`settings.campaign`). Answers the
141
+ * text `'OK'`, or `false` when the account was not updated.
142
+ */
143
+ setAutoPauseMode(
144
+ data: AutoPauseModePayload,
145
+ headers?: Record<string, string>,
146
+ ): Promise<'OK' | false> {
147
+ return this.client.put<'OK' | false, AutoPauseModePayload>(
148
+ `${this.basePath}/autoPauseMode`,
149
+ data,
150
+ headers,
151
+ )
64
152
  }
65
153
 
66
- /** Returns the total number of contacts targeted by a campaign. */
67
- getContactCount(id: string): Promise<number> {
68
- return this.client.get<number>(`${this.basePath}/${id}/contactCount`)
154
+ /**
155
+ * Exports the contacts of the campaign and their sending result as CSV (13 columns in the
156
+ * language of the user; `,`, or `;` with `type: 'semiColon'`), streamed without a Content-Type.
157
+ */
158
+ exportCampaignResult(
159
+ data: ExportCampaignResultPayload,
160
+ headers?: Record<string, string>,
161
+ ): Promise<string> {
162
+ return this.client.post<string, ExportCampaignResultPayload>(
163
+ `${this.basePath}/export/csv`,
164
+ data,
165
+ headers,
166
+ )
69
167
  }
70
168
 
71
- /** Configures the auto-pause behavior for campaigns. */
72
- setAutoPauseMode(data: AutoPauseModePayload): Promise<unknown> {
73
- return this.client.put(`${this.basePath}/autoPauseMode`, data)
169
+ /** Exports the CSV (`;`) to fill in the contacts of a campaign of the given HSM template. */
170
+ exportTemplateCsv(
171
+ data: ExportTemplateCsvPayload,
172
+ headers?: Record<string, string>,
173
+ ): Promise<string> {
174
+ return this.client.post<string, ExportTemplateCsvPayload>(
175
+ `${this.basePath}/exportTemplate`,
176
+ data,
177
+ headers,
178
+ )
74
179
  }
75
180
 
76
- /** Exports campaign send results as a CSV file. */
77
- exportCampaignResult(data: ExportCampaignResultPayload): Promise<unknown> {
78
- return this.client.post(`${this.basePath}/export/csv`, data)
181
+ /**
182
+ * Exports the rows of the import file that were not imported (invalid number, unknown or
183
+ * ambiguous username) as CSV: line, name, number, username and reason.
184
+ */
185
+ exportImportErrors(
186
+ id: string,
187
+ query?: ExportCampaignImportErrorsQuery,
188
+ headers?: Record<string, string>,
189
+ ): Promise<string> {
190
+ const queryString = query?.delimiter ? `?${qs.stringify({ delimiter: query.delimiter })}` : ''
191
+ return this.client.get<string>(`${this.basePath}/${id}/import-errors${queryString}`, headers)
79
192
  }
80
193
 
81
- /** Exports a CSV template for importing contacts into an HSM campaign. */
82
- exportTemplateCsv(data: ExportTemplateCsvPayload): Promise<unknown> {
83
- return this.client.post(`${this.basePath}/exportTemplate`, data)
194
+ /**
195
+ * Deletes the campaigns of the given ids and answers `true` (unknown ids are skipped). No id, or
196
+ * an id that is not a UUID, answers 400 `{ error: 'Invalid ids', code: 'INVALID_IDS' }`.
197
+ * @permissions campaigns.view OR campaigns.destroy
198
+ */
199
+ deleteMany(ids: string[], headers?: Record<string, string>): Promise<true> {
200
+ return this.client.request<true, { ids: string[] }>(
201
+ 'DELETE',
202
+ `${this.basePath}/delete-many`,
203
+ { ids },
204
+ headers,
205
+ )
84
206
  }
85
207
 
86
- /** Returns the HSM message usage and limit for an account. */
87
- getHsmLimit(accountId: string): Promise<HsmLimit> {
88
- return this.client.get<HsmLimit>(`/whatsapp-business-templates/${accountId}/hsmLimit`)
208
+ /** Returns the HSM message usage and limit of an account. */
209
+ getHsmLimit(accountId: string, headers?: Record<string, string>): Promise<HsmLimit> {
210
+ return this.client.get<HsmLimit>(`/whatsapp-business-templates/${accountId}/hsmLimit`, headers)
89
211
  }
90
212
  }