@ikatec/digisac-api-sdk 4.14.0 → 4.16.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.
Files changed (217) hide show
  1. package/README.md +10 -3
  2. package/dist/apis/absence/AbsenceApi.cjs +2 -2
  3. package/dist/apis/absence/AbsenceApi.cjs.map +1 -1
  4. package/dist/apis/absence/AbsenceApi.d.ts +2 -2
  5. package/dist/apis/absence/AbsenceApi.mjs +2 -2
  6. package/dist/apis/absence/AbsenceApi.mjs.map +1 -1
  7. package/dist/apis/absence/types.d.ts +1 -1
  8. package/dist/apis/answers/AnswersApi.cjs +23 -2
  9. package/dist/apis/answers/AnswersApi.cjs.map +1 -1
  10. package/dist/apis/answers/AnswersApi.d.ts +16 -2
  11. package/dist/apis/answers/AnswersApi.d.ts.map +1 -1
  12. package/dist/apis/answers/AnswersApi.mjs +23 -2
  13. package/dist/apis/answers/AnswersApi.mjs.map +1 -1
  14. package/dist/apis/answers/types.d.ts +1 -2
  15. package/dist/apis/answers/types.d.ts.map +1 -1
  16. package/dist/apis/authHistory/AuthHistoryApi.cjs +9 -2
  17. package/dist/apis/authHistory/AuthHistoryApi.cjs.map +1 -1
  18. package/dist/apis/authHistory/AuthHistoryApi.d.ts +7 -2
  19. package/dist/apis/authHistory/AuthHistoryApi.d.ts.map +1 -1
  20. package/dist/apis/authHistory/AuthHistoryApi.mjs +9 -2
  21. package/dist/apis/authHistory/AuthHistoryApi.mjs.map +1 -1
  22. package/dist/apis/authHistory/types.d.ts +6 -1
  23. package/dist/apis/authHistory/types.d.ts.map +1 -1
  24. package/dist/apis/bots/BotsApi.cjs +16 -7
  25. package/dist/apis/bots/BotsApi.cjs.map +1 -1
  26. package/dist/apis/bots/BotsApi.d.ts +13 -7
  27. package/dist/apis/bots/BotsApi.d.ts.map +1 -1
  28. package/dist/apis/bots/BotsApi.mjs +16 -7
  29. package/dist/apis/bots/BotsApi.mjs.map +1 -1
  30. package/dist/apis/bots/types.d.ts +12 -2
  31. package/dist/apis/bots/types.d.ts.map +1 -1
  32. package/dist/apis/campaigns/CampaignsApi.cjs +25 -1
  33. package/dist/apis/campaigns/CampaignsApi.cjs.map +1 -1
  34. package/dist/apis/campaigns/CampaignsApi.d.ts +16 -1
  35. package/dist/apis/campaigns/CampaignsApi.d.ts.map +1 -1
  36. package/dist/apis/campaigns/CampaignsApi.mjs +25 -1
  37. package/dist/apis/campaigns/CampaignsApi.mjs.map +1 -1
  38. package/dist/apis/campaigns/types.d.ts +98 -6
  39. package/dist/apis/campaigns/types.d.ts.map +1 -1
  40. package/dist/apis/cards/types.d.ts +5 -5
  41. package/dist/apis/categories/CategoriesApi.cjs +1 -2
  42. package/dist/apis/categories/CategoriesApi.cjs.map +1 -1
  43. package/dist/apis/categories/CategoriesApi.d.ts +1 -2
  44. package/dist/apis/categories/CategoriesApi.d.ts.map +1 -1
  45. package/dist/apis/categories/CategoriesApi.mjs +1 -2
  46. package/dist/apis/categories/CategoriesApi.mjs.map +1 -1
  47. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs +2 -3
  48. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs.map +1 -1
  49. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts +2 -3
  50. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts.map +1 -1
  51. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs +2 -3
  52. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs.map +1 -1
  53. package/dist/apis/contacts/ContactsApi.cjs +20 -6
  54. package/dist/apis/contacts/ContactsApi.cjs.map +1 -1
  55. package/dist/apis/contacts/ContactsApi.d.ts +28 -4
  56. package/dist/apis/contacts/ContactsApi.d.ts.map +1 -1
  57. package/dist/apis/contacts/ContactsApi.mjs +20 -6
  58. package/dist/apis/contacts/ContactsApi.mjs.map +1 -1
  59. package/dist/apis/contacts/types.d.ts +101 -2
  60. package/dist/apis/contacts/types.d.ts.map +1 -1
  61. package/dist/apis/customFields/types.d.ts +4 -4
  62. package/dist/apis/dashboard/DashboardApi.cjs +3 -3
  63. package/dist/apis/dashboard/DashboardApi.cjs.map +1 -1
  64. package/dist/apis/dashboard/DashboardApi.d.ts +3 -3
  65. package/dist/apis/dashboard/DashboardApi.mjs +3 -3
  66. package/dist/apis/dashboard/DashboardApi.mjs.map +1 -1
  67. package/dist/apis/dashboard/types.d.ts +11 -4
  68. package/dist/apis/dashboard/types.d.ts.map +1 -1
  69. package/dist/apis/departments/DepartmentsApi.cjs +5 -5
  70. package/dist/apis/departments/DepartmentsApi.cjs.map +1 -1
  71. package/dist/apis/departments/DepartmentsApi.d.ts +5 -5
  72. package/dist/apis/departments/DepartmentsApi.mjs +5 -5
  73. package/dist/apis/departments/DepartmentsApi.mjs.map +1 -1
  74. package/dist/apis/departments/types.d.ts +6 -0
  75. package/dist/apis/departments/types.d.ts.map +1 -1
  76. package/dist/apis/distribution/DistributionApi.cjs +3 -4
  77. package/dist/apis/distribution/DistributionApi.cjs.map +1 -1
  78. package/dist/apis/distribution/DistributionApi.d.ts +3 -4
  79. package/dist/apis/distribution/DistributionApi.d.ts.map +1 -1
  80. package/dist/apis/distribution/DistributionApi.mjs +3 -4
  81. package/dist/apis/distribution/DistributionApi.mjs.map +1 -1
  82. package/dist/apis/files/FilesApi.cjs +13 -1
  83. package/dist/apis/files/FilesApi.cjs.map +1 -1
  84. package/dist/apis/files/FilesApi.d.ts +12 -2
  85. package/dist/apis/files/FilesApi.d.ts.map +1 -1
  86. package/dist/apis/files/FilesApi.mjs +13 -1
  87. package/dist/apis/files/FilesApi.mjs.map +1 -1
  88. package/dist/apis/files/types.d.ts +19 -1
  89. package/dist/apis/files/types.d.ts.map +1 -1
  90. package/dist/apis/kanbanCards/KanbanCardsApi.cjs +32 -0
  91. package/dist/apis/kanbanCards/KanbanCardsApi.cjs.map +1 -1
  92. package/dist/apis/kanbanCards/KanbanCardsApi.d.ts +27 -1
  93. package/dist/apis/kanbanCards/KanbanCardsApi.d.ts.map +1 -1
  94. package/dist/apis/kanbanCards/KanbanCardsApi.mjs +32 -0
  95. package/dist/apis/kanbanCards/KanbanCardsApi.mjs.map +1 -1
  96. package/dist/apis/kanbanCards/types.d.ts +1 -1
  97. package/dist/apis/kanbanCards/types.d.ts.map +1 -1
  98. package/dist/apis/kanbanColumns/KanbanColumnsApi.cjs +19 -0
  99. package/dist/apis/kanbanColumns/KanbanColumnsApi.cjs.map +1 -1
  100. package/dist/apis/kanbanColumns/KanbanColumnsApi.d.ts +16 -1
  101. package/dist/apis/kanbanColumns/KanbanColumnsApi.d.ts.map +1 -1
  102. package/dist/apis/kanbanColumns/KanbanColumnsApi.mjs +19 -0
  103. package/dist/apis/kanbanColumns/KanbanColumnsApi.mjs.map +1 -1
  104. package/dist/apis/kanbanColumns/types.d.ts +4 -0
  105. package/dist/apis/kanbanColumns/types.d.ts.map +1 -1
  106. package/dist/apis/messages/MessagesApi.cjs +13 -4
  107. package/dist/apis/messages/MessagesApi.cjs.map +1 -1
  108. package/dist/apis/messages/MessagesApi.d.ts +13 -4
  109. package/dist/apis/messages/MessagesApi.d.ts.map +1 -1
  110. package/dist/apis/messages/MessagesApi.mjs +13 -4
  111. package/dist/apis/messages/MessagesApi.mjs.map +1 -1
  112. package/dist/apis/messages/types.d.ts +5 -0
  113. package/dist/apis/messages/types.d.ts.map +1 -1
  114. package/dist/apis/pipeline/types.d.ts +7 -8
  115. package/dist/apis/pipeline/types.d.ts.map +1 -1
  116. package/dist/apis/quickReplies/types.d.ts +4 -1
  117. package/dist/apis/quickReplies/types.d.ts.map +1 -1
  118. package/dist/apis/schedule/types.d.ts +41 -10
  119. package/dist/apis/schedule/types.d.ts.map +1 -1
  120. package/dist/apis/terms/TermsApi.cjs +2 -2
  121. package/dist/apis/terms/TermsApi.cjs.map +1 -1
  122. package/dist/apis/terms/TermsApi.d.ts +2 -2
  123. package/dist/apis/terms/TermsApi.mjs +2 -2
  124. package/dist/apis/terms/TermsApi.mjs.map +1 -1
  125. package/dist/apis/tickets/TicketsApi.cjs +18 -8
  126. package/dist/apis/tickets/TicketsApi.cjs.map +1 -1
  127. package/dist/apis/tickets/TicketsApi.d.ts +15 -8
  128. package/dist/apis/tickets/TicketsApi.d.ts.map +1 -1
  129. package/dist/apis/tickets/TicketsApi.mjs +18 -8
  130. package/dist/apis/tickets/TicketsApi.mjs.map +1 -1
  131. package/dist/apis/tickets/types.d.ts +4 -1
  132. package/dist/apis/tickets/types.d.ts.map +1 -1
  133. package/dist/apis/timetable/TimetableApi.cjs +6 -0
  134. package/dist/apis/timetable/TimetableApi.cjs.map +1 -1
  135. package/dist/apis/timetable/TimetableApi.d.ts +6 -0
  136. package/dist/apis/timetable/TimetableApi.d.ts.map +1 -1
  137. package/dist/apis/timetable/TimetableApi.mjs +6 -0
  138. package/dist/apis/timetable/TimetableApi.mjs.map +1 -1
  139. package/dist/apis/timetable/types.d.ts +8 -0
  140. package/dist/apis/timetable/types.d.ts.map +1 -1
  141. package/dist/apis/users/UsersApi.cjs +11 -3
  142. package/dist/apis/users/UsersApi.cjs.map +1 -1
  143. package/dist/apis/users/UsersApi.d.ts +10 -4
  144. package/dist/apis/users/UsersApi.d.ts.map +1 -1
  145. package/dist/apis/users/UsersApi.mjs +11 -3
  146. package/dist/apis/users/UsersApi.mjs.map +1 -1
  147. package/dist/apis/users/types.d.ts +26 -0
  148. package/dist/apis/users/types.d.ts.map +1 -1
  149. package/dist/apis/webhooks/types.d.ts +51 -6
  150. package/dist/apis/webhooks/types.d.ts.map +1 -1
  151. package/dist/core/BaseReadApi.cjs +2 -2
  152. package/dist/core/BaseReadApi.cjs.map +1 -1
  153. package/dist/core/BaseReadApi.d.ts +2 -2
  154. package/dist/core/BaseReadApi.mjs +2 -2
  155. package/dist/core/BaseReadApi.mjs.map +1 -1
  156. package/dist/core/errors.cjs +4 -1
  157. package/dist/core/errors.cjs.map +1 -1
  158. package/dist/core/errors.d.ts +4 -1
  159. package/dist/core/errors.d.ts.map +1 -1
  160. package/dist/core/errors.mjs +4 -1
  161. package/dist/core/errors.mjs.map +1 -1
  162. package/package.json +1 -1
  163. package/src/apis/absence/AbsenceApi.ts +2 -2
  164. package/src/apis/absence/types.ts +1 -1
  165. package/src/apis/answers/AnswersApi.test.ts +32 -0
  166. package/src/apis/answers/AnswersApi.ts +38 -2
  167. package/src/apis/answers/types.ts +1 -2
  168. package/src/apis/authHistory/AuthHistoryApi.test.ts +16 -0
  169. package/src/apis/authHistory/AuthHistoryApi.ts +10 -2
  170. package/src/apis/authHistory/types.ts +6 -1
  171. package/src/apis/bots/BotsApi.test.ts +16 -0
  172. package/src/apis/bots/BotsApi.ts +25 -7
  173. package/src/apis/bots/types.ts +12 -2
  174. package/src/apis/campaigns/CampaignsApi.test.ts +48 -0
  175. package/src/apis/campaigns/CampaignsApi.ts +31 -2
  176. package/src/apis/campaigns/types.ts +101 -3
  177. package/src/apis/cards/types.ts +5 -5
  178. package/src/apis/categories/CategoriesApi.ts +1 -2
  179. package/src/apis/clientFeedback/ClientFeedbackApi.ts +2 -3
  180. package/src/apis/contacts/ContactsApi.test.ts +49 -2
  181. package/src/apis/contacts/ContactsApi.ts +64 -4
  182. package/src/apis/contacts/types.ts +111 -1
  183. package/src/apis/creditMovements/CreditMovementsApi.test.ts +1 -1
  184. package/src/apis/customFields/types.ts +4 -4
  185. package/src/apis/dashboard/DashboardApi.test-d.ts +9 -0
  186. package/src/apis/dashboard/DashboardApi.ts +3 -3
  187. package/src/apis/dashboard/types.ts +11 -4
  188. package/src/apis/departments/DepartmentsApi.ts +5 -5
  189. package/src/apis/departments/types.ts +13 -0
  190. package/src/apis/distribution/DistributionApi.ts +3 -4
  191. package/src/apis/files/FilesApi.test.ts +21 -0
  192. package/src/apis/files/FilesApi.ts +19 -1
  193. package/src/apis/files/types.ts +20 -1
  194. package/src/apis/kanbanCards/KanbanCardsApi.test.ts +21 -0
  195. package/src/apis/kanbanCards/KanbanCardsApi.ts +47 -1
  196. package/src/apis/kanbanCards/types.ts +1 -1
  197. package/src/apis/kanbanColumns/KanbanColumnsApi.test.ts +26 -0
  198. package/src/apis/kanbanColumns/KanbanColumnsApi.ts +31 -0
  199. package/src/apis/kanbanColumns/types.ts +5 -0
  200. package/src/apis/messages/MessagesApi.ts +13 -4
  201. package/src/apis/messages/types.ts +5 -0
  202. package/src/apis/payloads.test-d.ts +245 -5
  203. package/src/apis/pipeline/types.ts +7 -8
  204. package/src/apis/queryFormat.test.ts +13 -0
  205. package/src/apis/quickReplies/types.ts +4 -1
  206. package/src/apis/schedule/types.ts +42 -10
  207. package/src/apis/terms/TermsApi.ts +2 -2
  208. package/src/apis/tickets/TicketsApi.ts +28 -8
  209. package/src/apis/tickets/types.ts +4 -1
  210. package/src/apis/timetable/TimetableApi.ts +6 -0
  211. package/src/apis/timetable/types.ts +9 -0
  212. package/src/apis/users/UsersApi.test.ts +17 -0
  213. package/src/apis/users/UsersApi.ts +21 -3
  214. package/src/apis/users/types.ts +28 -0
  215. package/src/apis/webhooks/types.ts +53 -6
  216. package/src/core/BaseReadApi.ts +2 -2
  217. package/src/core/errors.ts +4 -1
