@vuevox/sdk 0.7.0 → 0.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.
package/README.md CHANGED
@@ -31,9 +31,12 @@ hello:read
31
31
  spaces:read
32
32
  agents:read
33
33
  calls:read
34
+ calls:write
34
35
  leads:read
35
36
  leads:write
36
37
  lead_custom_fields:manage
38
+ webhooks:read
39
+ webhooks:write
37
40
  ```
38
41
 
39
42
  The token request can only request scopes that were granted to that API client.
@@ -104,6 +107,9 @@ vuevox.agents.list();
104
107
  vuevox.agents.paginate();
105
108
  vuevox.calls.list();
106
109
  vuevox.calls.get("call-id");
110
+ vuevox.calls.upload({ idempotencyKey: "crm-call-123", spaceId: "space-id", agent: { externalId: "agent-1", name: "Morgan" }, lead: { externalId: "lead-1", firstName: "Ada", lastName: "Lovelace" }, audio: { file: audioBlob, filename: "call.mp3" } });
111
+ vuevox.calls.queueAnalysis("call-id");
112
+ vuevox.calls.waitForAnalysis("call-id");
107
113
  vuevox.calls.paginate();
108
114
  vuevox.leads.list();
109
115
  vuevox.leads.get("lead-id");
@@ -113,6 +119,12 @@ vuevox.leads.paginate();
113
119
  vuevox.leadCustomFields.list();
114
120
  vuevox.leadCustomFields.create({ key: "crm_stage", label: "CRM Stage", type: "select", options: ["new", "qualified"] });
115
121
  vuevox.leadCustomFields.update("crm_stage", { label: "CRM Stage" });
122
+ vuevox.webhooks.endpoints.list();
123
+ vuevox.webhooks.endpoints.create({ url: "https://example.com/vuevox/webhooks", events: ["analysis.completed", "analysis.failed"] });
124
+ vuevox.webhooks.endpoints.update("endpoint-id", { isActive: false });
125
+ vuevox.webhooks.endpoints.rotateSecret("endpoint-id");
126
+ vuevox.webhooks.endpoints.test("endpoint-id");
127
+ vuevox.webhooks.endpoints.delete("endpoint-id");
116
128
  vuevox.raw.GET("/v1/hello", { headers: { Authorization: `Bearer ${await vuevox.getAccessToken()}` } });
117
129
  ```
118
130
 
@@ -160,7 +172,7 @@ for await (const call of vuevox.calls.paginate({ limit: 50 })) {
160
172
  }
