@vuevox/sdk 0.2.0 → 0.4.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
@@ -23,6 +23,8 @@ Available scopes:
23
23
  ```text
24
24
  hello:read
25
25
  spaces:read
26
+ calls:read
27
+ leads:read
26
28
  ```
27
29
 
28
30
  ## Basic Usage
@@ -33,21 +35,51 @@ import { createVueVoxClient } from "@vuevox/sdk";
33
35
  const vuevox = createVueVoxClient({
34
36
  clientId: process.env.VUEVOX_CLIENT_ID!,
35
37
  clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
36
- scope: ["hello:read", "spaces:read"],
38
+ scope: ["hello:read", "spaces:read", "calls:read", "leads:read"],
37
39
  });
38
40
 
39
- const hello = await vuevox.hello();
40
- console.log(hello.message);
41
+ const hello = await vuevox.hello.get();
42
+ console.log(hello.data.message, hello.requestId);
41
43
 
42
- const spaces = await vuevox.listSpaces({ limit: 50 });
43
- console.log(spaces.data);
44
+ const spaces = await vuevox.spaces.list({ limit: 50 });
45
+ console.log(spaces.data.data, spaces.requestId);
46
+
47
+ const calls = await vuevox.calls.list({ limit: 50 });
48
+ console.log(calls.data.data, calls.requestId);
49
+
50
+ const leads = await vuevox.leads.list({ limit: 50 });
51
+ console.log(leads.data.data, leads.requestId);
44
52
  ```
45
53
 
46
54
  The SDK requests and caches a short-lived access token using client credentials, then sends it as a bearer token for API calls.
47
55
 
56
+ ## Request IDs
57
+
58
+ Every Developer API response includes an `X-Request-Id` header. SDK endpoint methods return response metadata with the response body so you can log that ID for support requests.
59
+
60
+ ```ts
61
+ const call = await vuevox.calls.get("call-id");
62
+
63
+ console.log(call.requestId);
64
+ console.log(call.data.data.transcript);
65
+ ```
66
+
67
+ For centralized logging, pass `onResponse`. The hook runs for every SDK-managed HTTP response, including the token request.
68
+
69
+ ```ts
70
+ const vuevox = createVueVoxClient({
71
+ clientId: process.env.VUEVOX_CLIENT_ID!,
72
+ clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
73
+ scope: ["calls:read"],
74
+ onResponse: ({ method, path, status, requestId }) => {
75
+ console.log({ method, path, status, requestId });
76
+ },
77
+ });
78
+ ```
79
+
48
80
  ## Error Handling
49
81
 
50
- API errors throw `VueVoxApiError`.
82
+ API errors throw `VueVoxApiError`. The SDK exposes `error.requestId`, `error.details`, `error.retryAfter`, and `error.isRateLimited`.
51
83
 
52
84
  ```ts
53
85
  import { VueVoxApiError, createVueVoxClient } from "@vuevox/sdk";
@@ -55,14 +87,14 @@ import { VueVoxApiError, createVueVoxClient } from "@vuevox/sdk";
55
87
  const vuevox = createVueVoxClient({
56
88
  clientId: process.env.VUEVOX_CLIENT_ID!,
57
89
  clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
58
- scope: ["hello:read", "spaces:read"],
90
+ scope: ["calls:read"],
59
91
  });
60
92
 
61
93
  try {
62
- await vuevox.listSpaces({ limit: 50 });
94
+ await vuevox.calls.list({ limit: 50 });
63
95
  } catch (error) {
64
96
  if (error instanceof VueVoxApiError) {
65
- console.error(error.status, error.code, error.message);
97
+ console.error(error.status, error.code, error.message, error.requestId);
66
98
  }
67
99
 
68
100
  throw error;
@@ -78,6 +110,49 @@ invalid_client
78
110
  invalid_scope
79
111
  insufficient_scope
80
112
  rate_limited
113
+ invalid_request
114
+ call_not_found
115
+ lead_not_found
116
+ ```
117
+
118
+ ## Pagination
119
+
120
+ List endpoints use cursor pagination.
121
+
122
+ ```ts
123
+ const firstPage = await vuevox.calls.list({ limit: 50 });
124
+
125
+ if (firstPage.data.pagination.nextCursor) {
126
+ const secondPage = await vuevox.calls.list({
127
+ limit: 50,
128
+ cursor: firstPage.data.pagination.nextCursor,
129
+ });
130
+ }
131
+ ```
132
+
133
+ The SDK also includes async iterable helpers:
134
+
135
+ ```ts
136
+ for await (const call of vuevox.calls.paginate({ limit: 50 })) {
137
+ console.log(call.id);
138
+ }
139
+ ```
140
+
141
+ Pagination helpers are available for `spaces`, `calls`, and `leads`.
142
+
143
+ ## Retries
144
+
145
+ Configure retry/backoff for safe SDK-managed requests. The SDK retries token requests and GET endpoints for `429`, `500`, `502`, `503`, and `504`, and respects `Retry-After` when present.
146
+
147
+ ```ts
148
+ const vuevox = createVueVoxClient({
149
+ clientId: process.env.VUEVOX_CLIENT_ID!,
150
+ clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
151
+ scope: ["calls:read"],
152
+ retries: 2,
153
+ retryBaseDelayMs: 250,
154
+ retryMaxDelayMs: 2000,
155
+ });
81
156
  ```
82
157
 
83
158
  ## Token Behavior
@@ -104,7 +179,7 @@ const vuevox = createVueVoxClient({
104
179
  baseUrl: "https://api.vuevox.com",
105
180
  clientId: process.env.VUEVOX_CLIENT_ID!,
106
181
  clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
107
- scope: ["hello:read", "spaces:read"],
182
+ scope: ["calls:read"],
108
183
  });
109
184
  ```
110
185
 
@@ -119,18 +194,3 @@ const { data, error } = await vuevox.raw.GET("/v1/hello", {
119
194
  },
120
195
  });