@@ -32,3 +32,16 @@ export type ArchiveDepartmentPayload = {
32
32
  /** `true` archives, `false` restores. */
33
33
  archive: boolean
34
34
  }
35
+
36
+ /** The `extra` of the 403 `Cannot archive department.` error of `archiveWithValidation`. */
37
+ export type ArchiveDepartmentErrorExtra = {
38
+ reasons: (
39
+ | 'ONLY_ONE'
40
+ | 'USED_BOT'
41
+ | 'DEFAULT_DEPARTMENT'
42
+ | 'VISIBILITY_SETTINGS'
43
+ | 'OPEN_TICKETS'
44
+ )[]
45
+ /** Only with open tickets. */
46
+ openTicketsCount?: number
47
+ }
@@ -34,10 +34,9 @@ export class DistributionApi extends BaseCrudApi<
34
34
  }
35
35
 
36
36
  /**
37
- * Updates a distribution. The backend reads the id from the body, so it is sent there too. It replaces
38
- * the links: omitting `roles` or `departments` clears them. Resolves the stored distribution (with
39
- * `deletedAt`, without the links) or, when `departments` is sent, the updated departments. An unknown id
40
- * answers 500.
37
+ * Updates a distribution. It replaces the links: omitting `roles` or `departments` clears them.
38
+ * Resolves the stored distribution (with `deletedAt`, without the links) or, when `departments`
39
+ * is sent, the updated departments. An unknown id answers 500.
41
40
  * @permissions distribution.view OR distribution.update
42
41
  */
