@unboundcx/sdk 2.8.6 → 2.8.8

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.
@@ -139,7 +139,11 @@ export class EngagementMetricsService {
139
139
  * @param {string[]} [options.userIds] - Array of user IDs to filter by
140
140
  * @returns {Promise<Object>} All metrics
141
141
  */
142
- async getDashboardMetrics({ queueIds = [], statuses = [], userIds = [] } = {}) {
142
+ async getDashboardMetrics({
143
+ queueIds = [],
144
+ statuses = [],
145
+ userIds = [],
146
+ } = {}) {
143
147
  return this.getMetrics({
144
148
  queueIds,
145
149
  statuses,
@@ -150,4 +154,4 @@ export class EngagementMetricsService {
150
154
  includeAgentPerformance: true,
151
155
  });
152
156
  }
153
- }
157
+ }
@@ -122,4 +122,49 @@ export class ExternalOAuthService {
122
122
  const result = await this.sdk._fetch('/externalOAuth', 'GET');
123
123
  return result;
124
124
  }
125
+
126
+ async listUnified() {
127
+ const result = await this.sdk._fetch('/externalOAuth/unified', 'GET');
128
+ return result;
129
+ }
130
+
131
+ async providers() {
132
+ const result = await this.sdk._fetch('/externalOAuth/providers', 'GET');
133
+ return result;
134
+ }
135
+
136
+ async authorize({
137
+ name,
138
+ provider,
139
+ clientId,
140
+ clientSecret,
141
+ scopes,
142
+ authorizationUrl,
143
+ tokenUrl,
144
+ }) {
145
+ this.sdk.validateParams(
146
+ { name, provider },
147
+ {
148
+ name: { type: 'string', required: true },
149
+ provider: { type: 'string', required: true },
150
+ clientId: { type: 'string', required: false },
151
+ clientSecret: { type: 'string', required: false },
152
+ scopes: { type: 'array', required: false },
153
+ authorizationUrl: { type: 'string', required: false },
154
+ tokenUrl: { type: 'string', required: false },
155
+ },
156
+ );
157
+
158
+ const body = { name, provider };
159
+ if (clientId) body.clientId = clientId;
160
+ if (clientSecret) body.clientSecret = clientSecret;
161
+ if (scopes) body.scopes = scopes;
162
+ if (authorizationUrl) body.authorizationUrl = authorizationUrl;
163
+ if (tokenUrl) body.tokenUrl = tokenUrl;
164
+
165
+ const result = await this.sdk._fetch('/externalOAuth/authorize', 'POST', {
166
+ body,
167
+ });
168
+ return result;
169
+ }
125
170
  }