161
173
  ```
162
174
 
163
- Pagination helpers are available for `spaces`, `agents`, `calls`, and `leads`.
175
+ Pagination helpers are available for `spaces`, `agents`, `calls`, `leads`, and `webhooks.endpoints`.
164
176
 
165
177
  ## Methods
166
178
 
@@ -312,6 +324,106 @@ console.log(response.data.data.transcript);
312
324
 
313
325
  Returns: `Promise<VueVoxApiResponse<CallDetailResponse>>`.
314
326
 
327
+ ### `vuevox.calls.upload(input)`
328
+
329
+ Uploads an audio recording, upserts the agent and lead by `externalId`, creates the call in an existing platform-managed space, and optionally queues analysis.
330
+
331
+ Required scope: `calls:write`.
332
+
333
+ The API requires an idempotency key for uploads. Reusing the same `idempotencyKey` with the same request returns the original response; reusing it with different metadata or audio returns an `idempotency_key_conflict` error.
334
+
335
+ Spaces are managed in VueVox. Use `vuevox.spaces.list()` to obtain the `spaceId`.
336
+
337
+ The maximum audio upload size is configured by a VueVox superadmin and defaults to 20 MB. Your server/proxy upload limits must also allow that size.
338
+
339
+ Options: `UploadCallInput`
340
+
341
+ | Option | Type | Description |
342
+ | --- | --- | --- |
343
+ | `idempotencyKey` | `string` | Required unique key for safe retries. |
344
+ | `spaceId` | `string` | Required existing VueVox space ID. |
345
+ | `externalId` | `string \| null` | Optional call ID from your system. |
346
+ | `queueAnalysis` | `boolean` | Optional. Defaults to `true`; set `false` to upload now and queue later. |
347
+ | `agent.externalId` | `string` | Required agent ID from your system. |
348
+ | `agent.name` | `string` | Required when creating a new agent. |
349
+ | `agent.email` | `string \| null` | Optional agent email. |
350
+ | `agent.phone` | `string \| null` | Optional agent phone. |
351
+ | `lead.externalId` | `string` | Required lead/prospect ID from your system. |
352
+ | `lead.firstName` | `string` | Required when creating a new lead. |
353
+ | `lead.lastName` | `string` | Required when creating a new lead. |
354
+ | `lead.email` | `string \| null` | Optional lead email. |
355
+ | `lead.phone` | `string \| null` | Optional lead phone. |
356
+ | `lead.customFields` | `Record<string, unknown>` | Optional organization-defined lead custom fields. |
357
+ | `audio.file` | `Blob` | Required audio file blob. In Node, use `new Blob([buffer], { type: "audio/mpeg" })`. |
358
+ | `audio.filename` | `string` | Optional filename sent in multipart upload. |
359
+
360
+ ```ts
361
+ import { readFile } from "node:fs/promises";
362
+
363
+ const buffer = await readFile("./call.mp3");
364
+
365
+ const response = await vuevox.calls.upload({
366
+ idempotencyKey: "crm-call-789",
367
+ externalId: "crm-call-789",
368
+ spaceId: "space-id",
369
+ queueAnalysis: true,
370
+ agent: {
371
+ externalId: "agent-123",
372
+ name: "Morgan Agent",
373
+ },
374
+ lead: {
375
+ externalId: "prospect-456",
376
+ firstName: "Ada",
377
+ lastName: "Lovelace",
378
+ email: "ada@example.com",
379
+ customFields: {
380
+ crm_stage: "qualified",
381
+ },
382
+ },
383
+ audio: {
384
+ file: new Blob([buffer], { type: "audio/mpeg" }),
385
+ filename: "call.mp3",
386
+ },
387
+ });
388
+
389
+ console.log(response.data.data.id, response.data.data.analysisStatus, response.requestId);
390
+ ```
391
+
392
+ Returns: `Promise<VueVoxApiResponse<CallResponse>>`.
393
+
394
+ ### `vuevox.calls.queueAnalysis(callId)`
395
+
396
+ Queues analysis for a call that was uploaded with `queueAnalysis: false`.
397
+
398
+ Required scope: `calls:write`.
399
+
400
+ ```ts
401
+ const response = await vuevox.calls.queueAnalysis("call-id");
402
+ console.log(response.data.data.analysisStatus, response.data.data.queueStatus);
403
+ ```
404
+
405
+ Returns: `Promise<VueVoxApiResponse<CallResponse>>`.
406
+
407
+ ### `vuevox.calls.waitForAnalysis(callId, options?)`
408
+
409
+ Polls `vuevox.calls.get(callId)` until the call analysis status is `completed` or `failed`.
410
+
411
+ Required scope: `calls:read`.
412
+
413
+ Options: `WaitForAnalysisOptions`
414
+
415
+ | Option | Type | Description |
416
+ | --- | --- | --- |
417
+ | `intervalMs` | `number` | Poll interval. Defaults to `2000`. |
418
+ | `timeoutMs` | `number` | Timeout before throwing `analysis_timeout`. Defaults to `120000`. |
419
+
420
+ ```ts
421
+ const response = await vuevox.calls.waitForAnalysis("call-id", { timeoutMs: 180000 });
422
+ console.log(response.data.data.analysis);
423
+ ```
424
+
425
+ Returns: `Promise<VueVoxApiResponse<CallDetailResponse>>`.
426
+
315
427
  ### `vuevox.calls.paginate(options?)`
316
428
 
317
429
  Iterates organization calls across all pages.
@@ -497,6 +609,126 @@ await vuevox.leadCustomFields.update("crm_stage", {
497
609
 
498
610
  Returns: `Promise<VueVoxApiResponse<LeadCustomFieldResponse>>`.
499
611
 
612
+ ### `vuevox.webhooks.endpoints.list(options?)`
613
+
614
+ Lists webhook endpoints for the authenticated API client.
615
+
616
+ Required scope: `webhooks:read`.
617
+
618
+ Options: `ListWebhookEndpointsOptions`
619
+
620
+ ```ts
621
+ const response = await vuevox.webhooks.endpoints.list({ limit: 50 });
622
+ console.log(response.data.data);
623
+ ```
624
+
625
+ Returns: `Promise<VueVoxApiResponse<WebhookEndpointsListResponse>>`.
626
+
627
+ ### `vuevox.webhooks.endpoints.create(input)`
628
+
629
+ Creates a webhook endpoint. The signing secret is returned only once in this response.
630
+
631
+ Required scope: `webhooks:write`.
632
+
633
+ Input: `WebhookEndpointCreateRequest`
634
+
635
+ ```ts
636
+ const response = await vuevox.webhooks.endpoints.create({
637
+ url: "https://example.com/vuevox/webhooks",
638
+ events: ["analysis.completed", "analysis.failed"],
639
+ });
640
+
641
+ console.log(response.data.data.id, response.data.data.secret);
642
+ ```
643
+
644
+ Returns: `Promise<VueVoxApiResponse<WebhookEndpointSecretResponse>>`.
645
+
646
+ ### `vuevox.webhooks.endpoints.update(endpointId, input)`
647
+
648
+ Updates a webhook endpoint URL, event subscriptions, or active state.
649
+
650
+ Required scope: `webhooks:write`.
651
+
652
+ Input: `WebhookEndpointUpdateRequest`
653
+
654
+ ```ts
655
+ const response = await vuevox.webhooks.endpoints.update("endpoint-id", {
656
+ events: ["analysis.completed"],
657
+ isActive: true,
658
+ });
659
+ ```
660
+
661
+ Returns: `Promise<VueVoxApiResponse<WebhookEndpointResponse>>`.
662
+
663
+ ### `vuevox.webhooks.endpoints.delete(endpointId)`
664
+
665
+ Deletes a webhook endpoint.
666
+
667
+ Required scope: `webhooks:write`.
668
+
669
+ ```ts
670
+ await vuevox.webhooks.endpoints.delete("endpoint-id");
671
+ ```
672
+
673
+ Returns: `Promise<VueVoxApiResponse<null>>`.
674
+
675
+ ### `vuevox.webhooks.endpoints.rotateSecret(endpointId)`
676
+
677
+ Rotates the endpoint signing secret. The new secret is returned only once.
678
+
679
+ Required scope: `webhooks:write`.
680
+
681
+ ```ts
682
+ const response = await vuevox.webhooks.endpoints.rotateSecret("endpoint-id");
683
+ console.log(response.data.data.secret);
684
+ ```
685
+
686
+ Returns: `Promise<VueVoxApiResponse<WebhookEndpointSecretResponse>>`.
687
+
688
+ ### `vuevox.webhooks.endpoints.test(endpointId)`
689
+
690
+ Queues a `webhook.test` delivery to validate endpoint reachability and signature verification.
691
+
692
+ Required scope: `webhooks:write`.
693
+
694
+ ```ts
695
+ const response = await vuevox.webhooks.endpoints.test("endpoint-id");
696
+ console.log(response.data.data.eventType, response.data.data.status);
697
+ ```
698
+
699
+ Returns: `Promise<VueVoxApiResponse<WebhookTestResponse>>`.
700
+
701
+ ### `vuevox.webhooks.endpoints.paginate(options?)`
702
+
703
+ Iterates webhook endpoints across all pages.
704
+
705
+ Required scope: `webhooks:read`.
706
+
707
+ ```ts
708
+ for await (const endpoint of vuevox.webhooks.endpoints.paginate({ limit: 50 })) {
709
+ console.log(endpoint.id, endpoint.events);
710
+ }
711
+ ```
712
+
713
+ Returns: `AsyncGenerator<WebhookEndpoint>`.
714
+
715
+ ### `verifyVueVoxWebhookSignature(input)`
716
+
717
+ Verifies a webhook HMAC signature. Pass the raw request body string exactly as received.
718
+
719
+ ```ts
720
+ import { verifyVueVoxWebhookSignature } from "@vuevox/sdk";
721
+
722
+ const valid = await verifyVueVoxWebhookSignature({
723
+ body: rawBody,
724
+ secret: process.env.VUEVOX_WEBHOOK_SECRET!,
725
+ signature: request.headers.get("VueVox-Signature") ?? "",
726
+ timestamp: request.headers.get("VueVox-Timestamp") ?? "",
727
+ });
728
+ ```
729
+
730
+ Returns: `Promise<boolean>`.
731
+
500
732
  ## Lower-Level Calls
501
733
 
502
734
  For advanced integrations, `raw` exposes a typed lower-level OpenAPI client. You must attach authorization yourself.
@@ -551,6 +783,14 @@ insufficient_scope
551
783
  rate_limited
552
784
  invalid_request
553
785
  call_not_found
786
+ call_external_id_conflict
787
+ analysis_already_queued
788
+ analysis_timeout
789
+ ai_config_missing
790
+ evaluation_grid_missing
791
+ audio_not_found
792
+ idempotency_key_conflict
793
+ idempotency_key_in_progress
554
794
  lead_not_found
555
795
  custom_field_not_found
556
796
  custom_field_conflict
@@ -608,8 +848,10 @@ import type {
608
848
  Agent,
609
849
  AgentsListResponse,
610
850
  CallDetailResponse,
851
+ CallResponse,
611
852
  CallsListResponse,
612
853
  CallSummary,
854
+ CallUploadMetadata,
613
855
  CustomFieldFilters,
614
856
  CustomFieldFilterValue,
615
857
  HelloResponse,
@@ -628,13 +870,25 @@ import type {
628
870
  ListLeadCustomFieldsOptions,
629
871
  ListLeadsOptions,
630
872
  ListSpacesOptions,
873
+ ListWebhookEndpointsOptions,
631
874
  Space,
632
875
  SpacesListResponse,
876
+ UploadCallInput,
877
+ VerifyWebhookSignatureInput,
633
878
  VueVoxApiResponse,
634
879
  VueVoxClientOptions,
635
880
  VueVoxErrorResponse,
636
881
  VueVoxResponseEvent,
637
882
  VueVoxResponseMetadata,
883
+ WaitForAnalysisOptions,
884
+ WebhookEndpoint,
885
+ WebhookEndpointCreateRequest,
886
+ WebhookEndpointResponse,
887
+ WebhookEndpointSecretResponse,
888
+ WebhookEndpointUpdateRequest,
889
+ WebhookEndpointsListResponse,
890
+ WebhookEventPayload,
891
+ WebhookTestResponse,
638
892
  } from "@vuevox/sdk";
639
893
  ```