43
42
  override updateById(
@@ -97,6 +97,27 @@ describe('FilesApi', () => {
97
97
  })
98
98
  })
99
99
 
100
+ describe('duplicateFile', () => {
101
+ it('calls client.post with /files/duplicate-file and resolves the id of the copy', async () => {
102
+ vi.mocked(client.post).mockResolvedValue('file-2')
103
+ const result = await api.duplicateFile({ fileId: 'file-1' })
104
+ expect(client.post).toHaveBeenCalledWith(
105
+ '/files/duplicate-file',
106
+ { fileId: 'file-1' },
107
+ undefined,
108
+ )
109
+ expect(result).toBe('file-2')
110
+ })
111
+
112
+ it('sends urlToUpload and serviceId in the body and forwards custom headers', async () => {
113
+ vi.mocked(client.post).mockResolvedValue('file-1')
114
+ const headers = { 'X-Req': 'r-1' }
115
+ const body = { fileId: 'file-1', urlToUpload: URL_TO_UPLOAD, serviceId: 'svc-1' }
116
+ await api.duplicateFile(body, headers)
117
+ expect(client.post).toHaveBeenCalledWith('/files/duplicate-file', body, headers)
118
+ })
119
+ })
120
+
100
121
  describe('deleteById', () => {
101
122
  it('calls client.delete with /files/:id and resolves { success }', async () => {
102
123
  vi.mocked(client.delete).mockResolvedValue({ success: true })
@@ -9,6 +9,7 @@ import type {
9
9
  CreateToUploadPayload,
10
10
  CreateToUploadResult,
11
11
  DeleteFileResult,
12
+ DuplicateFilePayload,
12
13
  } from './types'
13
14
 
14
15
  /**
@@ -85,7 +86,8 @@ export class FilesApi extends BaseCrudApi<
85
86
  * Creates the record of a file and returns the signed URL to upload its content: send the
86
87
  * content with {@link upload}. An audio that must be converted (a voice message for the
87
88
  * connection) is sent in `base64` instead, and then no `urlToUpload` is returned. `name` and
88
- * `mimetype` are required (500 without them; an audio without `name` is named `recording`).
89
+ * `mimetype` are required (500 without them; an audio without `name` is named `recording`). With
90
+ * `serviceId`, an extension the account blocks for the type of the connection answers 403.
89
91
  */
90
92
  createToUpload(
91
93
  body: CreateToUploadPayload,
@@ -98,6 +100,22 @@ export class FilesApi extends BaseCrudApi<
98
100
  )
99
101
  }
100
102
 
103
+ /**
104
+ * Copies the content of a file. With `urlToUpload` (the signed URL of a record created by
105
+ * {@link createToUpload} with the same `mimetype` as the file copied), the content is sent to that
106
+ * record and the answer is the `fileId` given; without it, a new record (`attachedType`
107
+ * `'message.file'`) is created with the copied content and its id is the answer, and with
108
+ * `serviceId` an extension the account blocks for the type of the connection answers 403. An
109
+ * unknown `fileId` answers 500.
110
+ */
111
+ duplicateFile(body: DuplicateFilePayload, headers?: Record<string, string>): Promise<string> {
112
+ return this.client.post<string, DuplicateFilePayload>(
113
+ `${this.basePath}/duplicate-file`,
114
+ body,
115
+ headers,
116
+ )
117
+ }
118
+
101
119
  /**
102
120
  * Uploads the content of a file to the `urlToUpload` returned by {@link createToUpload}. The URL
103
121
  * is signed by the storage, so the request goes straight to it, without the API token (the
@@ -74,7 +74,10 @@ export type CreateToUploadPayload = {
74
74
  mimetype: string
75
75
  /** What the file is attached to, e.g. `'message.file'` (default), `'campaign.file'`, `'quickreply.file'`. */
76
76
  attachedType?: string
77
- /** Connection that will send the file (used to decide the audio conversion). */
77
+ /**
78
+ * Connection that will send the file: used to decide the audio conversion and to apply the
79
+ * extensions the account blocks for its type (403).
80
+ */
78
81
  serviceId?: string
79
82
  /** Audio sent as a voice message (default `true`). */
80
83
  isPtt?: boolean
@@ -93,6 +96,22 @@ export type CreateToUploadResult = {
93
96
  mimetype: string
94
97
  }
95
98
 
99
+ /** Body of `FilesApi.duplicateFile`. */
100
+ export type DuplicateFilePayload = {
101
+ /** The file whose content is copied. */
102
+ fileId: string
103
+ /**
104
+ * Signed URL of a record created by `FilesApi.createToUpload`, with the same `mimetype` as the file
105
+ * copied, to receive the content. Without it, a new record is created with the copied content.
106
+ */
107
+ urlToUpload?: string
108
+ /**
109
+ * Connection that will send the new record (used only without `urlToUpload`): the extensions the
110
+ * account blocks for its type apply.
111
+ */
112
+ serviceId?: string
113
+ }
114
+
96
115
  /** Result of `FilesApi.deleteById`. */
97
116
  export type DeleteFileResult = {
98
117
  success: boolean
@@ -43,4 +43,25 @@ describe('KanbanCardsApi', () => {
43
43
  await expect(api.reorder('kc1', {})).rejects.toThrow('Invalid reorder operation.')
44
44
  })
45
45
  })
46
+
47
+ describe('getById / create / updateById', () => {
48
+ it('keep the generic routes of /kanban-cards', async () => {
49
+ vi.mocked(client.get).mockResolvedValue({ id: 'kc1' })
50
+ vi.mocked(client.post).mockResolvedValue({ id: 'kc1' })
51
+ vi.mocked(client.put).mockResolvedValue({ id: 'kc1' })
52
+ await api.getById('kc1', { attributes: ['id'] }, headers)
53
+ await api.create({ columnId: 'col1', ticketId: 't1' }, headers)
54
+ await api.updateById('kc1', { columnId: 'col2' }, headers)
55
+ expect(client.get).toHaveBeenCalledWith(
56
+ `/kanban-cards/kc1?query=${encodeURIComponent(JSON.stringify({ attributes: ['id'] }))}`,
57
+ headers,
58
+ )
59
+ expect(client.post).toHaveBeenCalledWith(
60
+ '/kanban-cards',
61
+ { columnId: 'col1', ticketId: 't1' },
62
+ headers,
63
+ )
64
+ expect(client.put).toHaveBeenCalledWith('/kanban-cards/kc1', { columnId: 'col2' }, headers)
65
+ })
66
+ })
46
67
  })