121
196
  ```
122
-
123
- ## Pagination
124
-
125
- List endpoints use cursor pagination.
126
-
127
- ```ts
128
- const firstPage = await vuevox.listSpaces({ limit: 50 });
129
-
130
- if (firstPage.pagination.nextCursor) {
131
- const secondPage = await vuevox.listSpaces({
132
- limit: 50,
133
- cursor: firstPage.pagination.nextCursor,
134
- });
135
- }
136
- ```
package/dist/client.d.ts CHANGED
@@ -1,21 +1,98 @@
1
1
  import type { components, paths } from "./generated/schema.js";
2
2
  type HelloResponse = components["schemas"]["HelloResponse"];
3
3
  type SpacesListResponse = components["schemas"]["SpacesListResponse"];
4
+ type CallsListResponse = components["schemas"]["CallsListResponse"];
5
+ type CallDetailResponse = components["schemas"]["CallDetailResponse"];
6
+ type LeadsListResponse = components["schemas"]["LeadsListResponse"];
7
+ type LeadDetailResponse = components["schemas"]["LeadDetailResponse"];
8
+ type Space = components["schemas"]["Space"];
9
+ type CallSummary = components["schemas"]["CallSummary"];
10
+ type Lead = components["schemas"]["Lead"];
4
11
  export interface ListSpacesOptions {
5
12
  limit?: number;
6
13
  cursor?: string;
7
14
  }
15
+ export interface ListCallsOptions extends ListSpacesOptions {
16
+ spaceId?: string;
17
+ leadId?: string;
18
+ agentId?: string;
19
+ createdAfter?: string;
20
+ createdBefore?: string;
21
+ }
22
+ export interface ListLeadsOptions extends ListSpacesOptions {
23
+ spaceId?: string;
24
+ createdAfter?: string;
25
+ createdBefore?: string;
26
+ }
27
+ export interface VueVoxResponseMetadata {
28
+ requestId?: string;
29
+ status: number;
30
+ }
31
+ export interface VueVoxApiResponse<T> extends VueVoxResponseMetadata {
32
+ data: T;
33
+ }
34
+ export interface VueVoxResponseEvent extends VueVoxResponseMetadata {
35
+ method: string;
36
+ path: string;
37
+ retryAfter?: number;
38
+ }
8
39
  export interface VueVoxClientOptions {
9
40
  baseUrl?: string;
10
41
  clientId: string;
11
42
  clientSecret: string;
12
43
  scope?: string | string[];
13
44
  fetch?: typeof fetch;
45
+ onResponse?: (event: VueVoxResponseEvent) => void;
46
+ retries?: number;
47
+ retryBaseDelayMs?: number;
48
+ retryMaxDelayMs?: number;
14
49
  }
15
50
  export declare function createVueVoxClient(options: VueVoxClientOptions): {
16
51
  getAccessToken: () => Promise<string>;
17
- hello: () => Promise<HelloResponse>;
18
- listSpaces: (options?: ListSpacesOptions) => Promise<SpacesListResponse>;
52
+ hello: {
53
+ get: () => Promise<VueVoxApiResponse<HelloResponse>>;
54
+ };
55
+ spaces: {
56
+ list: (listOptions?: ListSpacesOptions) => Promise<VueVoxApiResponse<SpacesListResponse>>;
57
+ paginate: (listOptions?: ListSpacesOptions) => AsyncGenerator<{
58
+ id: string;
59
+ name: string;
60
+ description: string | null;
61
+ createdAt: string;
62
+ updatedAt: string;
63
+ }, any, any>;
64
+ };
65
+ calls: {
66
+ list: (listOptions?: ListCallsOptions) => Promise<VueVoxApiResponse<CallsListResponse>>;
67
+ get: (callId: string) => Promise<VueVoxApiResponse<CallDetailResponse>>;
68
+ paginate: (listOptions?: ListCallsOptions) => AsyncGenerator<{
69
+ id: string;
70
+ space: components["schemas"]["ResourceSummary"];
71
+ lead: components["schemas"]["LeadSummary"];
72
+ agent: components["schemas"]["ResourceSummary"];
73
+ duration: number;
74
+ score: number | null;
75
+ sentiment: string | null;
76
+ analysisStatus: string;
77
+ queueStatus: string | null;
78
+ createdAt: string;
79
+ updatedAt: string;
80
+ }, any, any>;
81
+ };
82
+ leads: {
83
+ list: (listOptions?: ListLeadsOptions) => Promise<VueVoxApiResponse<LeadsListResponse>>;
84
+ get: (leadId: string) => Promise<VueVoxApiResponse<LeadDetailResponse>>;
85
+ paginate: (listOptions?: ListLeadsOptions) => AsyncGenerator<{
86
+ id: string;
87
+ firstName: string;
88
+ lastName: string;
89
+ email: string | null;
90
+ phone: string | null;
91
+ space: components["schemas"]["ResourceSummary"];
92
+ createdAt: string;
93
+ updatedAt: string;
94
+ }, any, any>;
95
+ };
19
96
  raw: import("openapi-fetch").Client<paths, `${string}/${string}`>;
20
97
  };
21
- export {};
98
+ export type { CallDetailResponse, CallsListResponse, CallSummary, HelloResponse, Lead, LeadDetailResponse, LeadsListResponse, Space, SpacesListResponse };
package/dist/client.js CHANGED
@@ -4,12 +4,15 @@ export function createVueVoxClient(options) {
4
4
  const baseUrl = trimTrailingSlash(options.baseUrl ?? "https://api.vuevox.com");
5
5
  const fetchFn = options.fetch ?? fetch;
6
6
  const raw = createClient({ baseUrl, fetch: fetchFn });
7
+ const retries = options.retries ?? 0;
8
+ const retryBaseDelayMs = options.retryBaseDelayMs ?? 250;
9
+ const retryMaxDelayMs = options.retryMaxDelayMs ?? 2_000;
7
10
  let cachedToken = null;
8
11
  async function getAccessToken() {
9
12
  if (cachedToken && Date.now() < cachedToken.expiresAt - 30_000) {
10
13
  return cachedToken.accessToken;
11
14
  }
12
- const response = await fetchFn(`${baseUrl}/oauth/token`, {
15
+ const result = await requestJson("POST", "/oauth/token", {
13
16
  method: "POST",
14
17
  headers: {
15
18
  "Content-Type": "application/json",
@@ -21,51 +24,88 @@ export function createVueVoxClient(options) {
21
24
  scope: formatScope(options.scope),
22
25
  }),
23
26
  });
24
- const body = await parseJson(response);
25
- if (!response.ok) {
26
- const error = isErrorResponse(body) ? body.error : null;
27
- throw new VueVoxApiError(response.status, error?.code ?? "token_request_failed", error?.message ?? "VueVox token request failed.", isErrorResponse(body) ? body : undefined);
28
- }
29
- if (!isTokenResponse(body)) {
30
- throw new VueVoxApiError(response.status, "invalid_token_response", "VueVox returned an invalid token response.");
31
- }
27
+ const body = result.data;
32
28
  cachedToken = {
33
29
  accessToken: body.access_token,
34
30
  expiresAt: Date.now() + body.expires_in * 1000,
35
31
  };
36
32
  return cachedToken.accessToken;
37
33
  }
38
- async function hello() {
34
+ async function getHello() {
35
+ return apiGet("/v1/hello");
36
+ }
37
+ async function listSpaces(listOptions = {}) {
38
+ return apiGet("/v1/spaces", listOptions);
39
+ }
40
+ async function listCalls(listOptions = {}) {
41
+ return apiGet("/v1/calls", listOptions);
42
+ }
43
+ async function getCall(callId) {
44
+ return apiGet(`/v1/calls/${encodeURIComponent(callId)}`);
45
+ }
46
+ async function listLeads(listOptions = {}) {
47
+ return apiGet("/v1/leads", listOptions);
48
+ }
49
+ async function getLead(leadId) {
50
+ return apiGet(`/v1/leads/${encodeURIComponent(leadId)}`);
51
+ }
52
+ async function apiGet(path, query) {
39
53
  const accessToken = await getAccessToken();
40
- const { data, error, response } = await raw.GET("/v1/hello", {
54
+ const result = await requestJson("GET", path, {
55
+ method: "GET",
41
56
  headers: {
42
57
  Authorization: `Bearer ${accessToken}`,
43
58
  },
59
+ query,
44
60
  });
45
- if (error) {
46
- throw new VueVoxApiError(response.status, error.error.code, error.error.message, error);
61
+ return withMetadata(result.data, result.response, result.requestId);
62
+ }
63
+ async function requestJson(method, path, init) {
64
+ for (let attempt = 0;; attempt++) {
65
+ const response = await fetchFn(buildUrl(baseUrl, path, init.query), init);
66
+ const body = await parseJson(response);
67
+ const requestId = getRequestId(response, body);
68
+ const retryAfter = retryAfterSeconds(response);
69
+ notifyResponse(options, method, path, response, requestId, retryAfter);
70
+ if (response.ok) {
71
+ if (body === null || isErrorResponse(body)) {
72
+ throw new VueVoxApiError(response.status, "invalid_response", "VueVox returned an invalid response.", undefined, requestId, retryAfter);
73
+ }
74
+ return { data: body, requestId, response };
75
+ }
76
+ if (attempt < retries && shouldRetry(response.status)) {
77
+ await sleep(retryDelayMs(attempt, retryAfter));
78
+ continue;
79
+ }
80
+ const error = isErrorResponse(body) ? body.error : null;
81
+ throw new VueVoxApiError(response.status, error?.code ?? "api_request_failed", error?.message ?? "VueVox API request failed.", isErrorResponse(body) ? body : undefined, requestId, retryAfter);
47
82
  }
48
- return data;
49
83
  }
50
- async function listSpaces(options = {}) {
51
- const accessToken = await getAccessToken();
52
- const { data, error, response } = await raw.GET("/v1/spaces", {
53
- params: {
54
- query: options,
55
- },
56
- headers: {
57
- Authorization: `Bearer ${accessToken}`,
58
- },
59
- });
60
- if (error) {
61
- throw new VueVoxApiError(response.status, error.error.code, error.error.message, error);
84
+ function retryDelayMs(attempt, retryAfter) {
85
+ if (retryAfter !== undefined) {
86
+ return retryAfter * 1000;
62
87
  }
63
- return data;
88
+ return Math.min(retryBaseDelayMs * 2 ** attempt, retryMaxDelayMs);
64
89
  }
65
90
  return {
66
91
  getAccessToken,
67
- hello,
68
- listSpaces,
92
+ hello: {
93
+ get: getHello,
94
+ },
95
+ spaces: {
96
+ list: listSpaces,
97
+ paginate: (listOptions = {}) => paginate(listSpaces, listOptions),
98
+ },
99
+ calls: {
100
+ list: listCalls,
101
+ get: getCall,
102
+ paginate: (listOptions = {}) => paginate(listCalls, listOptions),
103
+ },
104
+ leads: {
105
+ list: listLeads,
106
+ get: getLead,
107
+ paginate: (listOptions = {}) => paginate(listLeads, listOptions),
108
+ },
69
109
  raw,
70
110
  };
71
111
  }
@@ -78,6 +118,15 @@ function formatScope(scope) {
78
118
  function trimTrailingSlash(value) {
79
119
  return value.replace(/\/+$/, "");
80
120
  }
121
+ function buildUrl(baseUrl, path, query) {
122
+ const url = new URL(`${baseUrl}${path}`);
123
+ for (const [key, value] of Object.entries(query ?? {})) {
124
+ if ((typeof value === "string" || typeof value === "number") && value !== "") {
125
+ url.searchParams.set(key, String(value));
126
+ }
127
+ }
128
+ return url.toString();
129
+ }
81
130
  async function parseJson(response) {
82
131
  try {
83
132
  return (await response.json());
@@ -87,8 +136,61 @@ async function parseJson(response) {
87
136
  }
88
137
  }
89
138
  function isTokenResponse(value) {
90
- return Boolean(value && "access_token" in value);
139
+ return isObject(value) && "access_token" in value;
91
140
  }
92
141
  function isErrorResponse(value) {
93
- return Boolean(value && "error" in value);
142
+ return isObject(value) && "error" in value;
143
+ }
144
+ function getRequestId(response, body) {
145
+ return response.headers.get("X-Request-Id") ?? (isErrorBody(body) ? body.error.requestId : undefined);
146
+ }
147
+ function isErrorBody(value) {
148
+ return isObject(value) && "error" in value;
149
+ }
150
+ function isObject(value) {
151
+ return typeof value === "object" && value !== null;
152
+ }
153
+ function withMetadata(data, response, requestId) {
154
+ return {
155
+ data,
156
+ requestId,
157
+ status: response.status,
158
+ };
159
+ }
160
+ function notifyResponse(options, method, path, response, requestId, retryAfter) {
161
+ options.onResponse?.({
162
+ method,
163
+ path,
164
+ requestId,
165
+ retryAfter,
166
+ status: response.status,
167
+ });
168
+ }
169
+ async function* paginate(list, listOptions) {
170
+ let cursor = listOptions.cursor;
171
+ do {
172
+ const response = await list({ ...listOptions, cursor });
173
+ for (const item of response.data.data) {
174
+ yield item;
175
+ }
176
+ cursor = response.data.pagination.nextCursor ?? undefined;
177
+ } while (cursor);
178
+ }
179
+ function shouldRetry(status) {
180
+ return [429, 500, 502, 503, 504].includes(status);
181
+ }
182
+ function retryAfterSeconds(response) {
183
+ const value = response.headers.get("Retry-After");
184
+ if (!value) {
185
+ return undefined;
186
+ }
187
+ const seconds = Number(value);
188
+ if (Number.isFinite(seconds)) {
189
+ return Math.max(0, seconds);
190
+ }
191
+ const timestamp = Date.parse(value);
192
+ return Number.isNaN(timestamp) ? undefined : Math.max(0, Math.ceil((timestamp - Date.now()) / 1000));
193
+ }
194
+ function sleep(ms) {
195
+ return new Promise((resolve) => setTimeout(resolve, ms));
94
196
  }
package/dist/errors.d.ts CHANGED
@@ -3,6 +3,10 @@ export type VueVoxErrorResponse = components["schemas"]["ErrorResponse"];
3
3
  export declare class VueVoxApiError extends Error {
4
4
  readonly status: number;
5
5
  readonly code: string;
6
+ readonly details?: Record<string, unknown>;
7
+ readonly isRateLimited: boolean;
8
+ readonly requestId?: string;
9
+ readonly retryAfter?: number;
6
10
  readonly response?: VueVoxErrorResponse;
7
- constructor(status: number, code: string, message: string, response?: VueVoxErrorResponse);
11
+ constructor(status: number, code: string, message: string, response?: VueVoxErrorResponse, requestId?: string, retryAfter?: number);
8
12
  }
package/dist/errors.js CHANGED
@@ -1,12 +1,20 @@
1
1
  export class VueVoxApiError extends Error {
2
2
  status;
3
3
  code;
4
+ details;
5
+ isRateLimited;
6
+ requestId;
7
+ retryAfter;
4
8
  response;
5
- constructor(status, code, message, response) {
9
+ constructor(status, code, message, response, requestId, retryAfter) {
6
10
  super(message);
7
11
  this.name = "VueVoxApiError";
8
12
  this.status = status;
9
13
  this.code = code;
14
+ this.details = response?.error.details;
15
+ this.isRateLimited = status === 429 || code === "rate_limited";
16
+ this.requestId = requestId ?? response?.error.requestId;
17
+ this.retryAfter = retryAfter;
10
18
  this.response = response;
11
19
  }
12
20
  }
@@ -64,6 +64,86 @@ export interface paths {
64
64
  patch?: never;
65
65
  trace?: never;
66
66
  };
67
+ "/v1/calls": {
68
+ parameters: {
69
+ query?: never;
70
+ header?: never;
71
+ path?: never;
72
+ cookie?: never;
73
+ };
74
+ /**
75
+ * List organization calls
76
+ * @description Returns calls for the API client's organization using cursor pagination. List responses do not expose audio storage paths or transcripts.
77
+ */
78
+ get: operations["listCalls"];
79
+ put?: never;
80
+ post?: never;
81
+ delete?: never;
82
+ options?: never;
83
+ head?: never;
84
+ patch?: never;
85
+ trace?: never;
86
+ };
87
+ "/v1/calls/{callId}": {
88
+ parameters: {
89
+ query?: never;
90
+ header?: never;
91
+ path?: never;
92
+ cookie?: never;
93
+ };
94
+ /**
95
+ * Get a call
96
+ * @description Returns a single organization-scoped call with transcript and analysis details.
97
+ */
98
+ get: operations["getCall"];
99
+ put?: never;
100
+ post?: never;
101
+ delete?: never;
102
+ options?: never;
103
+ head?: never;
104
+ patch?: never;
105
+ trace?: never;
106
+ };
107
+ "/v1/leads": {
108
+ parameters: {
109
+ query?: never;
110
+ header?: never;
111
+ path?: never;
112
+ cookie?: never;
113
+ };
114
+ /**
115
+ * List organization leads
116
+ * @description Returns leads for the API client's organization using cursor pagination. The leads:read scope includes email and phone contact details.
117
+ */
118
+ get: operations["listLeads"];
119
+ put?: never;
120
+ post?: never;
121
+ delete?: never;
122
+ options?: never;
123
+ head?: never;
124
+ patch?: never;
125
+ trace?: never;
126
+ };
127
+ "/v1/leads/{leadId}": {
128
+ parameters: {
129
+ query?: never;
130
+ header?: never;
131
+ path?: never;
132
+ cookie?: never;
133
+ };
134
+ /**
135
+ * Get a lead
136
+ * @description Returns a single organization-scoped lead with contact details.
137
+ */
138
+ get: operations["getLead"];
139
+ put?: never;
140
+ post?: never;
141
+ delete?: never;
142
+ options?: never;
143
+ head?: never;
144
+ patch?: never;
145
+ trace?: never;
146
+ };
67
147
  }
68
148
  export type webhooks = Record<string, never>;
69
149
  export interface components {
@@ -75,7 +155,7 @@ export interface components {
75
155
  client_secret: string;
76
156
  /**
77
157
  * @description Space-separated scopes requested for the access token.
78
- * @example hello:read
158
+ * @example hello:read spaces:read calls:read leads:read
79
159
  */
80
160
  scope?: string;
81
161
  };
@@ -85,7 +165,7 @@ export interface components {
85
165
  token_type: "Bearer";
86
166
  /** @example 3600 */
87
167
  expires_in: number;
88
- /** @example hello:read */
168
+ /** @example hello:read spaces:read calls:read leads:read */
89
169
  scope: string;
90
170
  };
91
171
  HelloResponse: {
@@ -108,6 +188,81 @@ export interface components {
108
188
  data: components["schemas"]["Space"][];
109
189
  pagination: components["schemas"]["CursorPagination"];
110
190
  };
191
+ ResourceSummary: {
192
+ /** Format: uuid */
193
+ id: string;
194
+ name: string;
195
+ } | null;
196
+ LeadSummary: {
197
+ /** Format: uuid */
198
+ id: string;
199
+ firstName: string;
200
+ lastName: string;
201
+ } | null;
202
+ CallSummary: {
203
+ /** Format: uuid */
204
+ id: string;
205
+ space: components["schemas"]["ResourceSummary"];
206
+ lead: components["schemas"]["LeadSummary"];
207
+ agent: components["schemas"]["ResourceSummary"];
208
+ /** @description Call duration in seconds. */
209
+ duration: number;
210
+ score: number | null;
211
+ sentiment: string | null;
212
+ /** @example completed */
213
+ analysisStatus: string;
214
+ /** @example processing */
215
+ queueStatus: string | null;
216
+ /** Format: date-time */
217
+ createdAt: string;
218
+ /** Format: date-time */
219
+ updatedAt: string;
220
+ };
221
+ CallDetail: components["schemas"]["CallSummary"] & {
222
+ transcript: {
223
+ [key: string]: unknown;
224
+ }[] | null;
225
+ analysis: components["schemas"]["CallAnalysis"];
226
+ };
227
+ CallAnalysis: {
228
+ summary: string | null;
229
+ totalScore: number;
230
+ totalMax: number;
231
+ sentiment: string;
232
+ objectiveMet: boolean;
233
+ objectiveNotes: string | null;
234
+ detectedTags: string[] | null;
235
+ strengths: string[] | null;
236
+ weaknesses: string[] | null;
237
+ } | null;
238
+ CallsListResponse: {
239
+ data: components["schemas"]["CallSummary"][];
240
+ pagination: components["schemas"]["CursorPagination"];
241
+ };
242
+ CallDetailResponse: {
243
+ data: components["schemas"]["CallDetail"];
244
+ };
245
+ Lead: {
246
+ /** Format: uuid */
247
+ id: string;
248
+ firstName: string;
249
+ lastName: string;
250
+ /** Format: email */
251
+ email: string | null;
252
+ phone: string | null;
253
+ space: components["schemas"]["ResourceSummary"];
254
+ /** Format: date-time */
255
+ createdAt: string;
256
+ /** Format: date-time */
257
+ updatedAt: string;
258
+ };
259
+ LeadsListResponse: {
260
+ data: components["schemas"]["Lead"][];
261
+ pagination: components["schemas"]["CursorPagination"];
262
+ };
263
+ LeadDetailResponse: {
264
+ data: components["schemas"]["Lead"];
265
+ };
111
266
  CursorPagination: {
112
267
  /** @example 50 */
113
268
  limit: number;
@@ -306,4 +461,276 @@ export interface operations {
306
461
  };
307
462
  };
308
463
  };
464
+ listCalls: {
465
+ parameters: {
466
+ query?: {
467
+ /** @description Number of calls to return. Defaults to 50. Maximum is 100. */
468
+ limit?: number;
469
+ /** @description Opaque cursor from the previous response's pagination.nextCursor value. */
470
+ cursor?: string;
471
+ /** @description Filter calls to a space ID. */
472
+ spaceId?: string;
473
+ /** @description Filter calls to a lead ID. */
474
+ leadId?: string;
475
+ /** @description Filter calls to an agent ID. */
476
+ agentId?: string;
477
+ /** @description Return calls created at or after this timestamp. */
478
+ createdAfter?: string;
479
+ /** @description Return calls created at or before this timestamp. */
480
+ createdBefore?: string;
481
+ };
482
+ header?: never;
483
+ path?: never;
484
+ cookie?: never;
485
+ };
486
+ requestBody?: never;
487
+ responses: {
488
+ /** @description Paginated calls response. */
489
+ 200: {
490
+ headers: {
491
+ [name: string]: unknown;
492
+ };
493
+ content: {
494
+ "application/json": components["schemas"]["CallsListResponse"];
495
+ };
496
+ };
497
+ /** @description Bearer token is missing, invalid, or expired. */
498
+ 401: {
499
+ headers: {
500
+ [name: string]: unknown;
501
+ };
502
+ content: {
503
+ "application/json": components["schemas"]["ErrorResponse"];
504
+ };
505
+ };
506
+ /** @description Bearer token does not include the required scope. */
507
+ 403: {
508
+ headers: {
509
+ [name: string]: unknown;
510
+ };
511
+ content: {
512
+ "application/json": components["schemas"]["ErrorResponse"];
513
+ };
514
+ };
515
+ /** @description Query parameters failed validation. */
516
+ 422: {
517
+ headers: {
518
+ [name: string]: unknown;
519
+ };
520
+ content: {
521
+ "application/json": components["schemas"]["ErrorResponse"];
522
+ };
523
+ };
524
+ /** @description Per-client rate limit exceeded. */
525
+ 429: {
526
+ headers: {
527
+ /** @description Seconds to wait before retrying. */
528
+ "Retry-After"?: string;
529
+ /** @description Request limit per minute for this API client. */
530
+ "X-RateLimit-Limit"?: string;
531
+ [name: string]: unknown;
532
+ };
533
+ content: {
534
+ "application/json": components["schemas"]["ErrorResponse"];
535
+ };
536
+ };
537
+ };
538
+ };
539
+ getCall: {
540
+ parameters: {
541
+ query?: never;
542
+ header?: never;
543
+ path: {
544
+ /** @description Call ID. */
545
+ callId: string;
546
+ };
547
+ cookie?: never;
548
+ };
549
+ requestBody?: never;
550
+ responses: {
551
+ /** @description Call detail response. */
552
+ 200: {
553
+ headers: {
554
+ [name: string]: unknown;
555
+ };
556
+ content: {
557
+ "application/json": components["schemas"]["CallDetailResponse"];
558
+ };
559
+ };
560
+ /** @description Bearer token is missing, invalid, or expired. */
561
+ 401: {
562
+ headers: {
563
+ [name: string]: unknown;
564
+ };
565
+ content: {
566
+ "application/json": components["schemas"]["ErrorResponse"];
567
+ };
568
+ };
569
+ /** @description Bearer token does not include the required scope. */
570
+ 403: {
571
+ headers: {
572
+ [name: string]: unknown;
573
+ };
574
+ content: {
575
+ "application/json": components["schemas"]["ErrorResponse"];
576
+ };
577
+ };
578
+ /** @description Call was not found in the API client's organization. */
579
+ 404: {
580
+ headers: {
581
+ [name: string]: unknown;
582
+ };
583
+ content: {
584
+ "application/json": components["schemas"]["ErrorResponse"];
585
+ };
586
+ };
587
+ /** @description Per-client rate limit exceeded. */
588
+ 429: {
589
+ headers: {
590
+ /** @description Seconds to wait before retrying. */
591
+ "Retry-After"?: string;
592
+ /** @description Request limit per minute for this API client. */
593
+ "X-RateLimit-Limit"?: string;
594
+ [name: string]: unknown;
595
+ };
596
+ content: {
597
+ "application/json": components["schemas"]["ErrorResponse"];
598
+ };
599
+ };
600
+ };
601
+ };
602
+ listLeads: {
603
+ parameters: {
604
+ query?: {
605
+ /** @description Number of leads to return. Defaults to 50. Maximum is 100. */
606
+ limit?: number;
607
+ /** @description Opaque cursor from the previous response's pagination.nextCursor value. */
608
+ cursor?: string;
609
+ /** @description Filter leads to a space ID. */
610
+ spaceId?: string;
611
+ /** @description Return leads created at or after this timestamp. */
612
+ createdAfter?: string;
613
+ /** @description Return leads created at or before this timestamp. */
614
+ createdBefore?: string;
615
+ };
616
+ header?: never;
617
+ path?: never;
618
+ cookie?: never;
619
+ };
620
+ requestBody?: never;
621
+ responses: {
622
+ /** @description Paginated leads response. */
623
+ 200: {
624
+ headers: {
625
+ [name: string]: unknown;
626
+ };
627
+ content: {
628
+ "application/json": components["schemas"]["LeadsListResponse"];
629
+ };
630
+ };
631
+ /** @description Bearer token is missing, invalid, or expired. */
632
+ 401: {
633
+ headers: {
634
+ [name: string]: unknown;
635
+ };
636
+ content: {
637
+ "application/json": components["schemas"]["ErrorResponse"];
638
+ };
639
+ };
640
+ /** @description Bearer token does not include the required scope. */
641
+ 403: {
642
+ headers: {
643
+ [name: string]: unknown;
644
+ };
645
+ content: {
646
+ "application/json": components["schemas"]["ErrorResponse"];
647
+ };
648
+ };
649
+ /** @description Query parameters failed validation. */
650
+ 422: {
651
+ headers: {
652
+ [name: string]: unknown;
653
+ };
654
+ content: {
655
+ "application/json": components["schemas"]["ErrorResponse"];
656
+ };
657
+ };
658
+ /** @description Per-client rate limit exceeded. */
659
+ 429: {
660
+ headers: {
661
+ /** @description Seconds to wait before retrying. */
662
+ "Retry-After"?: string;
663
+ /** @description Request limit per minute for this API client. */
664
+ "X-RateLimit-Limit"?: string;
665
+ [name: string]: unknown;
666
+ };
667
+ content: {
668
+ "application/json": components["schemas"]["ErrorResponse"];
669
+ };
670
+ };
671
+ };
672
+ };
673
+ getLead: {
674
+ parameters: {
675
+ query?: never;
676
+ header?: never;
677
+ path: {
678
+ /** @description Lead ID. */
679
+ leadId: string;
680
+ };
681
+ cookie?: never;
682
+ };
683
+ requestBody?: never;
684
+ responses: {
685
+ /** @description Lead detail response. */
686
+ 200: {
687
+ headers: {
688
+ [name: string]: unknown;
689
+ };
690
+ content: {
691
+ "application/json": components["schemas"]["LeadDetailResponse"];
692
+ };
693
+ };
694
+ /** @description Bearer token is missing, invalid, or expired. */
695
+ 401: {
696
+ headers: {
697
+ [name: string]: unknown;
698
+ };
699
+ content: {
700
+ "application/json": components["schemas"]["ErrorResponse"];
701
+ };
702
+ };
703
+ /** @description Bearer token does not include the required scope. */
704
+ 403: {
705
+ headers: {
706
+ [name: string]: unknown;
707
+ };
708
+ content: {
709
+ "application/json": components["schemas"]["ErrorResponse"];
710
+ };
711
+ };
712
+ /** @description Lead was not found in the API client's organization. */
713
+ 404: {
714
+ headers: {
715
+ [name: string]: unknown;
716
+ };
717
+ content: {
718
+ "application/json": components["schemas"]["ErrorResponse"];
719
+ };
720
+ };
721
+ /** @description Per-client rate limit exceeded. */
722
+ 429: {
723
+ headers: {
724
+ /** @description Seconds to wait before retrying. */
725
+ "Retry-After"?: string;
726
+ /** @description Request limit per minute for this API client. */
727
+ "X-RateLimit-Limit"?: string;
728
+ [name: string]: unknown;
729
+ };
730
+ content: {
731
+ "application/json": components["schemas"]["ErrorResponse"];
732
+ };
733
+ };
734
+ };
735
+ };
309
736
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { createVueVoxClient } from "./client.js";
2
- export type { ListSpacesOptions, VueVoxClientOptions } from "./client.js";
2
+ export type { CallDetailResponse, CallsListResponse, CallSummary, HelloResponse, Lead, LeadDetailResponse, LeadsListResponse, ListCallsOptions, ListLeadsOptions, ListSpacesOptions, Space, SpacesListResponse, VueVoxApiResponse, VueVoxClientOptions, VueVoxResponseEvent, VueVoxResponseMetadata, } from "./client.js";
3
3
  export { VueVoxApiError } from "./errors.js";
4
4
  export type { VueVoxErrorResponse } from "./errors.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vuevox/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "TypeScript SDK for the VueVox Developer API.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",