@@ -0,0 +1,249 @@
1
+ export class FaxService {
2
+ constructor(sdk) {
3
+ this.sdk = sdk;
4
+ }
5
+
6
+ /**
7
+ * Create an inbound fax document record with status 'receiving'.
8
+ * Called by the media manager when an inbound fax is first detected.
9
+ * After the fax is fully received, call sdk.fax.status() to update
10
+ * with the final storage IDs and completion metadata.
11
+ *
12
+ * @param {Object} options - Parameters
13
+ * @param {string} options.faxMailboxId - ID of the fax mailbox receiving the fax (required)
14
+ * @param {string} [options.sipCallId] - SIP call correlation ID
15
+ * @param {string} [options.name] - Display name for the fax document
16
+ * @param {string} [options.faxHeader] - TSI header string from the sender
17
+ * @param {string} [options.resolution] - Fax resolution (e.g. 'fine', 'standard')
18
+ * @param {string} [options.toNumber] - Destination number in E.164 format
19
+ * @param {string} [options.fromNumber] - Sender number in E.164 format
20
+ * @returns {Promise<Object>} Created fax document
21
+ * @returns {string} result.id - The fax document ID (use in subsequent status calls)
22
+ * @returns {string} result.status - 'receiving'
23
+ *
24
+ * @example
25
+ * const { id } = await sdk.fax.receive({
26
+ * faxMailboxId: '157abc123...',
27
+ * sipCallId: 'sip-call-uuid',
28
+ * name: 'Fax from +15551234567',
29
+ * toNumber: '+15559876543',
30
+ * fromNumber: '+15551234567',
31
+ * });
32
+ * // Save id for use with sdk.fax.status() after reception completes
33
+ */
34
+ async receive({
35
+ faxMailboxId,
36
+ sipCallId,
37
+ name,
38
+ faxHeader,
39
+ resolution,
40
+ toNumber,
41
+ fromNumber,
42
+ cId,
43
+ }) {
44
+ this.sdk.validateParams(
45
+ { faxMailboxId },
46
+ {
47
+ faxMailboxId: { type: 'string', required: true },
48
+ },
49
+ );
50
+
51
+ const params = {
52
+ body: {
53
+ faxMailboxId,
54
+ sipCallId,
55
+ name,
56
+ faxHeader,
57
+ resolution,
58
+ toNumber,
59
+ fromNumber,
60
+ cId,
61
+ },
62
+ };
63
+
64
+ return await this.sdk._fetch('/fax/receive', 'POST', params);
65
+ }
66
+
67
+ /**
68
+ * Send an outbound fax.
69
+ * Creates a fax document record, retrieves the document from storage,
70
+ * and publishes to the media manager via NATS to initiate the fax call.
71
+ *
72
+ * @param {Object} options - Parameters
73
+ * @param {string} options.faxMailboxId - ID of the fax mailbox to send from (required)
74
+ * @param {string} options.toNumber - Destination number in E.164 format (required)
75
+ * @param {string} options.fromNumber - Caller ID number in E.164 format (required)
76
+ * @param {string} [options.storageId] - Storage ID of a PDF or TIFF file to fax. The server will
77
+ * automatically convert it to the missing format so both PDF and TIFF are stored.
78
+ * Required if pdfStorageId and tiffStorageId are not provided.
79
+ * @param {string} [options.pdfStorageId] - Storage ID of the PDF version. Required if storageId is not provided.
80
+ * @param {string} [options.tiffStorageId] - Storage ID of the TIFF version. Required if storageId is not provided.
81
+ * @param {string} [options.faxHeader] - TSI header text (defaults to mailbox faxHeader)
82
+ * @param {string} [options.resolution] - Fax resolution (defaults to mailbox resolution)
83
+ * @param {boolean} [options.ecm] - Enable Error Correction Mode (default: true)
84
+ * @param {number} [options.timeout] - Dial timeout in seconds (defaults to mailbox dialTimeout)
85
+ * @returns {Promise<Object>} Send result
86
+ * @returns {string} result.id - The fax document ID
87
+ * @returns {string} result.status - 'sending' on success, 'failed' on NATS error
88
+ * @returns {string} [result.requestId] - NATS request ID (on success)
89
+ * @returns {string} [result.error] - Error message (on failure)
90
+ *
91
+ * @example
92
+ * // Send using a single storageId (PDF or TIFF — server auto-converts the missing format)
93
+ * const result = await sdk.fax.send({
94
+ * faxMailboxId: '157abc123...',
95
+ * toNumber: '+15551234567',
96
+ * fromNumber: '+15559876543',
97
+ * storageId: '017xyz788...',
98
+ * });
99
+ * console.log(result.id); // '158def456...'
100
+ * console.log(result.status); // 'sending'
101
+ *
102
+ * @example
103
+ * // Send using explicit PDF and TIFF storage IDs
104
+ * const result = await sdk.fax.send({
105
+ * faxMailboxId: '157abc123...',
106
+ * toNumber: '+15551234567',
107
+ * fromNumber: '+15559876543',
108
+ * pdfStorageId: '017xyz789...',
109
+ * tiffStorageId: '017xyz788...',
110
+ * faxHeader: 'My Company',
111
+ * resolution: 'fine',
112
+ * ecm: true,
113
+ * timeout: 90,
114
+ * });
115
+ */
116
+ async send({
117
+ faxMailboxId,
118
+ toNumber,
119
+ fromNumber,
120
+ storageId,
121
+ pdfStorageId,
122
+ tiffStorageId,
123
+ faxHeader,
124
+ resolution,
125
+ ecm,
126
+ timeout,
127
+ }) {
128
+ this.sdk.validateParams(
129
+ { faxMailboxId, toNumber, fromNumber },
130
+ {
131
+ faxMailboxId: { type: 'string', required: true },
132
+ toNumber: { type: 'string', required: true },
133
+ fromNumber: { type: 'string', required: true },
134
+ storageId: { type: 'string', required: false },
135
+ pdfStorageId: { type: 'string', required: false },
136
+ tiffStorageId: { type: 'string', required: false },
137
+ },
138
+ );
139
+
140
+ if (!storageId && (!pdfStorageId || !tiffStorageId)) {
141
+ throw new Error(
142
+ 'Either storageId or both pdfStorageId and tiffStorageId are required.',
143
+ );
144
+ }
145
+
146
+ const params = {
147
+ body: {
148
+ faxMailboxId,
149
+ toNumber,
150
+ fromNumber,
151
+ storageId,
152
+ pdfStorageId,
153
+ tiffStorageId,
154
+ faxHeader,
155
+ resolution,
156
+ ecm,
157
+ timeout,
158
+ },
159
+ };
160
+
161
+ return await this.sdk._fetch('/fax/send', 'POST', params);
162
+ }
163
+
164
+ /**
165
+ * Update the status and metadata of a fax document.
166
+ * Called by the media manager to report progress and completion
167
+ * of both inbound and outbound faxes.
168
+ *
169
+ * @param {Object} options - Parameters
170
+ * @param {string} options.faxDocumentId - ID of the fax document to update (required)
171
+ * @param {string} options.status - New status (required): 'receiving', 'sending', 'sent', 'completed', 'failed'
172
+ * @param {string} [options.sipCallId] - SIP call correlation ID
173
+ * @param {number} [options.pages] - Number of pages transmitted/received
174
+ * @param {number} [options.duration] - Call duration in seconds
175
+ * @param {number} [options.transferRate] - Baud rate (e.g. 14400)
176
+ * @param {number} [options.ecmUsed] - Whether ECM was used (1 or 0)
177
+ * @param {number} [options.isError] - Whether an error occurred (1 or 0)
178
+ * @param {string} [options.errorMessage] - Error description
179
+ * @param {number} [options.sendAttempts] - Number of send attempts made
180
+ * @param {string} [options.pdfStorageId] - Storage ID of the PDF version
181
+ * @param {string} [options.tiffStorageId] - Storage ID of the TIFF version
182
+ * @returns {Promise<Object>} Updated fields
183
+ * @returns {string} result.id - The fax document ID
184
+ *
185
+ * @example
186
+ * // Complete an inbound fax with storage files and metadata
187
+ * await sdk.fax.status({
188
+ * faxDocumentId: '158abc123...',
189
+ * status: 'completed',
190
+ * pdfStorageId: '017pdf456...',
191
+ * tiffStorageId: '017tiff789...',
192
+ * pages: 3,
193
+ * duration: 45,
194
+ * transferRate: 14400,
195
+ * ecmUsed: 1,
196
+ * });
197
+ *
198
+ * @example
199
+ * // Report a fax failure
200
+ * await sdk.fax.status({
201
+ * faxDocumentId: '158abc123...',
202
+ * status: 'failed',
203
+ * isError: 1,
204
+ * errorMessage: 'Remote side disconnected',
205
+ * sendAttempts: 2,
206
+ * });
207
+ */
208
+ async status({
209
+ faxDocumentId,
210
+ status,
211
+ sipCallId,
212
+ pages,
213
+ duration,
214
+ transferRate,
215
+ ecmUsed,
216
+ isError,
217
+ errorMessage,
218
+ sendAttempts,
219
+ pdfStorageId,
220
+ tiffStorageId,
221
+ }) {
222
+ this.sdk.validateParams(
223
+ { faxDocumentId, status },
224
+ {
225
+ faxDocumentId: { type: 'string', required: true },
226
+ status: { type: 'string', required: true },
227
+ },
228
+ );
229
+
230
+ const params = {
231
+ body: {
232
+ faxDocumentId,
233
+ status,
234
+ sipCallId,
235
+ pages,
236
+ duration,
237
+ transferRate,
238
+ ecmUsed,
239
+ isError,
240
+ errorMessage,
241
+ sendAttempts,
242
+ pdfStorageId,
243
+ tiffStorageId,
244
+ },
245
+ };
246
+
247
+ return await this.sdk._fetch('/fax/status', 'POST', params);
248
+ }
249
+ }
@@ -0,0 +1,229 @@
1
+ export class KnowledgeBaseService {
2
+ constructor(sdk) {
3
+ this.sdk = sdk;
4
+ }
5
+
6
+ /**
7
+ * Hybrid search across knowledge base content
8
+ * Combines keyword (FULLTEXT) and semantic (vector) search with optional LLM reranking
9
+ *
10
+ * @param {Object} params
11
+ * @param {string} params.query - Search query text
12
+ * @param {string} [params.knowledgeBaseId] - Specific KB to search (omit to search all)
13
+ * @param {number} [params.limit] - Max results to return
14
+ * @param {Object} [params.filters] - Optional filters
15
+ * @param {string} [params.filters.categoryId] - Filter by category
16
+ * @param {string} [params.filters.sourceType] - Filter by source type (article, document, source)
17
+ * @param {Array} [params.filters.tags] - Filter by tags
18
+ * @param {boolean} [params.rerank] - Whether to apply LLM reranking (default: true)
19
+ * @returns {Promise<Object>} Search results with source attribution
20
+ */
21
+ async search({ query, knowledgeBaseId, limit, filters, rerank }) {
22
+ this.sdk.validateParams(
23
+ { query },
24
+ {
25
+ query: { type: 'string', required: true },
26
+ },
27
+ );
28
+
29
+ const params = {
30
+ body: { query, knowledgeBaseId, limit, filters, rerank },
31
+ };
32
+
33
+ const result = await this.sdk._fetch(
34
+ '/knowledgeBase/search',
35
+ 'POST',
36
+ params,
37
+ );
38
+ return result;
39
+ }
40
+
41
+ /**
42
+ * Discover pages available at a URL
43
+ * Checks for sitemaps (auto-discovery + direct sitemap URLs) and returns
44
+ * a list of pages the user can select from before ingesting.
45
+ *
46
+ * @param {Object} params
47
+ * @param {string} params.url - URL to discover (base URL or sitemap URL)
48
+ * @returns {Promise<Object>} { url, type, sitemapUrl, pages[], pageCount }
49
+ */
50
+ async discoverUrl({ url }) {
51
+ this.sdk.validateParams(
52
+ { url },
53
+ {
54
+ url: { type: 'string', required: true },
55
+ },
56
+ );
57
+
58
+ const params = {
59
+ body: { url },
60
+ };
61
+
62
+ const result = await this.sdk._fetch(
63
+ '/knowledgeBase/discover-url',
64
+ 'POST',
65
+ params,
66
+ );
67
+ return result;
68
+ }
69
+
70
+ /**
71
+ * Ingest a URL into a knowledge base
72
+ * Creates a kbSources record and triggers async crawl + processing
73
+ *
74
+ * @param {Object} params
75
+ * @param {string} params.knowledgeBaseId - Target knowledge base
76
+ * @param {string} params.url - URL to crawl and ingest
77
+ * @param {string} [params.categoryId] - Category to assign
78
+ * @param {string} [params.title] - Display title (auto-detected from page if omitted)
79
+ * @param {string} [params.refreshInterval] - Recrawl interval: manual, daily, weekly, monthly
80
+ * @returns {Promise<Object>} Created kbSources record
81
+ */
82
+ async ingestUrl({ knowledgeBaseId, url, categoryId, title, refreshInterval }) {
83
+ this.sdk.validateParams(
84
+ { knowledgeBaseId, url },
85
+ {
86
+ knowledgeBaseId: { type: 'string', required: true },
87
+ url: { type: 'string', required: true },
88
+ },
89
+ );
90
+
91
+ const params = {
92
+ body: { knowledgeBaseId, url, categoryId, title, refreshInterval },
93
+ };
94
+
95
+ const result = await this.sdk._fetch(
96
+ '/knowledgeBase/ingest/url',
97
+ 'POST',
98
+ params,
99
+ );
100
+ return result;
101
+ }
102
+
103
+ /**
104
+ * Trigger (re)processing of a knowledge base source
105
+ * Sets processingStatus to pending and publishes NATS event
106
+ *
107
+ * @param {Object} params
108
+ * @param {string} params.sourceType - Type: article, document, or source
109
+ * @param {string} params.id - Source record ID
110
+ * @returns {Promise<Object>} Updated source record
111
+ */
112
+ async processSource({ sourceType, id }) {
113
+ this.sdk.validateParams(
114
+ { sourceType, id },
115
+ {
116
+ sourceType: { type: 'string', required: true },
117
+ id: { type: 'string', required: true },
118
+ },
119
+ );
120
+
121
+ const result = await this.sdk._fetch(
122
+ `/knowledgeBase/process/${sourceType}/${id}`,
123
+ 'POST',
124
+ );
125
+ return result;
126
+ }
127
+
128
+ /**
129
+ * Check the processing status of a source
130
+ *
131
+ * @param {Object} params
132
+ * @param {string} params.id - Source record ID
133
+ * @returns {Promise<Object>} Processing status { id, processingStatus, processingError }
134
+ */
135
+ async checkProcessingStatus({ id }) {
136
+ this.sdk.validateParams(
137
+ { id },
138
+ {
139
+ id: { type: 'string', required: true },
140
+ },
141
+ );
142
+
143
+ const result = await this.sdk._fetch(
144
+ `/knowledgeBase/process/${id}/status`,
145
+ 'GET',
146
+ );
147
+ return result;
148
+ }
149
+
150
+ /**
151
+ * Publish a draft article
152
+ * Changes status to published, creates version snapshot, triggers processing
153
+ *
154
+ * @param {Object} params
155
+ * @param {string} params.id - Article ID
156
+ * @returns {Promise<Object>} Updated article record
157
+ */
158
+ async publishArticle({ id }) {
159
+ this.sdk.validateParams(
160
+ { id },
161
+ {
162
+ id: { type: 'string', required: true },
163
+ },
164
+ );
165
+
166
+ const result = await this.sdk._fetch(
167
+ `/knowledgeBase/articles/${id}/publish`,
168
+ 'POST',
169
+ );
170
+ return result;
171
+ }
172
+
173
+ /**
174
+ * Get analytics for a knowledge base
175
+ *
176
+ * @param {Object} params
177
+ * @param {string} params.knowledgeBaseId - Knowledge base ID
178
+ * @param {Object} [params.filters] - Optional date range, source type filters
179
+ * @returns {Promise<Object>} Analytics data
180
+ */
181
+ async getAnalytics({ knowledgeBaseId, ...filters }) {
182
+ this.sdk.validateParams(
183
+ { knowledgeBaseId },
184
+ {
185
+ knowledgeBaseId: { type: 'string', required: true },
186
+ },
187
+ );
188
+
189
+ const params = {
190
+ query: filters,
191
+ };
192
+
193
+ const result = await this.sdk._fetch(
194
+ `/knowledgeBase/${knowledgeBaseId}/analytics`,
195
+ 'GET',
196
+ params,
197
+ );
198
+ return result;
199
+ }
200
+
201
+ /**
202
+ * Get content gap analysis for a knowledge base
203
+ * Identifies failed/low-result searches that indicate missing content
204
+ *
205
+ * @param {Object} params
206
+ * @param {string} params.knowledgeBaseId - Knowledge base ID
207
+ * @param {Object} [params.filters] - Optional date range filters
208
+ * @returns {Promise<Object>} Gap analysis data
209
+ */
210
+ async getGaps({ knowledgeBaseId, ...filters }) {
211
+ this.sdk.validateParams(
212
+ { knowledgeBaseId },
213
+ {
214
+ knowledgeBaseId: { type: 'string', required: true },
215
+ },
216
+ );
217
+
218
+ const params = {
219
+ query: filters,
220
+ };
221
+
222
+ const result = await this.sdk._fetch(
223
+ `/knowledgeBase/${knowledgeBaseId}/analytics/gaps`,
224
+ 'GET',
225
+ params,
226
+ );
227
+ return result;
228
+ }
229
+ }
@@ -10,18 +10,26 @@ export class EmailTemplatesService {
10
10
  * @param {string} params.subject - Template subject (required)
11
11
  * @param {string} [params.html] - HTML template body
12
12
  * @param {string} [params.text] - Plain text template body
13
- * @returns {Promise<Object>} Created template details with auto-extracted variables
13
+ * @param {Array<Object>} [params.variables] - Variable metadata definitions
14
+ * @param {string} params.variables[].key - Variable key (unique, alphanumeric + underscores)
15
+ * @param {string} params.variables[].label - Human-readable display name
16
+ * @param {string} params.variables[].type - One of: text, textarea, url, image, richtext
17
+ * @param {string} [params.variables[].defaultValue] - Default value
18
+ * @param {boolean} [params.variables[].required] - Whether variable is required
19
+ * @returns {Promise<Object>} Created template details with merged variables
14
20
  * @example
15
- * // Create template with variables in the content
16
21
  * const template = await sdk.messaging.email.templates.create({
17
22
  * name: 'Welcome Email',
18
23
  * subject: 'Welcome {{firstName}}!',
19
- * html: '<h1>Hello {{firstName}} {{lastName}}</h1>',
20
- * text: 'Hello {{firstName}} {{lastName}}'
24
+ * html: '<h1>Hello {{firstName}}</h1><p>{{body}}</p>',
25
+ * text: 'Hello {{firstName}}',
26
+ * variables: [
27
+ * { key: 'firstName', label: 'First Name', type: 'text', required: true },
28
+ * { key: 'body', label: 'Email Body', type: 'richtext' },
29
+ * ],
21
30
  * });
22
- * // Returns: { id, name, variables: ['firstName', 'lastName'] }
23
31
  */
24
- async create({ name, subject, html, text }) {
32
+ async create({ name, subject, html, text, variables }) {
25
33
  this.sdk.validateParams(
26
34
  { name, subject },
27
35
  {
@@ -29,12 +37,14 @@ export class EmailTemplatesService {
29
37
  subject: { type: 'string', required: true },
30
38
  html: { type: 'string', required: false },
31
39
  text: { type: 'string', required: false },
40
+ variables: { type: 'array', required: false },
32
41
  },
33
42
  );
34
43
 
35
44
  const templateData = { name, subject };
36
45
  if (html) templateData.html = html;
37
46
  if (text) templateData.text = text;
47
+ if (variables) templateData.variables = variables;
38
48
 
39
49
  const options = {
40
50
  body: templateData,
@@ -56,15 +66,23 @@ export class EmailTemplatesService {
56
66
  * @param {string} [params.subject] - Template subject
57
67
  * @param {string} [params.html] - HTML template body
58
68
  * @param {string} [params.text] - Plain text template body
59
- * @returns {Promise<Object>} Updated template details with auto-extracted variables
69
+ * @param {Array<Object>} [params.variables] - Variable metadata definitions
70
+ * @param {string} params.variables[].key - Variable key (unique, alphanumeric + underscores)
71
+ * @param {string} params.variables[].label - Human-readable display name
72
+ * @param {string} params.variables[].type - One of: text, textarea, url, image, richtext
73
+ * @param {string} [params.variables[].defaultValue] - Default value
74
+ * @param {boolean} [params.variables[].required] - Whether variable is required
75
+ * @returns {Promise<Object>} Updated template details with merged variables
60
76
  * @example
61
- * // Update template - variables are auto-extracted from content
62
77
  * const updated = await sdk.messaging.email.templates.update('tpl_123', {
63
- * subject: 'Hi {{firstName}}, welcome to {{companyName}}!'
78
+ * subject: 'Hi {{firstName}}, welcome to {{companyName}}!',
79
+ * variables: [
80
+ * { key: 'firstName', label: 'First Name', type: 'text', required: true },
81
+ * { key: 'companyName', label: 'Company Name', type: 'text' },
82
+ * ],
64
83
  * });
65
- * // Returns updated template with variables: ['firstName', 'companyName']
66
84
  */
67
- async update(id, { name, subject, html, text }) {
85
+ async update(id, { name, subject, html, text, variables }) {
68
86
  this.sdk.validateParams(
69
87
  { id },
70
88
  {
@@ -73,6 +91,7 @@ export class EmailTemplatesService {
73
91
  subject: { type: 'string', required: false },
74
92
  html: { type: 'string', required: false },
75
93
  text: { type: 'string', required: false },
94
+ variables: { type: 'array', required: false },
76
95
  },
77
96
  );
78
97
 
@@ -81,6 +100,7 @@ export class EmailTemplatesService {
81
100
  if (subject) updateData.subject = subject;
82
101
  if (html) updateData.html = html;
83
102
  if (text) updateData.text = text;
103
+ if (variables) updateData.variables = variables;
84
104
 
85
105
  const options = {
86
106
  body: updateData,