@@ -1,5 +1,5 @@
1
1
  import type { ApiClient } from '../../core/ApiClient'
2
- import type { ListQuery } from '../../core/types'
2
+ import type { GetByIdQuery, ListQuery } from '../../core/types'
3
3
  import { BaseCrudApi } from '../../core/BaseCrudApi'
4
4
  import type {
5
5
  KanbanCard,
@@ -8,6 +8,13 @@ import type {
8
8
  UpdateKanbanCardPayload,
9
9
  } from './types'
10
10
 
11
+ /**
12
+ * Cards of the kanban boards: one per ticket in each board. The reads need `kanban.boards.view`
13
+ * and the writes `kanban.cards.move`. `getMany` takes the column in `where.columnId`. The errors of
14
+ * the writes (unknown column or ticket, ticket of a department not linked to the board, a second
15
+ * card of the ticket in the board, an update by a user outside the department of the ticket)
16
+ * answer 500.
17
+ */
11
18
  export class KanbanCardsApi extends BaseCrudApi<
12
19
  KanbanCard,
13
20
  CreateKanbanCardPayload,
@@ -19,6 +26,45 @@ export class KanbanCardsApi extends BaseCrudApi<
19
26
  super(client, '/kanban-cards')
20
27
  }
21
28
 
29
+ /**
30
+ * Returns a card by ID. An unknown id answers 404 `Kanban card not found.`.
31
+ * @permissions kanban.boards.view
32
+ */
33
+ override getById(
34
+ id: string,
35
+ query?: GetByIdQuery<KanbanCard>,
36
+ headers?: Record<string, string>,
37
+ ): Promise<KanbanCard> {
38
+ return super.getById(id, query, headers)
39
+ }
40
+
41
+ /**
42
+ * Creates the card of a ticket in a column. The ticket must be of a department linked to the
43
+ * board and have no card in it yet.
44
+ * @permissions kanban.cards.move
45
+ */
46
+ override create(
47
+ body: CreateKanbanCardPayload,
48
+ headers?: Record<string, string>,
49
+ ): Promise<KanbanCard> {
50
+ return super.create(body, headers)
51
+ }
52
+
53
+ /**
54
+ * Updates a card. To move it, use {@link reorder}: `updateById` does not recalculate the
55
+ * `position` and does not close or reopen the ticket. The user must belong to the
56
+ * department of the card's ticket (of the new `ticketId` when sent), or have
57
+ * `tickets.transfer.all`, or be admin; otherwise 500.
58
+ * @permissions kanban.cards.move
59
+ */
60
+ override updateById(
61
+ id: string,
62
+ body: UpdateKanbanCardPayload,
63
+ headers?: Record<string, string>,
64
+ ): Promise<KanbanCard> {
65
+ return super.updateById(id, body, headers)
66
+ }
67
+
22
68
  /**
23
69
  * Deletes a card. Resolves the text `'OK'`.
24
70
  * @permissions kanban.cards.move
@@ -27,7 +27,7 @@ export type CreateKanbanCardPayload = {
27
27
  export type UpdateKanbanCardPayload = {
28
28
  columnId?: string
29
29
  position?: string
30
- /** The caller must share a department with the ticket (or transfer to all / be admin). */
30
+ /** Moves the card to another ticket (see `KanbanCardsApi.updateById` for who can update it). */
31
31
  ticketId?: string