640
894
 
package/dist/client.d.ts CHANGED
@@ -4,12 +4,22 @@ export type SpacesListResponse = components["schemas"]["SpacesListResponse"];
4
4
  export type AgentsListResponse = components["schemas"]["AgentsListResponse"];
5
5
  export type CallsListResponse = components["schemas"]["CallsListResponse"];
6
6
  export type CallDetailResponse = components["schemas"]["CallDetailResponse"];
7
+ export type CallResponse = components["schemas"]["CallResponse"];
8
+ export type CallUploadMetadata = components["schemas"]["CallUploadMetadata"];
7
9
  export type LeadsListResponse = components["schemas"]["LeadsListResponse"];
8
10
  export type LeadDetailResponse = components["schemas"]["LeadDetailResponse"];
9
11
  export type LeadCustomFieldsListResponse = components["schemas"]["LeadCustomFieldsListResponse"];
10
12
  export type LeadCustomFieldResponse = components["schemas"]["LeadCustomFieldResponse"];
11
13
  export type LeadCustomFieldCreateRequest = components["schemas"]["LeadCustomFieldCreateRequest"];
12
14
  export type LeadCustomFieldUpdateRequest = components["schemas"]["LeadCustomFieldUpdateRequest"];
15
+ export type WebhookEndpoint = components["schemas"]["WebhookEndpoint"];
16
+ export type WebhookEndpointCreateRequest = components["schemas"]["WebhookEndpointCreateRequest"];
17
+ export type WebhookEndpointUpdateRequest = components["schemas"]["WebhookEndpointUpdateRequest"];
18
+ export type WebhookEndpointResponse = components["schemas"]["WebhookEndpointResponse"];
19
+ export type WebhookEndpointSecretResponse = components["schemas"]["WebhookEndpointSecretResponse"];
20
+ export type WebhookEndpointsListResponse = components["schemas"]["WebhookEndpointsListResponse"];
21
+ export type WebhookTestResponse = components["schemas"]["WebhookTestResponse"];
22
+ export type WebhookEventPayload = components["schemas"]["WebhookEventPayload"];
13
23
  export type LeadUpsertRequest = components["schemas"]["LeadUpsertRequest"];
14
24
  export type LeadUpdateRequest = components["schemas"]["LeadUpdateRequest"];
15
25
  export type Space = components["schemas"]["Space"];
@@ -40,6 +50,17 @@ export interface ListCallsOptions extends ListSpacesOptions {
40
50
  createdBefore?: string;
41
51
  leadCustomFields?: CustomFieldFilters;
42
52
  }
53
+ export interface UploadCallInput extends CallUploadMetadata {
54
+ idempotencyKey: string;
55
+ audio: {
56
+ file: Blob;
57
+ filename?: string;
58
+ };
59
+ }
60
+ export interface WaitForAnalysisOptions {
61
+ intervalMs?: number;
62
+ timeoutMs?: number;
63
+ }
43
64
  export interface ListAgentsOptions extends ListSpacesOptions {
44
65
  spaceId?: string;
45
66
  }
