@ikatec/digisac-api-sdk 4.6.0 → 4.8.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.
@@ -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
  }
@@ -1,6 +1,7 @@
1
+ import type { ListQuery } from '../../core'
2
+ import type { Account } from '../accounts'
1
3
  import type { Department } from '../departments'
2
4
  import type { File } from '../files'
3
- import type { Message } from '../messages'
4
5
  import type { Service } from '../services'
5
6
  import type { Tag } from '../tags'
6
7
  import type { User } from '../users'
@@ -22,10 +23,13 @@ export type CampaignStatus =
22
23
  | 'preventive_break'
23
24
 
24
25
  export type CampaignConfig = {
25
- /** Minimum interval in ms between sends. */
26
- minInterval: number
27
- /** Maximum interval in ms between sends. */
28
- maxInterval: number
26
+ /**
27
+ * Minimum interval in ms between sends. Absent until set (a new campaign answers `config: {}`);
28
+ * `setIntervalById` stores a number, `updateById` stores what it is sent (a string stays a string).
29
+ */
30
+ minInterval?: number | string
31
+ /** Maximum interval in ms between sends. Same rules as `minInterval`. */
32
+ maxInterval?: number | string
29
33
  /** Column name in the import file used as the contact name. */
30
34
  nameField?: string
31
35
  /** Column name in the import file used as the phone number. */
@@ -50,11 +54,19 @@ export type Campaign = {
50
54
  totalContacts: number | null
51
55
  totalContactsImported: number | null
52
56
  totalValidContacts: number | null
53
- sentContacts: number | null
54
- viewedContacts: number | null
55
- contactCount: number | null
57
+ /** @deprecated Never answered: the campaign has no such column. */
58
+ sentContacts?: number | null
59
+ /** @deprecated Never answered: the campaign has no such column. */
60
+ viewedContacts?: number | null
61
+ /** @deprecated Never answered: read `getContactCount`. */
62
+ contactCount?: number | null
56
63
  mustOpenTicket: boolean
57
- marketingIntegration: boolean
64
+ /** @deprecated Never answered: the campaign transformer leaves the column out. */
65
+ marketingIntegration?: boolean
66
+ /** Always `null`: the campaign has no such column (answered by the transformer). */
67
+ userId?: null
68
+ /** Only in `getMany`: messages sent and viewed (ack 3). */
69
+ stats?: { sent: number; viewed: number }
58
70
  accountId: string
59
71
  serviceId: string
60
72
  defaultDepartmentId: string | null
@@ -71,10 +83,29 @@ export type Campaign = {
71
83
  createdBy?: User | null
72
84
  sentBy?: User | null
73
85
  tags?: Tag[]
74
- messages?: Message[]
86
+ messages?: CampaignMessage[]
75
87
  importFile?: File | null
76
88
  }
77
89
 
90
+ /** A message of the campaign (`campaign_messages`), as the `messages` include answers it. */
91
+ export type CampaignMessage = {
92
+ id: string
93
+ text: string | null
94
+ extraOptions: Record<string, unknown> | null
95
+ hsmId: string | null
96
+ hsmFileId: string | null
97
+ accountId: string
98
+ campaignId: string
99
+ createdAt: string
100
+ updatedAt: string
101
+ deletedAt: string | null
102
+ // relationships (includes `messages.file`, `messages.hsmFile` and `messages.hsm`)
103
+ account?: Account
104
+ file?: File | null
105
+ hsmFile?: File | null
106
+ hsm?: WhatsappBusinessTemplate | null
107
+ }
108
+
78
109
  export type CampaignRelationships =
79
110
  | 'service'
80
111
  | 'defaultDepartment'
@@ -129,6 +160,96 @@ export type CreateCampaignPayload = {
129
160
 
130
161
  export type UpdateCampaignPayload = Partial<CreateCampaignPayload>
131
162
 
163
+ /** The visible states the `status` filter of `getMany` takes (`'scheduled'` is derived from `isScheduled` + `sendsAt`). */
164
+ export type CampaignListStatus = CampaignStatus | 'scheduled'
165
+
166
+ /**
167
+ * `getMany` query: the `ListQuery` of a campaign plus the flat filters of the Campaigns screen.
168
+ * A flat `status` takes precedence over `where.status`. An invalid `status` or date answers 400
169
+ * `{ error: 'Invalid status' | 'Invalid date', code: 'FILTER_STATUS_INVALID' | 'FILTER_DATE_INVALID' }`.
170
+ */
171
+ export type CampaignListQuery = ListQuery<Campaign> & {
172
+ serviceId?: string
173
+ status?: CampaignListStatus
174
+ /**
175
+ * Quality of the HSM template of the messages. `'HIGH'`, `'MEDIUM'` and `'LOW'` only filter when
176
+ * `include` has `messages` (the filter rewrites that include); without it they are ignored.
177
+ * `'UNKNOWN'` keeps the campaigns without a rated template.
178
+ */
179
+ templateHealth?: 'UNKNOWN' | 'HIGH' | 'MEDIUM' | 'LOW'
180
+ /** Searches the title (accent-insensitive). */
181
+ q?: string
182
+ /**
183
+ * UTC ISO date-times ending in `Z` (a date alone answers 400 `FILTER_DATE_INVALID`); the days are
184
+ * taken in the timezone of the account. `sentAt*` filter `startedAt`.
185
+ */
186
+ sentAtFrom?: string
187
+ sentAtTo?: string
188
+ createdAtFrom?: string
189
+ createdAtTo?: string
190
+ }
191
+
192
+ /** The sending status of a contact of the campaign (`not_sent` in the filter also matches `pending`). */
193
+ export type CampaignContactStatus =
194
+ | 'pending'
195
+ | 'not_sent'
196
+ | 'sent'
197
+ | 'received'
198
+ | 'read'
199
+ | 'answered'
200
+ | 'quality_hold'
201
+ | 'quality_dropped'
202
+
203
+ export type CampaignContactsQuery = {
204
+ page?: number
205
+ perPage?: number
206
+ /** Searches the name, the number and the username of the contact. */
207
+ search?: string
208
+ contactStatus?: CampaignContactStatus[]
209
+ failReason?: string
210
+ sortBy?: 'contactName' | 'contactNumber' | 'contactUsername' | 'status' | 'sentAt' | 'failReason'
211
+ sortDir?: 'ASC' | 'DESC'
212
+ }
213
+
214
+ export type CampaignContact = {
215
+ id: string
216
+ sentAt: string | null
217
+ failed: boolean
218
+ failReason: string | null
219
+ parameters: unknown
220
+ contactName: string
221
+ /** `null` for a contact known only by its username. */
222
+ contactNumber: string | null
223
+ contactUsername: string | null
224
+ contactId: string
225
+ status: CampaignContactStatus
226
+ }
227
+
228
+ export type CampaignContactsPage = {
229
+ data: CampaignContact[]
230
+ total: number
231
+ limit: number
232
+ skip: number
233
+ currentPage: number
234
+ lastPage: number
235
+ from: number
236
+ to: number
237
+ }
238
+
239
+ export type CampaignTimelineEvent = {
240
+ /** `created`, `scheduled`, `started`, `finished` or `status-<status>`. */
241
+ id: string
242
+ type: 'created' | 'scheduled' | 'started' | 'finished' | CampaignStatus
243
+ timestamp: string
244
+ /** The name of the user who created or sent the campaign, or `'Sistema'`. */
245
+ actor: string
246
+ triggerType: 'manual' | 'automatic'
247
+ }
248
+
249
+ export type ExportCampaignImportErrorsQuery = {
250
+ delimiter?: ',' | ';' | ':'
251
+ }
252
+
132
253
  export type CampaignStatsInfo = {
133
254
  totalValidContacts: number
134
255
  totalContacts: number
@@ -136,29 +257,57 @@ export type CampaignStatsInfo = {
136
257
  firedSuccessCount: number
137
258
  firedFailedCount: number
138
259
  totalMessagesCount: number
260
+ sentMessagesCount: number
139
261
  totalFiredCount: number
140
262
  firedBlockedByMessageRuleCount: number
141
263
  }
142
264
 
265
+ /** Messages of the campaign by ack: `error`, `sending` (0), `sentToServer` (1), `received` (2), `viewed` (3), `played` (4). */
143
266
  export type CampaignStatsMessages = {
144
267
  error: number
268
+ sending: number
269
+ sentToServer: number
145
270
  received: number
146
271
  viewed: number
272
+ played: number
147
273
  answered: number
148
274
  }
149
275
 
150
276
  export type CampaignStats = {
151
277
  campaignInfo: CampaignStatsInfo
152
278
  messageInfo: CampaignStatsMessages
279
+ /** @deprecated Kept by the backend for backward compatibility: read `campaignInfo.totalMessagesCount`. */
280
+ total: number
281
+ /** @deprecated Read `campaignInfo.sentMessagesCount`. */
282
+ sent: number
283
+ /** @deprecated Read `campaignInfo.firedFailedCount`. */
284
+ failed: number
285
+ /** @deprecated Read `messageInfo.sending`. */
286
+ sending: number
287
+ /** @deprecated Read `messageInfo.sentToServer`. */
288
+ sentToServer: number
289
+ /** @deprecated Read `messageInfo.received`. */
290
+ received: number
291
+ /** @deprecated Read `messageInfo.viewed`. */
292
+ viewed: number
293
+ /** @deprecated Read `messageInfo.played`. */
294
+ played: number
295
+ /** @deprecated Read `messageInfo.answered`. */
296
+ answered: number
297
+ /** @deprecated Same as `messageInfo.sentToServer`. */
298
+ sentAndNotReceived: number
153
299
  }
154
300
 
301
+ /** Whole numbers, `0` or more (numeric strings are accepted). */
155
302
  export type SetIntervalPayload = {
156
- minInterval: string
157
- maxInterval: string
303
+ minInterval: number | string
304
+ maxInterval: number | string
158
305
  }
159
306
 
160
307
  export type ExportCampaignResultPayload = {
161
- campaignId: string
308
+ where: { campaignId: string; [key: string]: unknown }
309
+ /** `'semiColon'` separates the columns with `;` (default `,`). */
310
+ type?: 'semiColon'
162
311
  }
163
312
 
164
313
  export type ExportTemplateCsvPayload = {
@@ -166,10 +315,12 @@ export type ExportTemplateCsvPayload = {
166
315
  }
167
316
 
168
317
  export type AutoPauseModePayload = {
169
- mode: string
318
+ mode: 'low-health' | 'medium-health' | 'disabled'
170
319
  }
171
320
 
172
321
  export type HsmLimit = {
173
322
  hsmLimit: number
174
323
  hsmUsedLimit: number
324
+ /** The HSM messages still available (`0` when none or the plan has no WhatsApp Business); `true` when the limit is disabled. */
325
+ hasLimit: number | boolean
175
326
  }
@@ -6,6 +6,15 @@ import type { AnswersOverview } from './answers/types'
6
6
  import { BotSimulatorsApi } from './botSimulators/BotSimulatorsApi'
7
7
  import { BotsApi } from './bots/BotsApi'
8
8
  import type { Bot, BotContextInput, BotParsedContext } from './bots/types'
9
+ import { CampaignsApi } from './campaigns/CampaignsApi'
10
+ import type {
11
+ Campaign,
12
+ CampaignContact,
13
+ CampaignContactsPage,
14
+ CampaignMessage,
15
+ CampaignStats,
16
+ CampaignTimelineEvent,
17
+ } from './campaigns/types'
9
18
  import { CardsApi } from './cards/CardsApi'
10
19
  import { ContactsApi } from './contacts/ContactsApi'
11
20
  import { KanbanColumnsApi } from './kanbanColumns/KanbanColumnsApi'
@@ -20,7 +29,7 @@ import type { ScheduleRecord } from './schedule/types'
20
29
  import { TagsApi } from './tags/TagsApi'
21
30
  import { TicketTopicsApi } from './ticketTopics/TicketTopicsApi'
22
31
  import { TicketsApi } from './tickets/TicketsApi'
23
- import type { Ticket, TicketTransfer } from './tickets/types'
32
+ import type { Ticket, TicketTransfer, TicketsCount, TicketsCounts } from './tickets/types'
24
33
  import { TimetableApi } from './timetable/TimetableApi'
25
34
  import { UsersApi } from './users/UsersApi'
26
35
 
@@ -127,6 +136,35 @@ declare const transfer: TicketTransfer
127
136
  expectType<string | null>(transfer.byUserId)
128
137
  expectType<'opened' | 'transferred' | 'closed' | null>(transfer.action)
129
138
 
139
+ // ── tickets: the counts, the first waiting time and the exports ─────
140
+
141
+ expectType<Promise<TicketsCount>>(tickets.count({ where: { isOpen: true } }))
142
+ expectType<Promise<TicketsCounts>>(tickets.count({ queries: [{ where: { isOpen: true } }] }))
143
+ expectType<Promise<TicketsCount>>(tickets.countPost({ where: { isOpen: true }, query: {} }))
144
+ expectType<Promise<TicketsCounts>>(
145
+ tickets.countPost({ query: { queries: [{ where: { isOpen: false } }] } }),
146
+ )
147
+ // `query` is required: without it the route answers 500
148
+ expectError(tickets.countPost({ where: { isOpen: true } }))
149
+ expectType<Promise<number>>(tickets.countOpenByService('s-1'))
150
+ expectType<Promise<number>>(tickets.countOpenByDepartment('d-1'))
151
+ expectType<Promise<number>>(tickets.countOpenByUser('u-1'))
152
+ expectType<Promise<string>>(tickets.getFirstWaitingTime('t-1'))
153
+ expectType<Promise<string>>(tickets.exportTxt('202610061675'))
154
+ expectType<Promise<string>>(
155
+ tickets.exportHistory({ include: [{ model: 'contact', include: ['service'] }] }),
156
+ )
157
+ expectType<Promise<void>>(tickets.closeMany({ ids: ['t-1'], comments: 'Resolved' }))
158
+ expectType<Promise<void>>(
159
+ tickets.closeMany({
160
+ allContactsSelected: true,
161
+ query: { query: '{}' },
162
+ ticketTopicIds: ['tt-1'],
163
+ }),
164
+ )
165
+ tickets.getById('t-1', { include: ['ticketTopics', 'firstMessage', 'account'] })
166
+ expectType<string | undefined>(ticket.metrics.maxWaitingTimeDepartmentId)
167
+
130
168
  // ── bots: the /bots routes take and return the contexts as a list ───
131
169
 
132
170
  const bots = new BotsApi(client)
@@ -263,3 +301,38 @@ const quickReplies = new QuickRepliesApi(client)
263
301
  quickReplies.updateById('r-1', { title: 'Oi', text: 'Olá', files: [] })
264
302
  expectError(quickReplies.updateById('r-1', { title: 'Oi', text: 'Olá' }))
265
303
  expectType<Promise<'OK'>>(quickReplies.deleteById('r-1'))
304
+
305
+ // ── campaigns: the actions answer booleans or 'OK'; the CSV export takes a where ──
306
+
307
+ const campaigns = new CampaignsApi(client)
308
+ expectType<Promise<Campaign>>(campaigns.sendById('c-1'))
309
+ expectType<Promise<boolean>>(campaigns.pauseById('c-1'))
310
+ expectType<Promise<boolean>>(campaigns.resumeById('c-1'))
311
+ expectType<Promise<'OK' | false>>(campaigns.retryById('c-1'))
312
+ expectType<Promise<boolean>>(campaigns.setIntervalById('c-1', { minInterval: 5, maxInterval: 10 }))
313
+ expectType<Promise<CampaignStats>>(campaigns.getStatsById('c-1'))
314
+ expectType<Promise<CampaignTimelineEvent[]>>(campaigns.getTimeline('c-1'))
315
+ expectType<Promise<CampaignContactsPage>>(
316
+ campaigns.getContacts('c-1', { contactStatus: ['not_sent', 'read'], sortDir: 'DESC' }),
317
+ )
318
+ expectType<Promise<number>>(campaigns.countReadyByService('s-1'))
319
+ expectType<Promise<'OK'>>(campaigns.deleteById('c-1'))
320
+ expectType<Promise<true>>(campaigns.deleteMany(['c-1']))
321
+ expectType<Promise<string>>(campaigns.exportCampaignResult({ where: { campaignId: 'c-1' } }))
322
+ expectType<Promise<string>>(campaigns.exportImportErrors('c-1', { delimiter: ';' }))
323
+ // the export takes the campaign in `where`: `{ campaignId }` answers 400
324
+ expectError(campaigns.exportCampaignResult({ campaignId: 'c-1' }))
325
+ expectError(campaigns.setAutoPauseMode({ mode: 'auto' }))
326
+ campaigns.getMany({
327
+ status: 'scheduled',
328
+ q: 'Black Friday',
329
+ createdAtFrom: '2026-10-01T00:00:00.000Z',
330
+ })
331
+ expectError(campaigns.getMany({ status: 'sending' }))
332
+ declare const listed: Campaign
333
+ expectType<{ sent: number; viewed: number } | undefined>(listed.stats)
334
+ // a new campaign answers `config: {}`; the include answers the campaign messages, not chat messages
335
+ expectType<number | string | undefined>(listed.config.minInterval)
336
+ expectType<CampaignMessage[] | undefined>(listed.messages)
337
+ declare const campaignContact: CampaignContact
338
+ expectType<string | null>(campaignContact.contactNumber)