32
32
  }
33
33
 
@@ -43,4 +43,30 @@ describe('KanbanColumnsApi', () => {
43
43
  await expect(api.reorder('col1', {})).rejects.toThrow('Invalid reorder operation.')
44
44
  })
45
45
  })
46
+
47
+ describe('deleteById', () => {
48
+ it('calls client.delete with /kanban-columns/:id and resolves the deleted column', async () => {
49
+ vi.mocked(client.delete).mockResolvedValue({ id: 'col1' })
50
+ expect(await api.deleteById('col1', headers)).toEqual({ id: 'col1' })
51
+ expect(client.delete).toHaveBeenCalledWith('/kanban-columns/col1', headers)
52
+ })
53
+ })
54
+
55
+ describe('deleteMovingCards', () => {
56
+ it('sends DELETE /kanban-columns/:id with the destination column in the body', async () => {
57
+ vi.mocked(client.request).mockResolvedValue({ id: 'col1' })
58
+ const body = { cardsDestinationColumnId: 'col2' }
59
+ expect(await api.deleteMovingCards('col1', body, headers)).toEqual({ id: 'col1' })
60
+ expect(client.request).toHaveBeenCalledWith('DELETE', '/kanban-columns/col1', body, headers)
61
+ })
62
+
63
+ it('propagates errors', async () => {
64
+ vi.mocked(client.request).mockRejectedValue(
65
+ new Error('Initial and final columns cannot be deleted.'),
66
+ )
67
+ await expect(
68
+ api.deleteMovingCards('col1', { cardsDestinationColumnId: 'col2' }),
69
+ ).rejects.toThrow('Initial and final columns cannot be deleted.')
70
+ })
71
+ })
46
72
  })
@@ -3,6 +3,7 @@ import { BaseCrudApi } from '../../core/BaseCrudApi'
3
3
  import type {
4
4
  KanbanColumn,
5
5
  CreateKanbanColumnPayload,
6
+ DeleteKanbanColumnPayload,
6
7
  ReorderKanbanColumnPayload,
7
8
  UpdateKanbanColumnPayload,
8
9
  } from './types'
@@ -16,6 +17,36 @@ export class KanbanColumnsApi extends BaseCrudApi<
16
17
  super(client, '/kanban-columns')
17
18
  }
18
19
 
20
+ /**
21
+ * Deletes a column and its cards (their tickets are kept as they are). The `'initial'` and
22
+ * `'final'` columns cannot be deleted (500). Resolves to the deleted column. To keep the cards,
23
+ * use {@link deleteMovingCards}.
24
+ * @permissions kanban.columns.destroy
25
+ */
26
+ override deleteById(id: string, headers?: Record<string, string>): Promise<KanbanColumn> {
27
+ return super.deleteById(id, headers)
28
+ }
29
+
30
+ /**
31
+ * Deletes a column after moving its cards to the top of another column of the board, in their
32
+ * order. The cards move as `KanbanCardsApi.reorder` moves them: to the `'final'` column,
33
+ * their open tickets are closed. The `'initial'` and `'final'` columns cannot be deleted (500).
34
+ * Resolves to the deleted column.
35
+ * @permissions kanban.columns.destroy
36
+ */
37
+ deleteMovingCards(
38
+ id: string,
39
+ body: DeleteKanbanColumnPayload,
40
+ headers?: Record<string, string>,
41
+ ): Promise<KanbanColumn> {
42
+ return this.client.request<KanbanColumn, DeleteKanbanColumnPayload>(
43
+ 'DELETE',
44
+ `${this.basePath}/${id}`,
45
+ body,
46
+ headers,
47
+ )
48
+ }
49
+
19
50
  /**
20
51
  * Moves a column between two neighbours of the same board. Both the column and a neighbour
21
52
  * must be draggable; swapped neighbours (`before` after `after`) fail with 500. Resolves to the
@@ -50,3 +50,8 @@ export type ReorderKanbanColumnPayload = {
50
50
  /** The column that ends up on the right. */
51
51
  afterColumnId?: string
52
52
  }
53
+
54
+ export type DeleteKanbanColumnPayload = {
55
+ /** The column of the same board that receives the cards of the deleted one. */
56
+ cardsDestinationColumnId: string
57
+ }
@@ -24,10 +24,19 @@ export class MessagesApi extends BaseCrudApi<Message, CreateMessagePayload, Upda
24
24
  }