@@ -52,6 +73,16 @@ export interface ListLeadsOptions extends ListSpacesOptions {
52
73
  export interface ListLeadCustomFieldsOptions {
53
74
  includeArchived?: boolean;
54
75
  }
76
+ export interface ListWebhookEndpointsOptions extends ListSpacesOptions {
77
+ }
78
+ export interface VerifyWebhookSignatureInput {
79
+ body: string;
80
+ secret: string;
81
+ signature: string;
82
+ timestamp: string;
83
+ toleranceSeconds?: number;
84
+ now?: number;
85
+ }
55
86
  export interface VueVoxResponseMetadata {
56
87
  requestId?: string;
57
88
  status: number;
@@ -104,8 +135,12 @@ export declare function createVueVoxClient(options: VueVoxClientOptions): {
104
135
  calls: {
105
136
  list: (listOptions?: ListCallsOptions) => Promise<VueVoxApiResponse<CallsListResponse>>;
106
137
  get: (callId: string) => Promise<VueVoxApiResponse<CallDetailResponse>>;
138
+ upload: (input: UploadCallInput) => Promise<VueVoxApiResponse<CallResponse>>;
139
+ queueAnalysis: (callId: string) => Promise<VueVoxApiResponse<CallResponse>>;
140
+ waitForAnalysis: (callId: string, waitOptions?: WaitForAnalysisOptions) => Promise<VueVoxApiResponse<CallDetailResponse>>;
107
141
  paginate: (listOptions?: ListCallsOptions) => AsyncGenerator<{
108
142
  id: string;
143
+ externalId: string | null;
109
144
  space: components["schemas"]["ResourceSummary"];
110
145
  lead: components["schemas"]["LeadSummary"];
111
146
  agent: components["schemas"]["AgentSummary"];
@@ -141,5 +176,25 @@ export declare function createVueVoxClient(options: VueVoxClientOptions): {
141
176
  create: (input: LeadCustomFieldCreateRequest) => Promise<VueVoxApiResponse<LeadCustomFieldResponse>>;
142
177
  update: (key: string, input: LeadCustomFieldUpdateRequest) => Promise<VueVoxApiResponse<LeadCustomFieldResponse>>;
143
178
  };
179
+ webhooks: {
180
+ endpoints: {
181
+ list: (listOptions?: ListWebhookEndpointsOptions) => Promise<VueVoxApiResponse<WebhookEndpointsListResponse>>;
182
+ create: (input: WebhookEndpointCreateRequest) => Promise<VueVoxApiResponse<WebhookEndpointSecretResponse>>;
183
+ update: (endpointId: string, input: WebhookEndpointUpdateRequest) => Promise<VueVoxApiResponse<WebhookEndpointResponse>>;
184
+ delete: (endpointId: string) => Promise<VueVoxApiResponse<null>>;
185
+ rotateSecret: (endpointId: string) => Promise<VueVoxApiResponse<WebhookEndpointSecretResponse>>;
186
+ test: (endpointId: string) => Promise<VueVoxApiResponse<WebhookTestResponse>>;
187
+ paginate: (listOptions?: ListWebhookEndpointsOptions) => AsyncGenerator<{
188
+ id: string;
189
+ url: string;
190
+ events: components["schemas"]["WebhookEventType"][];
191
+ isActive: boolean;
192
+ lastDeliveredAt: string | null;
193
+ createdAt: string;
194
+ updatedAt: string;
195
+ }, any, any>;
196
+ };
197
+ };
144
198
  raw: import("openapi-fetch").Client<paths, `${string}/${string}`>;
145
199
  };
200
+ export declare function verifyVueVoxWebhookSignature(input: VerifyWebhookSignatureInput): Promise<boolean>;
package/dist/client.js CHANGED
@@ -46,6 +46,34 @@ export function createVueVoxClient(options) {
46
46
  async function getCall(callId) {
47
47
  return apiGet(`/v1/calls/${encodeURIComponent(callId)}`);
48
48
  }
49
+ async function uploadCall(input) {
50
+ const { audio, idempotencyKey, ...metadata } = input;
51
+ const formData = new FormData();
52
+ formData.set("metadata", JSON.stringify(metadata));
53
+ formData.set("audioFile", audio.file, audio.filename ?? "call-audio");
54
+ return apiMultipart("POST", "/v1/calls", formData, {
55
+ "Idempotency-Key": idempotencyKey,
56
+ });
57
+ }
58
+ async function queueCallAnalysis(callId) {
59
+ return apiJson("POST", `/v1/calls/${encodeURIComponent(callId)}/analysis-jobs`, {});
60
+ }
61
+ async function waitForCallAnalysis(callId, waitOptions = {}) {
62
+ const intervalMs = waitOptions.intervalMs ?? 2_000;
63
+ const timeoutMs = waitOptions.timeoutMs ?? 120_000;
64
+ const startedAt = Date.now();
65
+ for (;;) {
66
+ const response = await getCall(callId);
67
+ const status = response.data.data.analysisStatus;
68
+ if (status === "completed" || status === "failed") {
69
+ return response;
70
+ }
71
+ if (Date.now() - startedAt >= timeoutMs) {
72
+ throw new VueVoxApiError(408, "analysis_timeout", "Timed out waiting for call analysis.", undefined, response.requestId);
73
+ }
74
+ await sleep(intervalMs);
75
+ }
76
+ }
49
77
  async function listLeads(listOptions = {}) {
50
78
  return apiGet("/v1/leads", listOptions);
51
79
  }
@@ -67,6 +95,24 @@ export function createVueVoxClient(options) {
67
95
  async function updateLeadCustomField(key, input) {
68
96
  return apiJson("PATCH", `/v1/lead-custom-fields/${encodeURIComponent(key)}`, input);
69
97
  }
98
+ async function listWebhookEndpoints(listOptions = {}) {
99
+ return apiGet("/v1/webhooks/endpoints", listOptions);
100
+ }
101
+ async function createWebhookEndpoint(input) {
102
+ return apiJson("POST", "/v1/webhooks/endpoints", input);
103
+ }
104
+ async function updateWebhookEndpoint(endpointId, input) {
105
+ return apiJson("PATCH", `/v1/webhooks/endpoints/${encodeURIComponent(endpointId)}`, input);
106
+ }
107
+ async function deleteWebhookEndpoint(endpointId) {
108
+ return apiDelete(`/v1/webhooks/endpoints/${encodeURIComponent(endpointId)}`);
109
+ }
110
+ async function rotateWebhookEndpointSecret(endpointId) {
111
+ return apiJson("POST", `/v1/webhooks/endpoints/${encodeURIComponent(endpointId)}/rotate-secret`, {});
112
+ }
113
+ async function testWebhookEndpoint(endpointId) {
114
+ return apiJson("POST", `/v1/webhooks/endpoints/${encodeURIComponent(endpointId)}/test`, {});
115
+ }
70
116
  async function apiGet(path, query) {
71
117
  const accessToken = await getAccessToken();
72
118
  const result = await requestJson("GET", path, {
@@ -90,6 +136,36 @@ export function createVueVoxClient(options) {
90
136
  });
91
137
  return withMetadata(result.data, result.response, result.requestId);
92
138
  }
139
+ async function apiDelete(path) {
140
+ const accessToken = await getAccessToken();
141
+ const response = await fetchFn(buildUrl(baseUrl, path, undefined), {
142
+ method: "DELETE",
143
+ headers: {
144
+ Authorization: `Bearer ${accessToken}`,
145
+ },
146
+ });
147
+ const body = await parseJson(response);
148
+ const requestId = getRequestId(response, body);
149
+ const retryAfter = retryAfterSeconds(response);
150
+ notifyResponse(options, "DELETE", path, response, requestId, retryAfter);
151
+ if (response.ok) {
152
+ return withMetadata(null, response, requestId);
153
+ }
154
+ const error = isErrorResponse(body) ? body.error : null;
155
+ throw new VueVoxApiError(response.status, error?.code ?? "api_request_failed", error?.message ?? "VueVox API request failed.", isErrorResponse(body) ? body : undefined, requestId, retryAfter);
156
+ }
157
+ async function apiMultipart(method, path, body, headers) {
158
+ const accessToken = await getAccessToken();
159
+ const result = await requestJson(method, path, {
160
+ method,
161
+ headers: {
162
+ Authorization: `Bearer ${accessToken}`,
163
+ ...headers,
164
+ },
165
+ body,
166
+ });
167
+ return withMetadata(result.data, result.response, result.requestId);
168
+ }
93
169
  async function requestJson(method, path, init) {
94
170
  for (let attempt = 0;; attempt++) {
95
171
  const response = await fetchFn(buildUrl(baseUrl, path, init.query), init);
@@ -133,6 +209,9 @@ export function createVueVoxClient(options) {
133
209
  calls: {
134
210
  list: listCalls,
135
211
  get: getCall,
212
+ upload: uploadCall,
213
+ queueAnalysis: queueCallAnalysis,
214
+ waitForAnalysis: waitForCallAnalysis,
136
215
  paginate: (listOptions = {}) => paginate(listCalls, listOptions),
137
216
  },
138
217
  leads: {
@@ -147,9 +226,34 @@ export function createVueVoxClient(options) {
147
226
  create: createLeadCustomField,
148
227
  update: updateLeadCustomField,
149
228
  },
229
+ webhooks: {
230
+ endpoints: {
231
+ list: listWebhookEndpoints,
232
+ create: createWebhookEndpoint,
233
+ update: updateWebhookEndpoint,
234
+ delete: deleteWebhookEndpoint,
235
+ rotateSecret: rotateWebhookEndpointSecret,
236
+ test: testWebhookEndpoint,
237
+ paginate: (listOptions = {}) => paginate(listWebhookEndpoints, listOptions),
238
+ },
239
+ },
150
240
  raw,
151
241
  };
152
242
  }
243
+ export async function verifyVueVoxWebhookSignature(input) {
244
+ const toleranceSeconds = input.toleranceSeconds ?? 300;
245
+ const now = input.now ?? Date.now();
246
+ const timestampSeconds = Number(input.timestamp);
247
+ if (!Number.isFinite(timestampSeconds)) {
248
+ return false;
249
+ }
250
+ if (Math.abs(Math.floor(now / 1000) - timestampSeconds) > toleranceSeconds) {
251
+ return false;
252
+ }
253
+ const expected = await hmacSha256Hex(input.secret, `${input.timestamp}.${input.body}`);
254
+ const provided = input.signature.startsWith("v1=") ? input.signature.slice(3) : input.signature;
255
+ return constantTimeEqual(expected, provided);
256
+ }
153
257
  function formatScope(scope) {
154
258
  if (Array.isArray(scope)) {
155
259
  return scope.join(" ");
@@ -250,3 +354,18 @@ function retryAfterSeconds(response) {
250
354
  function sleep(ms) {
251
355
  return new Promise((resolve) => setTimeout(resolve, ms));
252
356
  }
357
+ async function hmacSha256Hex(secret, value) {
358
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
359
+ const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(value));
360
+ return [...new Uint8Array(signature)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
361
+ }
362
+ function constantTimeEqual(left, right) {
363
+ if (left.length !== right.length) {
364
+ return false;
365
+ }
366
+ let result = 0;
367
+ for (let index = 0; index < left.length; index++) {
368
+ result |= left.charCodeAt(index) ^ right.charCodeAt(index);
369
+ }
370
+ return result === 0;
371
+ }
@@ -97,7 +97,11 @@ export interface paths {
97
97
  */
98
98
  get: operations["listCalls"];
99
99
  put?: never;
100
- post?: never;
100
+ /**
101
+ * Upload a call and optionally queue analysis
102
+ * @description Uploads a call recording, upserts the agent and lead by externalId, creates the call in an existing platform-managed space, and optionally queues analysis. Requires an Idempotency-Key header for safe retries. The audio size limit is configured by a VueVox superadmin and defaults to 20 MB.
103
+ */
104
+ post: operations["uploadCall"];
101
105
  delete?: never;
102
106
  options?: never;
103
107
  head?: never;
@@ -124,6 +128,108 @@ export interface paths {
124
128
  patch?: never;
125
129
  trace?: never;
126
130
  };
131
+ "/v1/calls/{callId}/analysis-jobs": {
132
+ parameters: {
133
+ query?: never;
134
+ header?: never;
135
+ path?: never;
136
+ cookie?: never;
137
+ };
138
+ get?: never;
139
+ put?: never;
140
+ /**
141
+ * Queue analysis for a call
142
+ * @description Queues analysis for a previously uploaded organization call. Use this when a call was uploaded with queueAnalysis set to false.
143
+ */
144
+ post: operations["queueCallAnalysis"];
145
+ delete?: never;
146
+ options?: never;
147
+ head?: never;
148
+ patch?: never;
149
+ trace?: never;
150
+ };
151
+ "/v1/webhooks/endpoints": {
152
+ parameters: {
153
+ query?: never;
154
+ header?: never;
155
+ path?: never;
156
+ cookie?: never;
157
+ };
158
+ /**
159
+ * List webhook endpoints
160
+ * @description Returns webhook endpoints for the authenticated API client using cursor pagination.
161
+ */
162
+ get: operations["listWebhookEndpoints"];
163
+ put?: never;
164
+ /**
165
+ * Create a webhook endpoint
166
+ * @description Creates a webhook endpoint for the authenticated API client. The signing secret is returned only once.
167
+ */
168
+ post: operations["createWebhookEndpoint"];
169
+ delete?: never;
170
+ options?: never;
171
+ head?: never;
172
+ patch?: never;
173
+ trace?: never;
174
+ };
175
+ "/v1/webhooks/endpoints/{endpointId}": {
176
+ parameters: {
177
+ query?: never;
178
+ header?: never;
179
+ path?: never;
180
+ cookie?: never;
181
+ };
182
+ get?: never;
183
+ put?: never;
184
+ post?: never;
185
+ /** Delete a webhook endpoint */
186
+ delete: operations["deleteWebhookEndpoint"];
187
+ options?: never;
188
+ head?: never;
189
+ /** Update a webhook endpoint */
190
+ patch: operations["updateWebhookEndpoint"];
191
+ trace?: never;
192
+ };
193
+ "/v1/webhooks/endpoints/{endpointId}/rotate-secret": {
194
+ parameters: {
195
+ query?: never;
196
+ header?: never;
197
+ path?: never;
198
+ cookie?: never;
199
+ };
200
+ get?: never;
201
+ put?: never;
202
+ /**
203
+ * Rotate a webhook endpoint signing secret
204
+ * @description Replaces the endpoint signing secret. The new secret is returned only once.
205
+ */
206
+ post: operations["rotateWebhookEndpointSecret"];
207
+ delete?: never;
208
+ options?: never;
209
+ head?: never;
210
+ patch?: never;
211
+ trace?: never;
212
+ };
213
+ "/v1/webhooks/endpoints/{endpointId}/test": {
214
+ parameters: {
215
+ query?: never;
216
+ header?: never;
217
+ path?: never;
218
+ cookie?: never;
219
+ };
220
+ get?: never;
221
+ put?: never;
222
+ /**
223
+ * Send a test webhook event
224
+ * @description Queues a webhook.test delivery to validate endpoint reachability and signature verification.
225
+ */
226
+ post: operations["testWebhookEndpoint"];
227
+ delete?: never;
228
+ options?: never;
229
+ head?: never;
230
+ patch?: never;
231
+ trace?: never;
232
+ };
127
233
  "/v1/leads": {
128
234
  parameters: {
129
235
  query?: never;
@@ -243,7 +349,7 @@ export interface components {
243
349
  client_secret: string;
244
350
  /**
245
351
  * @description Space-separated scopes requested for the access token.
246
- * @example hello:read spaces:read agents:read calls:read leads:read leads:write lead_custom_fields:manage
352
+ * @example hello:read spaces:read agents:read calls:read calls:write leads:read leads:write lead_custom_fields:manage webhooks:read webhooks:write
247
353
  */
248
354
  scope?: string;
249
355
  };
@@ -253,7 +359,7 @@ export interface components {
253
359
  token_type: "Bearer";
254
360
  /** @example 3600 */
255
361
  expires_in: number;
256
- /** @example hello:read spaces:read agents:read calls:read leads:read leads:write lead_custom_fields:manage */
362
+ /** @example hello:read spaces:read agents:read calls:read calls:write leads:read leads:write lead_custom_fields:manage webhooks:read webhooks:write */
257
363
  scope: string;
258
364
  };
259
365
  HelloResponse: {
@@ -317,6 +423,8 @@ export interface components {
317
423
  CallSummary: {
318
424
  /** Format: uuid */
319
425
  id: string;
426
+ /** @description Optional ID from an external system. */
427
+ externalId: string | null;
320
428
  space: components["schemas"]["ResourceSummary"];
321
429
  lead: components["schemas"]["LeadSummary"];
322
430
  agent: components["schemas"]["AgentSummary"];
@@ -357,6 +465,44 @@ export interface components {
357
465
  CallDetailResponse: {
358
466
  data: components["schemas"]["CallDetail"];
359
467
  };
468
+ CallResponse: {
469
+ data: components["schemas"]["CallSummary"];
470
+ };
471
+ CallUploadMetadata: {
472
+ /** @description Optional call ID from your system. Must be unique within the organization when provided. */
473
+ externalId?: string | null;
474
+ /**
475
+ * Format: uuid
476
+ * @description Existing VueVox space ID. Spaces are managed in the VueVox platform.
477
+ */
478
+ spaceId: string;
479
+ /**
480
+ * @description When true or omitted, queues analysis immediately after upload. When false, queue later with queueCallAnalysis.
481
+ * @default true
482
+ */
483
+ queueAnalysis: boolean;
484
+ agent: components["schemas"]["CallUploadAgent"];
485
+ lead: components["schemas"]["CallUploadLead"];
486
+ };
487
+ CallUploadAgent: {
488
+ externalId: string;
489
+ /** @description Required when creating a new agent. */
490
+ name?: string;
491
+ /** Format: email */
492
+ email?: string | null;
493
+ phone?: string | null;
494
+ };
495
+ CallUploadLead: {
496
+ externalId: string;
497
+ /** @description Required when creating a new lead. */
498
+ firstName?: string;
499
+ /** @description Required when creating a new lead. */
500
+ lastName?: string;
501
+ /** Format: email */
502
+ email?: string | null;
503
+ phone?: string | null;
504
+ customFields?: components["schemas"]["CustomFields"];
505
+ };
360
506
  Lead: {
361
507
  /** Format: uuid */
362
508
  id: string;
@@ -447,6 +593,79 @@ export interface components {
447
593
  isFilterable?: boolean;
448
594
  archived?: boolean;
449
595
  };
596
+ /**
597
+ * @description Subscribable webhook event type.
598
+ * @enum {string}
599
+ */
600
+ WebhookEventType: "analysis.completed" | "analysis.failed";
601
+ WebhookEndpoint: {
602
+ /** Format: uuid */
603
+ id: string;
604
+ /** Format: uri */
605
+ url: string;
606
+ events: components["schemas"]["WebhookEventType"][];
607
+ isActive: boolean;
608
+ /** Format: date-time */
609
+ lastDeliveredAt: string | null;
610
+ /** Format: date-time */
611
+ createdAt: string;
612
+ /** Format: date-time */
613
+ updatedAt: string;
614
+ };
615
+ WebhookEndpointWithSecret: components["schemas"]["WebhookEndpoint"] & {
616
+ /** @description Signing secret shown only when creating or rotating the endpoint. */
617
+ secret: string;
618
+ };
619
+ WebhookEndpointsListResponse: {
620
+ data: components["schemas"]["WebhookEndpoint"][];
621
+ pagination: components["schemas"]["CursorPagination"];
622
+ };
623
+ WebhookEndpointResponse: {
624
+ data: components["schemas"]["WebhookEndpoint"];
625
+ };
626
+ WebhookEndpointSecretResponse: {
627
+ data: components["schemas"]["WebhookEndpointWithSecret"];
628
+ };
629
+ WebhookEndpointCreateRequest: {
630
+ /**
631
+ * Format: uri
632
+ * @description Public HTTPS URL that will receive webhook POST requests.
633
+ */
634
+ url: string;
635
+ events: components["schemas"]["WebhookEventType"][];
636
+ /** @default true */
637
+ isActive: boolean;
638
+ };
639
+ WebhookEndpointUpdateRequest: {
640
+ /** Format: uri */
641
+ url?: string;
642
+ events?: components["schemas"]["WebhookEventType"][];
643
+ isActive?: boolean;
644
+ };
645
+ WebhookTestResponse: {
646
+ data: {
647
+ /**
648
+ * Format: uuid
649
+ * @description Delivery ID.
650
+ */
651
+ id: string;
652
+ /** @enum {string} */
653
+ status: "pending" | "retrying" | "delivered" | "failed" | "skipped";
654
+ /** @enum {string} */
655
+ eventType: "webhook.test";
656
+ };
657
+ };
658
+ WebhookEventPayload: {
659
+ /** Format: uuid */
660
+ id: string;
661
+ /** @enum {string} */
662
+ type: "analysis.completed" | "analysis.failed" | "webhook.test";
663
+ /** Format: date-time */
664
+ createdAt: string;
665
+ data: {
666
+ [key: string]: unknown;
667
+ };
668
+ };
450
669
  CursorPagination: {
451
670
  /** @example 50 */
452
671
  limit: number;
@@ -471,7 +690,10 @@ export interface components {
471
690
  };
472
691
  };
473
692
  responses: never;
474
- parameters: never;
693
+ parameters: {
694
+ /** @description Webhook endpoint ID. */
695
+ WebhookEndpointId: string;
696
+ };
475
697
  requestBodies: never;
476
698
  headers: never;
477
699
  pathItems: never;
@@ -789,6 +1011,77 @@ export interface operations {
789
1011
  };
790
1012
  };
791
1013
  };
1014
+ uploadCall: {
1015
+ parameters: {
1016
+ query?: never;
1017
+ header: {
1018
+ /** @description Unique key for this upload attempt. Reusing the same key with the same request returns the original response; reusing it with a different request returns a conflict. */
1019
+ "Idempotency-Key": string;
1020
+ };
1021
+ path?: never;
1022
+ cookie?: never;
1023
+ };
1024
+ requestBody: {
1025
+ content: {
1026
+ "multipart/form-data": {
1027
+ /** @description JSON string matching CallUploadMetadata. */
1028
+ metadata: string;
1029
+ /**
1030
+ * Format: binary
1031
+ * @description Audio file. Supported extensions include wav, mp3, aac, m4a, amr, mpeg, mpga, x-wav, ogg, oga, mp4, and mp4a.
1032
+ */
1033
+ audioFile: string;
1034
+ };
1035
+ };
1036
+ };
1037
+ responses: {
1038
+ /** @description Call uploaded. Analysis is queued when metadata.queueAnalysis is true. */
1039
+ 201: {
1040
+ headers: {
1041
+ [name: string]: unknown;
1042
+ };
1043
+ content: {
1044
+ "application/json": components["schemas"]["CallResponse"];
1045
+ };
1046
+ };
1047
+ /** @description Bearer token is missing, invalid, or expired. */
1048
+ 401: {
1049
+ headers: {
1050
+ [name: string]: unknown;
1051
+ };
1052
+ content: {
1053
+ "application/json": components["schemas"]["ErrorResponse"];
1054
+ };
1055
+ };
1056
+ /** @description Bearer token does not include the required scope. */
1057
+ 403: {
1058
+ headers: {
1059
+ [name: string]: unknown;
1060
+ };
1061
+ content: {
1062
+ "application/json": components["schemas"]["ErrorResponse"];
1063
+ };
1064
+ };
1065
+ /** @description Idempotency key conflict, external call ID conflict, or analysis already queued. */
1066
+ 409: {
1067
+ headers: {
1068
+ [name: string]: unknown;
1069
+ };
1070
+ content: {
1071
+ "application/json": components["schemas"]["ErrorResponse"];
1072
+ };
1073
+ };
1074
+ /** @description Multipart upload or metadata failed validation. */
1075
+ 422: {
1076
+ headers: {
1077
+ [name: string]: unknown;
1078
+ };
1079
+ content: {
1080
+ "application/json": components["schemas"]["ErrorResponse"];
1081
+ };
1082
+ };
1083
+ };
1084
+ };
792
1085
  getCall: {
793
1086
  parameters: {
794
1087
  query?: never;
@@ -852,6 +1145,388 @@ export interface operations {
852
1145
  };
853
1146
  };
854
1147
  };
1148
+ queueCallAnalysis: {
1149
+ parameters: {
1150
+ query?: never;
1151
+ header?: never;
1152
+ path: {
1153
+ /** @description Call ID. */
1154
+ callId: string;
1155
+ };
1156
+ cookie?: never;
1157
+ };
1158
+ requestBody?: never;
1159
+ responses: {
1160
+ /** @description Analysis queued. */
1161
+ 200: {
1162
+ headers: {
1163
+ [name: string]: unknown;
1164
+ };
1165
+ content: {
1166
+ "application/json": components["schemas"]["CallResponse"];
1167
+ };
1168
+ };
1169
+ /** @description Bearer token is missing, invalid, or expired. */
1170
+ 401: {
1171
+ headers: {
1172
+ [name: string]: unknown;
1173
+ };
1174
+ content: {
1175
+ "application/json": components["schemas"]["ErrorResponse"];
1176
+ };
1177
+ };
1178
+ /** @description Bearer token does not include the required scope. */
1179
+ 403: {
1180
+ headers: {
1181
+ [name: string]: unknown;
1182
+ };
1183
+ content: {
1184
+ "application/json": components["schemas"]["ErrorResponse"];
1185
+ };
1186
+ };
1187
+ /** @description Call was not found in the API client's organization. */
1188
+ 404: {
1189
+ headers: {
1190
+ [name: string]: unknown;
1191
+ };
1192
+ content: {
1193
+ "application/json": components["schemas"]["ErrorResponse"];
1194
+ };
1195
+ };
1196
+ /** @description Analysis is already queued or processing. */
1197
+ 409: {
1198
+ headers: {
1199
+ [name: string]: unknown;
1200
+ };
1201
+ content: {
1202
+ "application/json": components["schemas"]["ErrorResponse"];
1203
+ };
1204
+ };
1205
+ /** @description Organization AI settings, evaluation grid, or audio file is missing. */
1206
+ 422: {
1207
+ headers: {
1208
+ [name: string]: unknown;
1209
+ };
1210
+ content: {
1211
+ "application/json": components["schemas"]["ErrorResponse"];
1212
+ };
1213
+ };
1214
+ };
1215
+ };
1216
+ listWebhookEndpoints: {
1217
+ parameters: {
1218
+ query?: {
1219
+ /** @description Number of endpoints to return. Defaults to 50. Maximum is 100. */
1220
+ limit?: number;
1221
+ /** @description Opaque cursor from the previous response's pagination.nextCursor value. */
1222
+ cursor?: string;
1223
+ };
1224
+ header?: never;
1225
+ path?: never;
1226
+ cookie?: never;
1227
+ };
1228
+ requestBody?: never;
1229
+ responses: {
1230
+ /** @description Paginated webhook endpoints response. */
1231
+ 200: {
1232
+ headers: {
1233
+ [name: string]: unknown;
1234
+ };
1235
+ content: {
1236
+ "application/json": components["schemas"]["WebhookEndpointsListResponse"];
1237
+ };
1238
+ };
1239
+ /** @description Bearer token is missing, invalid, or expired. */
1240
+ 401: {
1241
+ headers: {
1242
+ [name: string]: unknown;
1243
+ };
1244
+ content: {
1245
+ "application/json": components["schemas"]["ErrorResponse"];
1246
+ };
1247
+ };
1248
+ /** @description Bearer token does not include the required scope. */
1249
+ 403: {
1250
+ headers: {
1251
+ [name: string]: unknown;
1252
+ };
1253
+ content: {
1254
+ "application/json": components["schemas"]["ErrorResponse"];
1255
+ };
1256
+ };
1257
+ /** @description Query parameters failed validation. */
1258
+ 422: {
1259
+ headers: {
1260
+ [name: string]: unknown;
1261
+ };
1262
+ content: {
1263
+ "application/json": components["schemas"]["ErrorResponse"];
1264
+ };
1265
+ };
1266
+ };
1267
+ };
1268
+ createWebhookEndpoint: {
1269
+ parameters: {
1270
+ query?: never;
1271
+ header?: never;
1272
+ path?: never;
1273
+ cookie?: never;
1274
+ };
1275
+ requestBody: {
1276
+ content: {
1277
+ "application/json": components["schemas"]["WebhookEndpointCreateRequest"];
1278
+ };
1279
+ };
1280
+ responses: {
1281
+ /** @description Webhook endpoint created. */
1282
+ 201: {
1283
+ headers: {
1284
+ [name: string]: unknown;
1285
+ };
1286
+ content: {
1287
+ "application/json": components["schemas"]["WebhookEndpointSecretResponse"];
1288
+ };
1289
+ };
1290
+ /** @description Bearer token is missing, invalid, or expired. */
1291
+ 401: {
1292
+ headers: {
1293
+ [name: string]: unknown;
1294
+ };
1295
+ content: {
1296
+ "application/json": components["schemas"]["ErrorResponse"];
1297
+ };
1298
+ };
1299
+ /** @description Bearer token does not include the required scope. */
1300
+ 403: {
1301
+ headers: {
1302
+ [name: string]: unknown;
1303
+ };
1304
+ content: {
1305
+ "application/json": components["schemas"]["ErrorResponse"];
1306
+ };
1307
+ };
1308
+ /** @description Request body failed validation. */
1309
+ 422: {
1310
+ headers: {
1311
+ [name: string]: unknown;
1312
+ };
1313
+ content: {
1314
+ "application/json": components["schemas"]["ErrorResponse"];
1315
+ };
1316
+ };
1317
+ };
1318
+ };
1319
+ deleteWebhookEndpoint: {
1320
+ parameters: {
1321
+ query?: never;
1322
+ header?: never;
1323
+ path: {
1324
+ /** @description Webhook endpoint ID. */
1325
+ endpointId: components["parameters"]["WebhookEndpointId"];
1326
+ };
1327
+ cookie?: never;
1328
+ };
1329
+ requestBody?: never;
1330
+ responses: {
1331
+ /** @description Webhook endpoint deleted. */
1332
+ 204: {
1333
+ headers: {
1334
+ [name: string]: unknown;
1335
+ };
1336
+ content?: never;
1337
+ };
1338
+ /** @description Bearer token is missing, invalid, or expired. */
1339
+ 401: {
1340
+ headers: {
1341
+ [name: string]: unknown;
1342
+ };
1343
+ content: {
1344
+ "application/json": components["schemas"]["ErrorResponse"];
1345
+ };
1346
+ };
1347
+ /** @description Bearer token does not include the required scope. */
1348
+ 403: {
1349
+ headers: {
1350
+ [name: string]: unknown;
1351
+ };
1352
+ content: {
1353
+ "application/json": components["schemas"]["ErrorResponse"];
1354
+ };
1355
+ };
1356
+ /** @description Webhook endpoint was not found for this API client. */
1357
+ 404: {
1358
+ headers: {
1359
+ [name: string]: unknown;
1360
+ };
1361
+ content: {
1362
+ "application/json": components["schemas"]["ErrorResponse"];
1363
+ };
1364
+ };
1365
+ };
1366
+ };
1367
+ updateWebhookEndpoint: {
1368
+ parameters: {
1369
+ query?: never;
1370
+ header?: never;
1371
+ path: {
1372
+ /** @description Webhook endpoint ID. */
1373
+ endpointId: components["parameters"]["WebhookEndpointId"];
1374
+ };
1375
+ cookie?: never;
1376
+ };
1377
+ requestBody: {
1378
+ content: {
1379
+ "application/json": components["schemas"]["WebhookEndpointUpdateRequest"];
1380
+ };
1381
+ };
1382
+ responses: {
1383
+ /** @description Webhook endpoint updated. */
1384
+ 200: {
1385
+ headers: {
1386
+ [name: string]: unknown;
1387
+ };
1388
+ content: {
1389
+ "application/json": components["schemas"]["WebhookEndpointResponse"];
1390
+ };
1391
+ };
1392
+ /** @description Bearer token is missing, invalid, or expired. */
1393
+ 401: {
1394
+ headers: {
1395
+ [name: string]: unknown;
1396
+ };
1397
+ content: {
1398
+ "application/json": components["schemas"]["ErrorResponse"];
1399
+ };
1400
+ };
1401
+ /** @description Bearer token does not include the required scope. */
1402
+ 403: {
1403
+ headers: {
1404
+ [name: string]: unknown;
1405
+ };
1406
+ content: {
1407
+ "application/json": components["schemas"]["ErrorResponse"];
1408
+ };
1409
+ };
1410
+ /** @description Webhook endpoint was not found for this API client. */
1411
+ 404: {
1412
+ headers: {
1413
+ [name: string]: unknown;
1414
+ };
1415
+ content: {
1416
+ "application/json": components["schemas"]["ErrorResponse"];
1417
+ };
1418
+ };
1419
+ /** @description Request body failed validation. */
1420
+ 422: {
1421
+ headers: {
1422
+ [name: string]: unknown;
1423
+ };
1424
+ content: {
1425
+ "application/json": components["schemas"]["ErrorResponse"];
1426
+ };
1427
+ };
1428
+ };
1429
+ };
1430
+ rotateWebhookEndpointSecret: {
1431
+ parameters: {
1432
+ query?: never;
1433
+ header?: never;
1434
+ path: {
1435
+ /** @description Webhook endpoint ID. */
1436
+ endpointId: components["parameters"]["WebhookEndpointId"];
1437
+ };
1438
+ cookie?: never;
1439
+ };
1440
+ requestBody?: never;
1441
+ responses: {
1442
+ /** @description Webhook endpoint secret rotated. */
1443
+ 200: {
1444
+ headers: {
1445
+ [name: string]: unknown;
1446
+ };
1447
+ content: {
1448
+ "application/json": components["schemas"]["WebhookEndpointSecretResponse"];
1449
+ };
1450
+ };
1451
+ /** @description Bearer token is missing, invalid, or expired. */
1452
+ 401: {
1453
+ headers: {
1454
+ [name: string]: unknown;
1455
+ };
1456
+ content: {
1457
+ "application/json": components["schemas"]["ErrorResponse"];
1458
+ };
1459
+ };
1460
+ /** @description Bearer token does not include the required scope. */
1461
+ 403: {
1462
+ headers: {
1463
+ [name: string]: unknown;
1464
+ };
1465
+ content: {
1466
+ "application/json": components["schemas"]["ErrorResponse"];
1467
+ };
1468
+ };
1469
+ /** @description Webhook endpoint was not found for this API client. */
1470
+ 404: {
1471
+ headers: {
1472
+ [name: string]: unknown;
1473
+ };
1474
+ content: {
1475
+ "application/json": components["schemas"]["ErrorResponse"];
1476
+ };
1477
+ };
1478
+ };
1479
+ };
1480
+ testWebhookEndpoint: {
1481
+ parameters: {
1482
+ query?: never;
1483
+ header?: never;
1484
+ path: {
1485
+ /** @description Webhook endpoint ID. */
1486
+ endpointId: components["parameters"]["WebhookEndpointId"];
1487
+ };
1488
+ cookie?: never;
1489
+ };
1490
+ requestBody?: never;
1491
+ responses: {
1492
+ /** @description Test delivery queued. */
1493
+ 202: {
1494
+ headers: {
1495
+ [name: string]: unknown;
1496
+ };
1497
+ content: {
1498
+ "application/json": components["schemas"]["WebhookTestResponse"];
1499
+ };
1500
+ };
1501
+ /** @description Bearer token is missing, invalid, or expired. */
1502
+ 401: {
1503
+ headers: {
1504
+ [name: string]: unknown;
1505
+ };
1506
+ content: {
1507
+ "application/json": components["schemas"]["ErrorResponse"];
1508
+ };
1509
+ };
1510
+ /** @description Bearer token does not include the required scope. */
1511
+ 403: {
1512
+ headers: {
1513
+ [name: string]: unknown;
1514
+ };
1515
+ content: {
1516
+ "application/json": components["schemas"]["ErrorResponse"];
1517
+ };
1518
+ };
1519
+ /** @description Webhook endpoint was not found for this API client. */
1520
+ 404: {
1521
+ headers: {
1522
+ [name: string]: unknown;
1523
+ };
1524
+ content: {
1525
+ "application/json": components["schemas"]["ErrorResponse"];
1526
+ };
1527
+ };
1528
+ };
1529
+ };
855
1530
  listLeads: {
856
1531
  parameters: {
857
1532
  query?: {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { createVueVoxClient } from "./client.js";
2
- export type { Agent, AgentsListResponse, CallDetailResponse, CallsListResponse, CallSummary, CustomFieldFilters, CustomFieldFilterValue, HelloResponse, Lead, LeadCustomField, LeadCustomFieldCreateRequest, LeadCustomFieldResponse, LeadCustomFieldUpdateRequest, LeadCustomFieldsListResponse, LeadDetailResponse, LeadUpdateRequest, LeadUpsertRequest, LeadsListResponse, ListAgentsOptions, ListCallsOptions, ListLeadCustomFieldsOptions, ListLeadsOptions, ListSpacesOptions, Space, SpacesListResponse, VueVoxApiResponse, VueVoxClientOptions, VueVoxResponseEvent, VueVoxResponseMetadata, } from "./client.js";
1
+ export { createVueVoxClient, verifyVueVoxWebhookSignature } from "./client.js";
2
+ export type { Agent, AgentsListResponse, CallDetailResponse, CallResponse, CallsListResponse, CallSummary, CallUploadMetadata, CustomFieldFilters, CustomFieldFilterValue, HelloResponse, Lead, LeadCustomField, LeadCustomFieldCreateRequest, LeadCustomFieldResponse, LeadCustomFieldUpdateRequest, LeadCustomFieldsListResponse, LeadDetailResponse, LeadUpdateRequest, LeadUpsertRequest, LeadsListResponse, ListAgentsOptions, ListCallsOptions, ListLeadCustomFieldsOptions, ListLeadsOptions, ListSpacesOptions, ListWebhookEndpointsOptions, Space, SpacesListResponse, UploadCallInput, VerifyWebhookSignatureInput, VueVoxApiResponse, VueVoxClientOptions, VueVoxResponseEvent, VueVoxResponseMetadata, WaitForAnalysisOptions, WebhookEndpoint, WebhookEndpointCreateRequest, WebhookEndpointResponse, WebhookEndpointSecretResponse, WebhookEndpointUpdateRequest, WebhookEndpointsListResponse, WebhookEventPayload, WebhookTestResponse, } from "./client.js";
3
3
  export { VueVoxApiError } from "./errors.js";
4
4
  export type { VueVoxErrorResponse } from "./errors.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { createVueVoxClient } from "./client.js";
1
+ export { createVueVoxClient, verifyVueVoxWebhookSignature } from "./client.js";
2
2
  export { VueVoxApiError } from "./errors.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuevox/sdk",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "TypeScript SDK for the VueVox Developer API.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",