@vuevox/sdk
v0.9.1
Published
TypeScript SDK for the VueVox Developer API.
Downloads
167
Readme
@vuevox/sdk
TypeScript SDK for the VueVox Developer API.
Install
npm install @vuevox/sdkRequirements:
- Node.js 18 or newer.
- A VueVox Developer API client ID and secret.
- Server-side usage only. Do not expose
clientSecretin browser code.
Create API Credentials
In VueVox, open:
Settings -> Developer API -> Manage API ClientsCreate a client, select the scopes it can request, and copy the client_secret immediately. VueVox shows each client secret only once.
Available scopes:
hello:read
spaces:read
agents:read
calls:read
calls:write
leads:read
leads:write
lead_custom_fields:manage
webhooks:read
webhooks:writeThe token request can only request scopes that were granted to that API client.
Quickstart
import { createVueVoxClient } from "@vuevox/sdk";
const vuevox = createVueVoxClient({
clientId: process.env.VUEVOX_CLIENT_ID!,
clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
scope: ["hello:read", "spaces:read", "agents:read", "calls:read", "leads:read"],
});
const hello = await vuevox.hello.get();
console.log(hello.data.message, hello.requestId);
const calls = await vuevox.calls.list({ limit: 50 });
console.log(calls.data.data, calls.requestId);The SDK requests and caches a short-lived access token using client credentials, then sends it as a bearer token for API calls.
Client Reference
createVueVoxClient(options)
Creates a VueVox API client.
const vuevox = createVueVoxClient({
baseUrl: "https://api.vuevox.com",
clientId: process.env.VUEVOX_CLIENT_ID!,
clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
scope: ["calls:read"],
retries: 2,
retryBaseDelayMs: 250,
retryMaxDelayMs: 2000,
onResponse: ({ method, path, status, requestId }) => {
console.log({ method, path, status, requestId });
},
});Options:
| Option | Type | Required | Description |
| --- | --- | --- | --- |
| clientId | string | Yes | Developer API client ID. |
| clientSecret | string | Yes | Developer API client secret. Keep this server-side. |
| baseUrl | string | No | API base URL. Defaults to https://api.vuevox.com. |
| scope | string \| string[] | No | Space-separated string or array of requested token scopes. If omitted, the token request uses all scopes granted to the client. |
| fetch | typeof fetch | No | Custom fetch implementation. Defaults to global fetch. |
| onResponse | (event: VueVoxResponseEvent) => void | No | Called for every SDK-managed HTTP response, including token requests. |
| retries | number | No | Retry count for token requests and GET endpoints. Defaults to 0. |
| retryBaseDelayMs | number | No | Initial retry delay. Defaults to 250. |
| retryMaxDelayMs | number | No | Maximum retry delay. Defaults to 2000. |
Returns a namespaced client:
vuevox.getAccessToken();
vuevox.hello.get();
vuevox.spaces.list();
vuevox.spaces.paginate();
vuevox.agents.list();
vuevox.agents.paginate();
vuevox.calls.list();
vuevox.calls.get("call-id");
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" } });
vuevox.calls.queueAnalysis("call-id");
vuevox.calls.waitForAnalysis("call-id");
vuevox.calls.paginate();
vuevox.leads.list();
vuevox.leads.get("lead-id");
vuevox.leads.update("lead-id", { customFields: { crm_stage: "qualified" } });
vuevox.leads.upsertByExternalId("crm-lead-id", { firstName: "Ada", lastName: "Lovelace" });
vuevox.leads.paginate();
vuevox.leadCustomFields.list();
vuevox.leadCustomFields.create({ key: "crm_stage", label: "CRM Stage", type: "select", options: ["new", "qualified"] });
vuevox.leadCustomFields.update("crm_stage", { label: "CRM Stage" });
vuevox.webhooks.endpoints.list();
vuevox.webhooks.endpoints.create({ url: "https://example.com/vuevox/webhooks", events: ["analysis.completed", "analysis.failed"] });
vuevox.webhooks.endpoints.update("endpoint-id", { isActive: false });
vuevox.webhooks.endpoints.rotateSecret("endpoint-id");
vuevox.webhooks.endpoints.test("endpoint-id");
vuevox.webhooks.endpoints.delete("endpoint-id");
vuevox.raw.GET("/v1/hello", { headers: { Authorization: `Bearer ${await vuevox.getAccessToken()}` } });Response Envelope
All SDK endpoint methods return a response envelope:
type VueVoxApiResponse<T> = {
data: T;
status: number;
requestId?: string;
};Use requestId in logs and support requests. It maps to the API X-Request-Id response header.
Pagination
List endpoints use cursor pagination.
const firstPage = await vuevox.calls.list({ limit: 50 });
if (firstPage.data.pagination.nextCursor) {
const secondPage = await vuevox.calls.list({
limit: 50,
cursor: firstPage.data.pagination.nextCursor,
});
}List option fields shared by paginated endpoints:
| Option | Type | Description |
| --- | --- | --- |
| limit | number | Number of items to return. Defaults to 50; maximum is 100. |
| cursor | string | Cursor from the previous response's pagination.nextCursor. |
The SDK also includes async iterable helpers:
for await (const call of vuevox.calls.paginate({ limit: 50 })) {
console.log(call.id);
}Pagination helpers are available for spaces, agents, calls, leads, and webhooks.endpoints.
Methods
vuevox.getAccessToken()
Requests or returns a cached bearer token.
const token = await vuevox.getAccessToken();Returns: Promise<string>.
vuevox.hello.get()
Checks credentials and connectivity.
Required scope: hello:read.
const response = await vuevox.hello.get();
console.log(response.data.message);Returns: Promise<VueVoxApiResponse<HelloResponse>>.
vuevox.spaces.list(options?)
Lists organization spaces.
Required scope: spaces:read.
Options: ListSpacesOptions
const response = await vuevox.spaces.list({ limit: 50 });
for (const space of response.data.data) {
console.log(space.id, space.name);
}Returns: Promise<VueVoxApiResponse<SpacesListResponse>>.
vuevox.spaces.paginate(options?)
Iterates organization spaces across all pages.
Required scope: spaces:read.
Options: ListSpacesOptions
for await (const space of vuevox.spaces.paginate({ limit: 100 })) {
console.log(space.id, space.name);
}Returns: AsyncGenerator<Space>.
vuevox.agents.list(options?)
Lists organization agents.
Agent records include nullable externalId when you store an ID from another system.
Required scope: agents:read.
Options: ListAgentsOptions
| Option | Type | Description |
| --- | --- | --- |
| limit | number | Number of agents to return. Defaults to 50; maximum is 100. |
| cursor | string | Cursor from the previous response. |
| spaceId | string | Optional space ID filter. |
const response = await vuevox.agents.list({ limit: 50, spaceId: "space-id" });
for (const agent of response.data.data) {
console.log(agent.id, agent.externalId, agent.name);
}Returns: Promise<VueVoxApiResponse<AgentsListResponse>>.
vuevox.agents.paginate(options?)
Iterates organization agents across all pages.
Required scope: agents:read.
Options: ListAgentsOptions
for await (const agent of vuevox.agents.paginate({ spaceId: "space-id" })) {
console.log(agent.id, agent.externalId, agent.name);
}Returns: AsyncGenerator<Agent>.
vuevox.calls.list(options?)
Lists organization calls. List responses do not include transcripts.
Required scope: calls:read.
Options: ListCallsOptions
| Option | Type | Description |
| --- | --- | --- |
| limit | number | Number of calls to return. Defaults to 50; maximum is 100. |
| cursor | string | Cursor from the previous response. |
| spaceId | string | Optional space ID filter. |
| leadId | string | Optional lead ID filter. |
| agentId | string | Optional agent ID filter. |
| createdAfter | string | Optional ISO 8601 lower bound for call creation time. |
| createdBefore | string | Optional ISO 8601 upper bound for call creation time. |
| leadCustomFields | Record<string, CustomFieldFilterValue> | Optional filters on lead custom fields attached to calls. |
const response = await vuevox.calls.list({
limit: 50,
spaceId: "space-id",
createdAfter: "2026-01-01T00:00:00.000Z",
leadCustomFields: {
crm_stage: "qualified",
},
});
for (const call of response.data.data) {
console.log(call.id, call.status, call.createdAt);
}Returns: Promise<VueVoxApiResponse<CallsListResponse>>.
vuevox.calls.get(callId)
Gets a call detail record, including transcript data when available.
Required scope: calls:read.
const response = await vuevox.calls.get("call-id");
console.log(response.data.data.transcript);Returns: Promise<VueVoxApiResponse<CallDetailResponse>>.
vuevox.calls.upload(input)
Uploads an audio recording, upserts the agent and lead by externalId, creates the call in an existing platform-managed space, and optionally queues analysis.
Required scope: calls:write.
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.
Spaces are managed in VueVox. Use vuevox.spaces.list() to obtain the spaceId.
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.
Options: UploadCallInput
| Option | Type | Description |
| --- | --- | --- |
| idempotencyKey | string | Required unique key for safe retries. |
| spaceId | string | Required existing VueVox space ID. |
| externalId | string \| null | Optional call ID from your system. |
| queueAnalysis | boolean | Optional. Defaults to true; set false to upload now and queue later. |
| agent.externalId | string | Required agent ID from your system. |
| agent.name | string | Required when creating a new agent. |
| agent.email | string \| null | Optional agent email. |
| agent.phone | string \| null | Optional agent phone. |
| lead.externalId | string | Required lead/prospect ID from your system. |
| lead.firstName | string | Required when creating a new lead. |
| lead.lastName | string | Required when creating a new lead. |
| lead.email | string \| null | Optional lead email. |
| lead.phone | string \| null | Optional lead phone. |
| lead.customFields | Record<string, unknown> | Optional organization-defined lead custom fields. |
| audio.file | Blob \| ArrayBuffer \| Uint8Array | Required audio file data. In Node, a Buffer from readFile() can be passed directly. |
| audio.filename | string | Optional filename sent in multipart upload. |
| audio.contentType | string | Optional MIME type, for example audio/mpeg. |
import { readFile } from "node:fs/promises";
const buffer = await readFile("./call.mp3");
const response = await vuevox.calls.upload({
idempotencyKey: "crm-call-789",
externalId: "crm-call-789",
spaceId: "space-id",
queueAnalysis: true,
agent: {
externalId: "agent-123",
name: "Morgan Agent",
},
lead: {
externalId: "prospect-456",
firstName: "Ada",
lastName: "Lovelace",
email: "[email protected]",
customFields: {
crm_stage: "qualified",
},
},
audio: {
file: buffer,
filename: "call.mp3",
contentType: "audio/mpeg",
},
});
console.log(response.data.data.id, response.data.data.analysisStatus, response.requestId);Returns: Promise<VueVoxApiResponse<CallResponse>>.
vuevox.calls.queueAnalysis(callId)
Queues analysis for a call that was uploaded with queueAnalysis: false.
Required scope: calls:write.
const response = await vuevox.calls.queueAnalysis("call-id");
console.log(response.data.data.analysisStatus, response.data.data.queueStatus);Returns: Promise<VueVoxApiResponse<CallResponse>>.
vuevox.calls.waitForAnalysis(callId, options?)
Polls vuevox.calls.get(callId) until the call analysis status is completed or failed.
Required scope: calls:read.
Options: WaitForAnalysisOptions
| Option | Type | Description |
| --- | --- | --- |
| intervalMs | number | Poll interval. Defaults to 2000. |
| timeoutMs | number | Timeout before throwing analysis_timeout. Defaults to 120000. |
const response = await vuevox.calls.waitForAnalysis("call-id", { timeoutMs: 180000 });
console.log(response.data.data.analysis);Returns: Promise<VueVoxApiResponse<CallDetailResponse>>.
vuevox.calls.paginate(options?)
Iterates organization calls across all pages.
Required scope: calls:read.
Options: ListCallsOptions
for await (const call of vuevox.calls.paginate({ leadId: "lead-id" })) {
console.log(call.id);
}Returns: AsyncGenerator<CallSummary>.
vuevox.leads.list(options?)
Lists organization leads. The leads:read scope includes lead email and phone contact details.
Lead records include nullable externalId when you store an ID from another system, and a customFields object for organization-defined CRM fields.
Required scope: leads:read.
Options: ListLeadsOptions
| Option | Type | Description |
| --- | --- | --- |
| limit | number | Number of leads to return. Defaults to 50; maximum is 100. |
| cursor | string | Cursor from the previous response. |
| spaceId | string | Optional space ID filter. |
| createdAfter | string | Optional ISO 8601 lower bound for lead creation time. |
| createdBefore | string | Optional ISO 8601 upper bound for lead creation time. |
| customFields | Record<string, CustomFieldFilterValue> | Optional filters on lead custom fields. |
const response = await vuevox.leads.list({
limit: 50,
spaceId: "space-id",
customFields: {
crm_stage: "qualified",
deal_value: { gte: 10000 },
},
});
for (const lead of response.data.data) {
console.log(lead.id, lead.externalId, lead.email, lead.phone, lead.customFields);
}Returns: Promise<VueVoxApiResponse<LeadsListResponse>>.
vuevox.leads.get(leadId)
Gets a lead detail record, including email and phone contact details.
Required scope: leads:read.
const response = await vuevox.leads.get("lead-id");
console.log(response.data.data.externalId, response.data.data.email, response.data.data.phone, response.data.data.customFields);Returns: Promise<VueVoxApiResponse<LeadDetailResponse>>.
vuevox.leads.update(leadId, input)
Updates a lead and/or its custom fields.
Required scope: leads:write.
Input: LeadUpdateRequest
const response = await vuevox.leads.update("lead-id", {
email: "[email protected]",
customFields: {
crm_stage: "qualified",
renewal_date: "2026-09-01",
},
});
console.log(response.data.data.customFields);Returns: Promise<VueVoxApiResponse<LeadDetailResponse>>.
vuevox.leads.upsertByExternalId(externalId, input)
Creates or updates a lead by the integrating CRM/system ID. This is the preferred write method for CRM integrations because callers can use their own stable lead ID.
Required scope: leads:write.
Input: LeadUpsertRequest
const response = await vuevox.leads.upsertByExternalId("hubspot_123", {
firstName: "Ada",
lastName: "Lovelace",
email: "[email protected]",
customFields: {
crm_stage: "qualified",
deal_value: 12000,
},
});
console.log(response.data.data.id, response.data.data.customFields);Returns: Promise<VueVoxApiResponse<LeadDetailResponse>>.
vuevox.leads.paginate(options?)
Iterates organization leads across all pages.
Required scope: leads:read.
Options: ListLeadsOptions
for await (const lead of vuevox.leads.paginate({ createdAfter: "2026-01-01T00:00:00.000Z" })) {
console.log(lead.id, lead.externalId);
}Returns: AsyncGenerator<Lead>.
vuevox.leadCustomFields.list(options?)
Lists organization lead custom field definitions.
Required scope: leads:read.
Options: ListLeadCustomFieldsOptions
| Option | Type | Description |
| --- | --- | --- |
| includeArchived | boolean | Include archived definitions. Defaults to false. |
const response = await vuevox.leadCustomFields.list();
console.log(response.data.data);Returns: Promise<VueVoxApiResponse<LeadCustomFieldsListResponse>>.
vuevox.leadCustomFields.create(input)
Creates a lead custom field definition. Supported types are text, number, boolean, date, datetime, select, and multi_select.
Required scope: lead_custom_fields:manage.
Input: LeadCustomFieldCreateRequest
const response = await vuevox.leadCustomFields.create({
key: "crm_stage",
label: "CRM Stage",
type: "select",
options: ["new", "qualified", "customer"],
isFilterable: true,
});
console.log(response.data.data.key);Returns: Promise<VueVoxApiResponse<LeadCustomFieldResponse>>.
vuevox.leadCustomFields.update(key, input)
Updates mutable metadata for a lead custom field definition. Field keys and types are immutable.
Required scope: lead_custom_fields:manage.
Input: LeadCustomFieldUpdateRequest
await vuevox.leadCustomFields.update("crm_stage", {
label: "CRM Pipeline Stage",
archived: false,
});Returns: Promise<VueVoxApiResponse<LeadCustomFieldResponse>>.
vuevox.webhooks.endpoints.list(options?)
Lists webhook endpoints for the authenticated API client.
Required scope: webhooks:read.
Options: ListWebhookEndpointsOptions
const response = await vuevox.webhooks.endpoints.list({ limit: 50 });
console.log(response.data.data);Returns: Promise<VueVoxApiResponse<WebhookEndpointsListResponse>>.
vuevox.webhooks.endpoints.create(input)
Creates a webhook endpoint. The signing secret is returned only once in this response.
Required scope: webhooks:write.
Input: WebhookEndpointCreateRequest
const response = await vuevox.webhooks.endpoints.create({
url: "https://example.com/vuevox/webhooks",
events: ["analysis.completed", "analysis.failed"],
});
console.log(response.data.data.id, response.data.data.secret);Returns: Promise<VueVoxApiResponse<WebhookEndpointSecretResponse>>.
vuevox.webhooks.endpoints.update(endpointId, input)
Updates a webhook endpoint URL, event subscriptions, or active state.
Required scope: webhooks:write.
Input: WebhookEndpointUpdateRequest
const response = await vuevox.webhooks.endpoints.update("endpoint-id", {
events: ["analysis.completed"],
isActive: true,
});Returns: Promise<VueVoxApiResponse<WebhookEndpointResponse>>.
vuevox.webhooks.endpoints.delete(endpointId)
Deletes a webhook endpoint.
Required scope: webhooks:write.
await vuevox.webhooks.endpoints.delete("endpoint-id");Returns: Promise<VueVoxApiResponse<null>>.
vuevox.webhooks.endpoints.rotateSecret(endpointId)
Rotates the endpoint signing secret. The new secret is returned only once.
Required scope: webhooks:write.
const response = await vuevox.webhooks.endpoints.rotateSecret("endpoint-id");
console.log(response.data.data.secret);Returns: Promise<VueVoxApiResponse<WebhookEndpointSecretResponse>>.
vuevox.webhooks.endpoints.test(endpointId)
Queues a webhook.test delivery to validate endpoint reachability and signature verification.
Required scope: webhooks:write.
const response = await vuevox.webhooks.endpoints.test("endpoint-id");
console.log(response.data.data.eventType, response.data.data.status);Returns: Promise<VueVoxApiResponse<WebhookTestResponse>>.
vuevox.webhooks.endpoints.paginate(options?)
Iterates webhook endpoints across all pages.
Required scope: webhooks:read.
for await (const endpoint of vuevox.webhooks.endpoints.paginate({ limit: 50 })) {
console.log(endpoint.id, endpoint.events);
}Returns: AsyncGenerator<WebhookEndpoint>.
verifyVueVoxWebhookSignature(input)
Verifies a webhook HMAC signature. Pass the raw request body string exactly as received.
import { verifyVueVoxWebhookSignature } from "@vuevox/sdk";
const valid = await verifyVueVoxWebhookSignature({
body: rawBody,
secret: process.env.VUEVOX_WEBHOOK_SECRET!,
signature: request.headers.get("VueVox-Signature") ?? "",
timestamp: request.headers.get("VueVox-Timestamp") ?? "",
});Returns: Promise<boolean>.
Lower-Level Calls
For advanced integrations, raw exposes a typed lower-level OpenAPI client. You must attach authorization yourself.
const { data, error } = await vuevox.raw.GET("/v1/hello", {
headers: {
Authorization: `Bearer ${await vuevox.getAccessToken()}`,
},
});Errors
API errors throw VueVoxApiError.
import { VueVoxApiError, createVueVoxClient } from "@vuevox/sdk";
try {
await vuevox.calls.list({ limit: 50 });
} catch (error) {
if (error instanceof VueVoxApiError) {
console.error(error.status, error.code, error.message, error.requestId);
}
throw error;
}Error fields:
| Field | Type | Description |
| --- | --- | --- |
| status | number | HTTP status code. |
| code | string | Stable API error code. |
| message | string | Human-readable message. |
| requestId | string \| undefined | Request ID from X-Request-Id or error body. |
| details | Record<string, unknown> \| undefined | Structured error details when present. |
| retryAfter | number \| undefined | Seconds to wait before retrying when present. |
| isRateLimited | boolean | true for rate limit errors. |
| response | VueVoxErrorResponse \| undefined | Full API error response when available. |
Common API error codes:
missing_token
invalid_token
invalid_client
invalid_scope
insufficient_scope
rate_limited
invalid_request
internal_error
call_not_found
call_external_id_conflict
analysis_already_queued
analysis_timeout
ai_config_missing
evaluation_grid_missing
audio_not_found
idempotency_key_conflict
idempotency_key_in_progress
lead_not_found
custom_field_not_found
custom_field_conflictRetries
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.
const vuevox = createVueVoxClient({
clientId: process.env.VUEVOX_CLIENT_ID!,
clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
scope: ["calls:read"],
retries: 2,
retryBaseDelayMs: 250,
retryMaxDelayMs: 2000,
});Token Behavior
The SDK:
- Requests tokens using client credentials.
- Caches the access token in memory.
- Refreshes the token before expiry.
- Never stores credentials or tokens on disk.
Base URL
The SDK defaults to:
https://api.vuevox.comIf VueVox support gives you a custom API base URL, pass it explicitly:
const vuevox = createVueVoxClient({
baseUrl: "https://api.vuevox.com",
clientId: process.env.VUEVOX_CLIENT_ID!,
clientSecret: process.env.VUEVOX_CLIENT_SECRET!,
scope: ["calls:read"],
});Exported Types
The package exports these TypeScript types:
import 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,
VueVoxErrorResponse,
VueVoxResponseEvent,
VueVoxResponseMetadata,
WaitForAnalysisOptions,
WebhookEndpoint,
WebhookEndpointCreateRequest,
WebhookEndpointResponse,
WebhookEndpointSecretResponse,
WebhookEndpointUpdateRequest,
WebhookEndpointsListResponse,
WebhookEventPayload,
WebhookTestResponse,
} from "@vuevox/sdk";Generated OpenAPI-derived types are included in the package declaration files, so editors can inspect the exact response fields for each method.