25
25
 
26
26
  /**
27
- * Edits the text (or the file) of a message sent by the account, up to 15 minutes after it was
28
- * sent (403 `Cannot edit message` after that). `contactId` is required (without it the route
29
- * answers 500), and `text` or `file` (400 without both). Sent as `PATCH /messages/:id`.
30
- * @permissions messages.update
27
+ * Edits the text of a message sent by the account, up to 15 minutes after it was sent. Sent as
28
+ * `PATCH /messages/:id`. `contactId` is required: without it the route answers 500, and an
29
+ * unknown or blocked contact answers 400 `The given data was invalid.` (the error comes in
30
+ * `contactId`). So is `text`: `text: ''` alone answers 400 (`At least one field (text or file)
31
+ * must be provided`), and a `text` with only spaces answers 400 `Text cannot be empty`. Only the
32
+ * text is edited: `file` is not sent to the connection. A message received, or sent more than
33
+ * 15 minutes ago, answers 403 `Cannot edit message`; an unknown message answers 404
34
+ * `Message not found`. The message passes to the user who edited it.
35
+ *
36
+ * The message is answered once the edit is sent to the connection, before the connection
37
+ * confirms it: read it again until `text` changes (`data.edited` is set to `true` when the
38
+ * connection reports the edit). On WhatsApp Business and Telegram connections the route answers
39
+ * the result of the edit job, not the message: read it again with `getById`.
31
40
  */
32
41
  override updateById(
33
42
  id: string,
@@ -62,6 +62,11 @@ export type MessageData = {
62
62
  subject?: unknown
63
63
  resourceId?: string
64
64
  hasReaction?: boolean
65
+ /**
66
+ * `true` once the connection reports that the text was edited (see `MessagesApi.updateById`; on
67
+ * WhatsApp, an edit made by the contact included).
68
+ */
69
+ edited?: boolean
65
70
  whatsappMessageId?: string
66
71
  startBlockedAt?: string
67
72
  endBlockedAt?: string
@@ -13,6 +13,7 @@ import type {
13
13
  CampaignContactsPage,
14
14
  CampaignMessage,
15
15
  CampaignStats,
16
+ CampaignTemplateParameter,
16
17
  CampaignTimelineEvent,
17
18
  } from './campaigns/types'
18
19
  import { CardsApi } from './cards/CardsApi'
@@ -36,7 +37,7 @@ import { QuestionsApi } from './questions/QuestionsApi'
36
37
  import { QuickRepliesApi } from './quickReplies/QuickRepliesApi'
37
38
  import { RolesApi } from './roles/RolesApi'
38
39
  import { ScheduleApi } from './schedule/ScheduleApi'
39
- import type { ScheduleRecord } from './schedule/types'
40
+ import type { Schedule, ScheduleRecord } from './schedule/types'
40
41
  import type {
41
42
  CreateServicePayload,
42
43
  Service,
@@ -48,16 +49,26 @@ import { TicketTopicsApi } from './ticketTopics/TicketTopicsApi'
48
49
  import { TicketsApi } from './tickets/TicketsApi'
49
50
  import type { Ticket, TicketTransfer, TicketsCount, TicketsCounts } from './tickets/types'
50
51
  import { TimetableApi } from './timetable/TimetableApi'
52
+ import type { OutOfOfficeHoursErrorExtra, TimetableWorkSlot } from './timetable/types'
51
53
  import { UsersApi } from './users/UsersApi'
52
54
 
53
55
  import { AcceptanceTermsApi } from './acceptanceTerms/AcceptanceTermsApi'
54
56
  import { CategoriesApi } from './categories/CategoriesApi'
55
57
  import { ContactBlockListsApi } from './contactBlockLists/ContactBlockListsApi'
56
58
  import { ContactBlockListsControlsApi } from './contactBlockListsControls/ContactBlockListsControlsApi'
57
- import type { SummarizeTicketResult } from './contacts/types'
59
+ import type {
60
+ Contact,
61
+ CustomFieldValue,
62
+ FormattedCustomField,
63
+ SummarizeTicketResult,
64
+ TransferTicketKanbanCardResult,
65
+ TransferTicketPayload,
66
+ } from './contacts/types'
67
+ import { AuthHistoryApi } from './authHistory/AuthHistoryApi'
68
+ import type { AuthHistoryEvent } from './authHistory/types'
58
69
  import { CustomFieldsApi } from './customFields/CustomFieldsApi'
59
70
  import { DepartmentsApi } from './departments/DepartmentsApi'
60
- import type { Department } from './departments/types'
71
+ import type { ArchiveDepartmentErrorExtra, Department } from './departments/types'
61
72
  import type { Distribution } from './distribution/types'
62
73
  import type { ContactBlockList } from './contactBlockLists/types'
63
74
  import { DistributionApi } from './distribution/DistributionApi'
@@ -86,8 +97,11 @@ import type { Sticker } from './stickers/types'
86
97
  import { OrganizationsApi } from './organizations/OrganizationsApi'
87
98
  import { PeopleApi } from './people/PeopleApi'
88
99
  import { ServicesApi } from './services/ServicesApi'
89
- import type { UpdateUserPayload, User } from './users/types'
100
+ import type { BulkUpdateUsersLinksResult, UpdateUserPayload, User } from './users/types'
90
101
  import { WebhooksApi } from './webhooks/WebhooksApi'
102
+ import type { KanbanColumn } from './kanbanColumns/types'
103
+ import type { Message } from './messages/types'
104
+ import type { Webhook, WebhookMessageFilters, WebhookType } from './webhooks/types'
91
105
 
92
106
  declare const client: ApiClient
93
107
 
@@ -306,7 +320,7 @@ expectError(pipelines.deleteStatus('st-1', { action: 'move-to-another' }))
306
320
  expectError(pipelines.createAutomation({ pipelineId: 'p-1', type: 'email' }))
307
321
  expectError(pipelines.deleteStage('s-1', { typeAction: 'archive' }))
308
322
 
309
- // ── cards: getCards is not scoped by the backend and only answers the attributes asked ─
323
+ // ── cards: getCards takes where.accountId and only answers the attributes asked ─
310
324
 
311
325
  const cards = new CardsApi(client)
312
326
  cards.getCards({
@@ -769,3 +783,229 @@ expectAssignable<Awaited<ReturnType<ServicesApi['accessManagement']>>>({
769
783
  })
770
784
  expectNotAssignable<Awaited<ReturnType<ServicesApi['accessManagement']>>>({ message: 'denied' })
771
785
  expectError(new ServicesApi(client).accessManagement('s-1', { userIds: ['u-1'] }))
786
+
787
+ // ── QARC-1015 (4.15.0): custom fields of the contact, file copy, logout event ─────
788
+
789
+ expectAssignable<Parameters<ContactsApi['create']>[0]>({
790
+ serviceId: 's-1',
791
+ customFields: [
792
+ { id: 'f-1', value: 'text' },
793
+ { id: 'f-2', value: { value: '10,50', identifier: 'BRL', min: '', max: '' } },
794
+ ],
795
+ })
796
+ expectAssignable<Parameters<ContactsApi['updateById']>[1]>({
797
+ customFields: [{ id: 'f-1', value: 'new' }],
798
+ })
799
+ expectError(
800
+ new ContactsApi(client).create({ serviceId: 's-1', customFields: [{ value: 'no id' }] }),
801
+ )
802
+ expectAssignable<Parameters<ContactsApi['getById']>[1]>({
803
+ include: [{ model: 'customFieldValues', include: ['customField'] }],
804
+ formatCustomFields: true,
805
+ })
806
+ declare const contactWithFields: Contact
807
+ expectType<CustomFieldValue[] | undefined>(contactWithFields.customFieldValues)
808
+ expectType<Record<string, FormattedCustomField> | undefined>(contactWithFields.customFields)
809
+ expectAssignable<Parameters<ContactsApi['getMany']>[0]>({
810
+ include: [{ model: 'customFieldValues', include: ['customField'] }],
811
+ formatCustomFields: true,
812
+ })
813
+ expectType<Promise<string>>(new FilesApi(client).duplicateFile({ fileId: 'file-1' }))
814
+ expectError(new FilesApi(client).duplicateFile({ urlToUpload: 'https://storage' }))
815
+ expectType<Promise<void>>(new AuthHistoryApi(client).registerLogout())
816
+ expectAssignable<AuthHistoryEvent>('2fa_disabled')
817
+
818
+ // ── QARC-948 (4.16.0): the open subtasks ─────────────────────────────
819
+
820
+ // campaigns: one page of one campaign, and the fields the create reads
821
+ expectType<Promise<Campaign | null>>(new CampaignsApi(client).getOne({ status: 'ready' }))
822
+ expectAssignable<Parameters<CampaignsApi['create']>[0]>({
823
+ title: 'Campanha',
824
+ serviceId: 's-1',
825
+ text: 'Olá',
826
+ messages: [{ text: 'Olá', base64Url: 'data:image/png;base64,AAAA' }],
827
+ fileId: 'f-1',
828
+ nameField: 'Nome',
829
+ numberField: 'Número',
830
+ usernameField: 'Usuário',
831
+ hsmFileId: 'f-2',
832
+ })
833
+
834
+ // campaigns: the update reads its own fields and updates the messages by id
835
+ expectAssignable<Parameters<CampaignsApi['updateById']>[1]>({
836
+ title: 'Campanha',
837
+ messages: [{ id: 'm-1', text: 'Olá' }, { text: 'Nova' }],
838
+ })
839
+ expectAssignable<Parameters<CampaignsApi['updateById']>[1]>({
840
+ config: { minInterval: '1', maxInterval: '5' },
841
+ messages: [
842
+ { id: 'm-1', text: 'Olá', file: { id: 'f-1' } },
843
+ {
844
+ id: 'm-2',
845
+ base64Url: 'data:image/png;base64,AAAA',
846
+ fileName: 'b.png',
847
+ },
848
+ ],
849
+ })
850
+ expectError(new CampaignsApi(client).updateById('cmp-1', { text: 'Olá' }))
851
+ expectError(new CampaignsApi(client).updateById('cmp-1', { usernameField: 'Usuário' }))
852
+ expectError(new CampaignsApi(client).updateById('cmp-1', { hsmFileId: 'f-2' }))
853
+ expectAssignable<CampaignTemplateParameter>({
854
+ type: 'header',
855
+ parameters: [{ type: 'image', image: { link: 'https://example.invalid/a.png' } }],
856
+ })
857
+
858
+ // messages: the edit flag of the connection
859
+ declare const editedMessage: Message
860
+ expectType<boolean | undefined>(editedMessage.data.edited)
861
+
862
+ // schedule: the template parameters
863
+ expectAssignable<Parameters<ScheduleApi['create']>[0]>({
864
+ scheduledAt: '2026-10-20T12:00:00.000Z',
865
+ contactId: 'c-1',
866
+ departmentId: 'd-1',
867
+ notes: '',
868
+ hsmId: 'h-1',
869
+ extraOptions: { parameters: [{ type: 'body', parameters: [{ type: 'text', text: 'Ana' }] }] },
870
+ })
871
+ expectError(
872
+ new ScheduleApi(client).create({
873
+ scheduledAt: '2026-10-20T12:00:00.000Z',
874
+ contactId: 'c-1',
875
+ departmentId: 'd-1',
876
+ notes: '',
877
+ extraOptions: { parameters: 'Ana' },
878
+ }),
879
+ )
880
+
881
+ declare const storedSchedule: Schedule
882
+ expectType<CampaignTemplateParameter[] | undefined>(storedSchedule.extraOptions?.parameters)
883
+
884
+ // kanban: deleting a column moving its cards
885
+ expectType<Promise<KanbanColumn>>(
886
+ new KanbanColumnsApi(client).deleteMovingCards('col-1', { cardsDestinationColumnId: 'col-2' }),
887
+ )
888
+ expectError(new KanbanColumnsApi(client).deleteMovingCards('col-1', {}))
889
+ expectType<Promise<KanbanColumn>>(new KanbanColumnsApi(client).deleteById('col-1'))
890
+
891
+ // contacts: the result of the transfer and the close of the ticket
892
+ const ticketContacts = new ContactsApi(client)
893
+ expectType<Promise<TransferTicketKanbanCardResult>>(
894
+ ticketContacts.transferTicket('c-1', { departmentId: 'd-1', includeKanbanCardId: true }),
895
+ )
896
+ expectType<Promise<true>>(ticketContacts.transferTicket('c-1', { departmentId: 'd-1' }))
897
+ declare const transferPayload: TransferTicketPayload
898
+ expectType<Promise<true | TransferTicketKanbanCardResult>>(
899
+ ticketContacts.transferTicket('c-1', transferPayload),
900
+ )
901
+ declare const transferResult: TransferTicketKanbanCardResult
902
+ expectType<string | null>(transferResult.kanbanCardId)
903
+ expectType<Promise<true>>(ticketContacts.closeTicket('c-1'))
904
+ expectType<Promise<true>>(
905
+ ticketContacts.closeTicket('c-1', { ticketTopicIds: ['tt-1'], aiSummaryRating: '5' }),
906
+ )
907
+ expectError(ticketContacts.closeTicket('c-1', { aiSummaryRating: '6' }))
908
+
909
+ // webhooks: the type, the connections and the message filters
910
+ declare const webhook: Webhook
911
+ expectType<WebhookType>(webhook.type)
912
+ expectType<WebhookMessageFilters | null | undefined>(webhook.messageFilters)
913
+ expectType<number | null | undefined>(webhook.avgLatencyMs)
914
+ expectAssignable<Parameters<WebhooksApi['create']>[0]>({
915
+ name: 'Webhook',
916
+ url: 'https://example.invalid/hook',
917
+ type: 'by_service',
918
+ events: ['message.created'],
919
+ services: ['s-1'],
920
+ messageFilters: { bot: false, system: false },
921
+ })
922
+ expectAssignable<Parameters<WebhooksApi['create']>[0]>({
923
+ name: 'Webhook',
924
+ url: 'https://example.invalid/hook',
925
+ type: 'general',
926
+ events: ['message.created'],
927
+ messageFilters: null,
928
+ })
929
+ expectError(
930
+ new WebhooksApi(client).create({
931
+ name: 'Webhook',
932
+ url: 'https://example.invalid/hook',
933
+ type: 'other',
934
+ events: [],
935
+ }),
936
+ )
937
+ expectError(
938
+ new WebhooksApi(client).create({
939
+ name: 'Webhook',
940
+ url: 'https://example.invalid/hook',
941
+ type: 'general',
942
+ events: [],
943
+ messageFilters: { comments: false },
944
+ }),
945
+ )
946
+ expectError(new WebhooksApi(client).updateById('w-1', { active: false }))
947
+ const hookUrl = 'https://example.invalid/hook'
948
+ const hooks = new WebhooksApi(client)
949
+ expectError(hooks.updateById('w-1', { url: hookUrl, type: 'general', events: [] }))
950
+ expectError(hooks.updateById('w-1', { name: 'Webhook', type: 'general', events: [] }))
951
+ expectError(hooks.updateById('w-1', { name: 'Webhook', url: hookUrl, events: [] }))
952
+ expectError(hooks.updateById('w-1', { name: 'Webhook', url: hookUrl, type: 'general' }))
953
+ expectError(hooks.updateById('w-1', { name: 'Webhook', url: hookUrl, type: 'other', events: [] }))
954
+ expectAssignable<WebhookType>('whatsapp_low_level')
955
+ expectAssignable<WebhookMessageFilters>({ received: true, sent: false, bot: true, system: false })
956
+ expectAssignable<Parameters<WebhooksApi['updateById']>[1]>({
957
+ name: 'Webhook',
958
+ url: 'https://example.invalid/hook',
959
+ type: 'by_service',
960
+ events: ['message.created'],
961
+ services: ['s-1'],
962
+ messageFilters: { received: false },
963
+ })
964
+ expectAssignable<Parameters<WebhooksApi['updateById']>[1]>({
965
+ name: 'Webhook',
966
+ url: 'https://example.invalid/hook',
967
+ type: 'general',
968
+ events: ['message.created'],
969
+ messageFilters: null,
970
+ })
971
+
972
+ // users, bots and answers
973
+ expectType<Promise<BulkUpdateUsersLinksResult>>(
974
+ new UsersApi(client).bulkUpdateLinks({ ids: ['u-1'], timetableId: null }),
975
+ )
976
+ expectError(new UsersApi(client).bulkUpdateLinks({ timetableId: 't-1' }))
977
+ expectType<Promise<ArrayBuffer>>(new BotsApi(client).exportErrors('b-1'))
978
+ expectType<Promise<Response>>(new AnswersApi(client).exportTxtRaw())
979
+ expectType<Promise<Response>>(new AnswersApi(client).exportCsvRaw())
980
+
981
+ // QARC-948, second review: the remaining fields of the new types
982
+ expectAssignable<CampaignTemplateParameter>({
983
+ type: 'header',
984
+ parameters: [
985
+ { type: 'video', video: { link: 'https://example.invalid/a.mp4' } },
986
+ { type: 'document', document: { link: 'https://example.invalid/a.pdf' } },
987
+ ],
988
+ })
989
+ expectAssignable<Parameters<ContactsApi['closeTicket']>[1]>({ comments: 'Resolvido' })
990
+ expectAssignable<TransferTicketPayload>({ departmentId: 'd-1', includeKanbanCardId: true })
991
+ expectError(
992
+ new ScheduleApi(client).updateById('sch-1', {
993
+ scheduledAt: '2026-10-20T12:00:00.000Z',
994
+ contactId: 'c-1',
995
+ departmentId: 'd-1',
996
+ extraOptions: { parameters: 'Ana' },
997
+ }),
998
+ )
999
+ expectAssignable<Parameters<UsersApi['bulkUpdateLinks']>[0]>({
1000
+ ids: ['u-1'],
1001
+ departmentIds: ['d-1'],
1002
+ roleIds: ['r-1'],
1003
+ })
1004
+ declare const linksResult: BulkUpdateUsersLinksResult
1005
+ expectType<number>(linksResult.failed)
1006
+ expectType<string[]>(linksResult.missingIds)
1007
+ declare const outOfOffice: OutOfOfficeHoursErrorExtra
1008
+ expectType<TimetableWorkSlot[]>(outOfOffice.workPlan)
1009
+ declare const archiveRefusal: ArchiveDepartmentErrorExtra
1010
+ expectAssignable<ArchiveDepartmentErrorExtra['reasons'][number]>('ONLY_ONE')
1011
+ expectType<number | undefined>(archiveRefusal.openTicketsCount)