Ana içeriğe geç

Node.js / TypeScript SDK (bessai)

The official Node.js SDK for the BESS AI Voice Platform. Fully typed, async-only (every method returns a Promise), built on native fetch, with automatic retry and exponential backoff for rate limits and transient server errors. This page documents every public method on the client, its TypeScript signature, and the REST endpoint it maps to.

  • Install: npm install bessai
  • Requirements: Node.js 18+ (native fetch); TypeScript 5.0+ optional
  • Base URL (production): https://api.bess-ai.com
  • Module formats: ESM and CommonJS (dual build)
  • Support: contact@bess-ai.com

1. Install and authenticate

npm install bessai

Get your API key from the BESS AI dashboard → Settings → API Keys. The key looks like bess_sk_live_xxxxxxxx... and is shown only once at creation — store it securely.

Set it as an environment variable (the SDK reads it automatically):

export BESSAI_API_KEY="bess_sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
import BessAI from "bessai";
// or: import { BessAI } from "bessai";

const client = new BessAI(); // reads BESSAI_API_KEY from the environment

CommonJS works too:

const { BessAI } = require("bessai");
const client = new BessAI({ apiKey: "bess_..." });

Client options

const client = new BessAI({
apiKey: "bess_...", // or env BESSAI_API_KEY
baseUrl: "https://api.bess-ai.com", // or env BESSAI_BASE_URL
timeout: 30_000, // per-request timeout in ms
maxRetries: 3, // retries for 429/5xx and network errors
headers: {}, // extra headers merged into every request
});
OptionTypeDefaultNotes
apiKeystringBESSAI_API_KEY envRequired. The constructor throws if neither is set.
baseUrlstringhttps://api.bess-ai.comOverride with BESSAI_BASE_URL (trailing slashes are stripped).
timeoutnumber30000Per-request timeout in milliseconds.
maxRetriesnumber3Auto-retries on 429/500/502/503/504, timeouts, and connection errors, with exponential backoff that honors Retry-After.
headersRecord<string, string>{}Extra default headers.

Every request sends Authorization: Bearer <apiKey>.

Resource namespaces

All resources are properties on the client:

PropertyClassMethods
client.agentAgentResource12
client.callCallResource8
client.phoneNumberPhoneNumberResource12 (+1 alias)
client.batchCallBatchCallResource10 (+3 aliases)
client.workflowWorkflowResource25
client.analyticsAnalyticsResource3
client.billingBillingResource9
client.configConfigResource4
client.knowledgeBasesKnowledgeBasesResource6
client.apiKeysAPIKeysResource7
client.streamingStreamingResource1

Plural aliases point at the same instances: client.agents, client.calls, client.phoneNumbers, client.batchCalls, client.workflows.

The low-level HTTP client is exposed as client.httpClient (get / post / patch / put / delete / request / getBytes) for advanced use.

Request and response conventions

  • Request bodies use the API's snake_case field names (agent_name, from_number, system_prompt, ...) — exactly as documented in the REST reference. Method names and option-object wrappers are camelCase.
  • List endpoints return PaginatedResponse<T>:
interface PaginatedResponse<T> {
items: T[];
total: number;
skip: number;
limit: number;
}

There is no automatic paginator — loop with increasing skip until you receive fewer than limit items.


2. Quickstart

Create an agent, publish it, and place an outbound phone call:

import BessAI from "bessai";

const client = new BessAI(); // BESSAI_API_KEY from env

// 1. Create a draft agent
const agent = await client.agent.create({
agent_name: "Support Bot",
llm_provider: "openai",
llm_model: "gpt-4o-mini",
voice_provider: "elevenlabs",
voice_id: "21m00Tcm4TlvDq8ikWAM",
system_prompt: "You are a friendly support agent for Acme Co.",
greeting_message: "Hello! How can I help you today?",
language: "en-US",
});

// 2. Publish it — only published agents can take phone/web calls
await client.agent.publish(agent.agent_id);

// 3. Place a call
const call = await client.call.createPhoneCall({
agent_id: agent.agent_id,
from_number: "+14155550123", // a number you own
to_number: "+14155550199",
dynamic_variables: { customer_name: "Jordan" },
});
console.log(call.call_id, call.status);

3. Errors

All SDK errors extend BessAIError and carry message, statusCode, and body (the parsed error response). Import them from the package root:

import {
BessAIError,
AuthenticationError,
NotFoundError,
RateLimitError,
ValidationError,
} from "bessai";

try {
await client.agent.retrieve("does-not-exist");
} catch (e) {
if (e instanceof NotFoundError) {
console.log("No such agent");
} else if (e instanceof RateLimitError) {
console.log(`Slow down — retry after ${e.retryAfter}s`);
} else if (e instanceof AuthenticationError) {
console.log("Bad or missing API key");
} else if (e instanceof BessAIError) {
console.log(`API error ${e.statusCode}: ${e.message}`);
}
}
Error classHTTPMeaning
AuthenticationError401Missing, invalid, or expired API key.
PermissionDeniedError403The key's scopes do not allow this operation.
NotFoundError404Resource does not exist.
ValidationError422Invalid input. Has .errors (list of field errors from the API).
RateLimitError429Rate limit hit. Has .retryAfter (seconds, when provided).
InternalServerError5xxServer-side error.
ConnectionErrorCould not reach the server.
TimeoutErrorThe request timed out.
BessAIErrorBase class for all of the above.

Retryable statuses (429, 500, 502, 503, 504), timeouts, and connection errors are retried automatically up to maxRetries times before the error is thrown.


4. Agents — client.agent

Agents define the LLM, STT, TTS, voice, and behavior of your voice AI. Create a draft, then publish() to make it callable. Every update() saves a new draft version.

create(params: AgentCreateParams): Promise<AgentResponse>

POST /v1/agents — create a new agent in draft state. Only agent_name is required; everything else has a sensible server-side default.

Key AgentCreateParams fields (all snake_case, all optional unless noted):

FieldTypeDescription
agent_namestringRequired. Display name (1–255 chars). Sent to the API as name.
descriptionstringInternal description.
agent_typestringorchestration_agent (STT → LLM → TTS, default) or realtime_agent (speech-to-speech).
agent_modestringsingle_prompt (default) or conversation_flow.
llm_provider / llm_modelstringe.g. openai / gpt-4o, anthropic, groq.
system_promptstringMain instructions.
greeting_messagestringFirst utterance; empty = agent waits for the user.
temperaturenumber0–2.
max_completion_tokensnumber1–8192.
voice_provider / voice_idstringe.g. elevenlabs, cartesia + provider voice id.
voice_speednumber0.5–2.0.
stt_provider / stt_modelstringe.g. deepgram / nova-2.
stt_multilingualbooleanAuto language detection.
realtime_provider / realtime_model / realtime_voicestringFor realtime_agent types.
languagestringBCP-47 tag (en-US, tr-TR, ...).
interruption_sensitivitynumber0–1.
end_call_after_silence_msnumberAuto-end after silence.
max_call_duration_msnumberHard cap on call length.
knowledge_base_idsstring[]RAG knowledge bases to attach.
webhook_urlstringWebhook for call events.
mcp_configRecord<string, unknown>MCP server config (external tools).
native_tools_configRecord<string, unknown>Built-in tools (end_call, transfer_to_human, ...).

Additional fine-tuning fields: llm_base_url, llm_api_key, tts_model, volume, emotion, tts_base_url, tts_sample_rate, tts_use_websocket, stt_base_url, stt_api_key, voice_stability, responsiveness, endpointing_ms, enable_backchannel, response_delay_ms, reminder_trigger_ms, reminder_max_count, background_sound, analytics_prompt, analytics_model_provider, analytics_model, enable_sentiment_analysis, enable_summary_generation, post_call_analysis_config, version_description.

Returns AgentResponse: agent_id, agent_name, description, is_published, published_version, agent_type, agent_mode, created_at, updated_at, versions (array of AgentVersion config snapshots), linked_workflows.

const agent = await client.agent.create({
agent_name: "Reservations Bot",
agent_type: "orchestration_agent",
llm_provider: "openai",
llm_model: "gpt-4o",
voice_provider: "elevenlabs",
voice_id: "21m00Tcm4TlvDq8ikWAM",
system_prompt: "You take restaurant reservations. Be concise and friendly.",
language: "en-US",
});

retrieve(agentId: string): Promise<AgentResponse>

GET /v1/agents/{agent_id} — full agent including version history.

list(skip = 0, limit = 50): Promise<PaginatedResponse<AgentResponse>>

GET /v1/agents — all agents in your organization.

const { items, total } = await client.agent.list(0, 50);

update(agentId: string, params: AgentUpdateParams): Promise<AgentResponse>

PATCH /v1/agents/{agent_id} — change any field from create (all optional). Saves a new draft version; call publish() to go live.

delete(agentId: string): Promise<Record<string, unknown>>

DELETE /v1/agents/{agent_id} — permanently delete the agent and all versions.

publish(agentId: string, version?: number): Promise<AgentResponse>

POST /v1/agents/{agent_id}/publish — publish the current draft (or a specific version, sent as a query parameter) as the live version. Only published agents can take phone/web calls.

getVersions(agentId: string): Promise<Record<string, unknown>[]>

GET /v1/agents/{agent_id}/versions — full version history.

export(agentId: string): Promise<Record<string, unknown>>

GET /v1/agents/{agent_id}/export — portable JSON you can re-import.

importAgent(data: Record<string, unknown>): Promise<AgentResponse>

POST /v1/agents/import — create an agent from previously exported JSON. Takes the parsed JSON object (read the file yourself first).

import { readFileSync } from "node:fs";
const data = JSON.parse(readFileSync("./agent-export.json", "utf8"));
const imported = await client.agent.importAgent(data);

linkWorkflow(agentId, workflowId, options?): Promise<Record<string, unknown>>

POST /v1/agents/{agent_id}/workflows/{workflow_id} — link a workflow to an agent. Full signature:

linkWorkflow(
agentId: string,
workflowId: string,
options?: {
triggerConditionDescription?: string;
priority?: number;
executionModeOverride?: string;
}
): Promise<Record<string, unknown>>

unlinkWorkflow(agentId: string, workflowId: string): Promise<Record<string, unknown>>

DELETE /v1/agents/{agent_id}/workflows/{workflow_id} — unlink a workflow.

listWorkflows(agentId: string): Promise<Record<string, unknown>[]>

GET /v1/agents/{agent_id}/workflows — workflows linked to this agent.


5. Calls — client.call

Place phone calls, browser (web) calls, and test calls; retrieve transcripts, recordings, and analytics.

CallResponse fields: call_id, call_type, status (waitingringingconnectedended / failed), agent_id, from_number, to_number, access_token, room_name, room_url, start_time, end_time, duration_seconds, transcript, recording_url, call_analysis, call_summary, call_sentiment, call_cost, latency_metrics, processing_status, workflow_executions, created_at.

createPhoneCall(params: PhoneCallCreateParams): Promise<CallResponse>

POST /v1/calls/phone — place an outbound PSTN call immediately.

FieldTypeRequiredDescription
agent_idstringyesA published agent.
from_numberstringyesCaller ID in E.164 (+14155550123); a number you own.
to_numberstringyesDestination in E.164.
metadataRecord<string, unknown>noArbitrary metadata stored on the call.
dynamic_variablesRecord<string, string>noValues injected into the system prompt.
batch_call_idstringnoLink this call to a batch campaign.
const call = await client.call.createPhoneCall({
agent_id: "ag_abc123",
from_number: "+14155550123",
to_number: "+14155550199",
dynamic_variables: { customer_name: "Jordan" },
});

createWebCall(params: WebCallCreateParams): Promise<CallResponse>

POST /v1/calls/web — browser-to-agent call over WebRTC. Params: agent_id (required), metadata, dynamic_variables. The response includes access_token, room_name, and room_url for the LiveKit client SDK to connect.

createTestCall(params: TestCallCreateParams): Promise<CallResponse>

POST /v1/calls/test — like a web call but accepts draft agents and a temp_config object of non-persisted overrides. Params: agent_id (required), temp_config, dynamic_variables. The fastest loop for iterating on an agent during development.

retrieve(callId: string): Promise<CallResponse>

GET /v1/calls/{call_id} — full call details. Poll this after a call ends for the transcript, recording URL, and analytics.

list(params?: CallListParams): Promise<PaginatedResponse<CallListItem>>

GET /v1/calls — list calls with optional filters:

interface CallListParams {
skip?: number;
limit?: number;
agent_id?: string;
status?: string;
call_type?: string;
batch_call_id?: string; // a UUID, "none" (single calls), or "any" (batch calls)
from_date?: string; // ISO 8601
to_date?: string; // ISO 8601
}
const calls = await client.call.list({
agent_id: "ag_abc123",
status: "completed",
from_date: "2026-01-01",
});

end(callId: string): Promise<Record<string, unknown>>

POST /v1/calls/{call_id}/end — end an active call and trigger post-call processing.

delete(callId: string): Promise<Record<string, unknown>>

DELETE /v1/calls/{call_id} — delete a call record.

getRecording(callId: string): Promise<ArrayBuffer>

GET /v1/calls/{call_id}/recording — the recording audio file as raw bytes (typically OGG). Check retrieve(...).recording_url first to confirm a recording exists.

import { writeFileSync } from "node:fs";
const audio = await client.call.getRecording(call.call_id);
writeFileSync("./recording.ogg", Buffer.from(audio));

6. Phone Numbers — client.phoneNumber

Register numbers and manage their SIP connections and dispatch rules.

create(params: PhoneNumberCreateParams): Promise<PhoneNumberResponse>

POST /v1/phone-numbers — register a phone number.

FieldTypeRequiredDescription
phone_numberstringyesE.164 number.
nicknamestringnoFriendly name.
providerstringnoe.g. custom, netgsm, twilio, telnyx.
inbound_agent_idstringnoAgent for inbound calls.
outbound_agent_idstringnoAgent for outbound calls.
allowed_inbound_countriesstring[]noInbound country allowlist (default all).
allowed_outbound_countriesstring[]noOutbound country allowlist (default all).
inbound_webhook_urlstringnoWebhook for inbound events.

Returns PhoneNumberResponse: phone_number_id, phone_number, nickname, provider, inbound_agent_id, outbound_agent_id, status, created_at.

retrieve(phoneNumberId: string): Promise<PhoneNumberDetailResponse>

GET /v1/phone-numbers/{id} — detail view including sip_connections and dispatch_rules, agent names, country allowlists.

get(phoneNumberId: string): Promise<PhoneNumberDetailResponse>

Alias for retrieve().

list(skip = 0, limit = 50): Promise<PaginatedResponse<PhoneNumberResponse>>

GET /v1/phone-numbers — all numbers in your organization.

update(phoneNumberId: string, params: PhoneNumberUpdateParams): Promise<PhoneNumberDetailResponse>

PATCH /v1/phone-numbers/{id} — update nickname, provider, inbound_agent_id, outbound_agent_id, country allowlists, or inbound_webhook_url.

await client.phoneNumber.update(pn.phone_number_id, {
inbound_agent_id: "ag_xyz",
outbound_agent_id: "ag_abc",
});

delete(phoneNumberId: string): Promise<Record<string, unknown>>

DELETE /v1/phone-numbers/{id} — deletes the number and its SIP connections.

updateAgents(phoneNumberId: string, params: PhoneNumberAgentUpdateParams): Promise<PhoneNumberDetailResponse>

PATCH /v1/phone-numbers/{id}/agents — update only the agent assignment. Params: inbound_agent_id, outbound_agent_id (both optional).

SIP connections

A SIP connection carries the carrier trunk details (termination URI + credentials) and auto-syncs to the telephony layer.

createSipConnection(phoneNumberId: string, params: SIPConnectionCreateParams): Promise<SIPConnectionResponse>

POST /v1/phone-numbers/{id}/sip-connections

FieldTypeRequiredDescription
termination_uristringyesCarrier SIP URI.
username / passwordstringnoTrunk credentials.
nicknamestringnoFriendly name.
connection_typestringnoinbound, outbound, or both.
transportstringnoTCP, UDP, or TLS.
await client.phoneNumber.createSipConnection(pn.phone_number_id, {
termination_uri: "sip:trunk.provider.com",
username: "user",
password: "pass",
connection_type: "both",
});

getSipConnection(phoneNumberId: string, connectionId: string): Promise<SIPConnectionResponse>

GET /v1/phone-numbers/{id}/sip-connections/{connection_id}

listSipConnections(phoneNumberId: string): Promise<SIPConnectionResponse[]>

GET /v1/phone-numbers/{id}/sip-connections

updateSipConnection(phoneNumberId: string, connectionId: string, params: SIPConnectionUpdateParams): Promise<SIPConnectionResponse>

PATCH /v1/phone-numbers/{id}/sip-connections/{connection_id} — same fields as create, all optional.

deleteSipConnection(phoneNumberId: string, connectionId: string): Promise<Record<string, unknown>>

DELETE /v1/phone-numbers/{id}/sip-connections/{connection_id}

SIPConnectionResponse: sip_connection_id, phone_number_id, termination_uri, username, nickname, connection_type, transport, livekit_trunk_id, sync_status, sync_error, created_at, updated_at.

listDispatchRules(phoneNumberId: string): Promise<SIPDispatchRuleResponse[]>

GET /v1/phone-numbers/{id}/dispatch-rules — the SIP dispatch rules that route inbound calls on this number to an agent.


7. Batch Calls — client.batchCall

Run outbound calling campaigns over a list of contacts.

create(params: BatchCallCreateParams): Promise<BatchCallResponse>

POST /v1/batch-calls — create a campaign (does not start dialing; call start() next).

FieldTypeRequiredDescription
agent_idstringyesAgent that makes the calls.
from_numberstringyesRegistered caller ID.
contactsBatchCallContact[]yesEach: { phone_number, dynamic_variables? }.
namestringnoCampaign name.
max_concurrent_callsnumbernoSimultaneous calls.
retry_attemptsnumbernoRetries per failed contact.
const batch = await client.batchCall.create({
agent_id: "ag_abc123",
from_number: "+14155550123",
name: "March outreach",
contacts: [
{ phone_number: "+14155551001", dynamic_variables: { name: "Sam" } },
{ phone_number: "+14155551002" },
],
max_concurrent_calls: 10,
});
await client.batchCall.start(batch.batch_call_id);

retrieve(batchCallId: string): Promise<BatchCallStatusResponse>

GET /v1/batch-calls/{batch_id} — live status and counts: batch_call_id, name, status, total, pending, active, completed, failed, processed, counts, progress_percent, started_at, completed_at.

get(batchCallId) / getStatus(batchCallId)

Aliases for retrieve().

list(skip = 0, limit = 50): Promise<PaginatedResponse<BatchCallResponse>>

GET /v1/batch-calls — all campaigns.

listActive(): Promise<BatchCallResponse[]>

GET /v1/batch-calls/active — currently running campaigns.

listItems(batchCallId: string, skip = 0, limit = 100): Promise<PaginatedResponse<BatchCallItemResponse>>

GET /v1/batch-calls/{batch_id}/items — per-contact results (item_id, phone_number, status, attempt_count, call_id, error_message, started_at, completed_at). getItems(...) is an alias.

delete(batchCallId: string): Promise<Record<string, unknown>>

DELETE /v1/batch-calls/{batch_id} — delete a campaign (only when not running).

Lifecycle controls

  • start(batchCallId: string)POST /v1/batch-calls/{batch_id}/start
  • pause(batchCallId: string)POST /v1/batch-calls/{batch_id}/pause
  • resume(batchCallId: string)POST /v1/batch-calls/{batch_id}/resume
  • cancel(batchCallId: string)POST /v1/batch-calls/{batch_id}/cancel

Each returns Promise<Record<string, unknown>>.


8. Workflows — client.workflow

AI-powered automation (n8n under the hood) triggered by calls, webhooks, or schedules.

Create and generate

create(params: WorkflowCreateParams): Promise<WorkflowCreateResponse>

POST /v1/workflows — save n8n workflow JSON directly. Fields: name (required), trigger_type (required — post_call, in_call, webhook, or schedule), workflow_json (required), description, trigger_config, execution_mode, timeout_seconds.

generate(params: WorkflowGenerateParams): Promise<GenerateResponse>

POST /v1/workflows/generate — AI-generate a workflow from a natural-language description. Fields: name (required), description (required), trigger_type, trigger_config, agent_id. Returns workflow_id, workflow_json, required_secrets, visualization.

const wf = await client.workflow.generate({
name: "Lead Qualifier",
description: "After each call, send qualified leads to the CRM via webhook.",
});

refine(workflowId: string, params: WorkflowRefineParams): Promise<GenerateResponse>

POST /v1/workflows/{workflow_id}/refine — AI-refine an existing workflow. Params: { feedback: string }.

Read, update, delete

retrieve(workflowId: string): Promise<WorkflowDetailResponse>

GET /v1/workflows/{workflow_id} — includes workflow_json, detected_credentials, generation_history, n8n_workflow_id, timestamps.

list(params?: WorkflowListParams): Promise<PaginatedResponse<WorkflowResponse>>

GET /v1/workflows — filters: trigger_type, status, agent_id, include_deleted, page, per_page. (Note: this resource paginates with page/per_page, not skip/limit.)

update(workflowId: string, params: WorkflowUpdateParams): Promise<Record<string, unknown>>

PATCH /v1/workflows/{workflow_id}name, description, is_active, status, execution_mode, timeout_seconds.

delete(workflowId: string): Promise<Record<string, unknown>>

DELETE /v1/workflows/{workflow_id}

restore(workflowId: string, version: number): Promise<WorkflowDetailResponse>

POST /v1/workflows/{workflow_id}/restore — restore a previous version.

Secrets and credentials

saveSecrets(workflowId: string, params: WorkflowSecretsParams): Promise<Record<string, unknown>>

POST /v1/workflows/{workflow_id}/secrets — save encrypted secrets. secrets is an array of { key_name, value, type? }; optional validate_credentials.

getCredentialSchema(workflowId: string): Promise<CredentialSchemaResponse[]>

GET /v1/workflows/{workflow_id}/credential-schema — which credentials the workflow needs and their field schemas.

Deploy and execute

deploy(workflowId: string): Promise<DeployResponse>

POST /v1/workflows/{workflow_id}/deploy — push to the execution engine and activate. Returns status, workflow_id, n8n_workflow_id, error.

test(workflowId: string): Promise<ExecuteResponse>

POST /v1/workflows/{workflow_id}/test — test-execute.

execute(workflowId: string, params?: ManualExecuteParams): Promise<ExecuteResponse>

POST /v1/workflows/{workflow_id}/execute — manual execution with optional context_data and execution_mode. Returns status, execution_id, execution_time_ms, output, error.

listExecutions(workflowId: string, skip = 0, limit = 50, status?: string): Promise<PaginatedResponse<WorkflowExecutionResponse>>

GET /v1/workflows/{workflow_id}/executions — past executions with status, timing, error details, and input/output snapshots.

await client.workflow.saveSecrets(wf.workflow_id!, {
secrets: [{ key_name: "CRM_API_KEY", value: "sk_..." }],
});
await client.workflow.deploy(wf.workflow_id!);
const result = await client.workflow.test(wf.workflow_id!);

Export and import

  • export(workflowId: string): Promise<Record<string, unknown>>GET /v1/workflows/{workflow_id}/export
  • importWorkflow(data: Record<string, unknown>): Promise<WorkflowResponse>POST /v1/workflows/import (takes the parsed JSON object)

Agent linking

  • linkAgent(workflowId: string, agentId: string, params?: LinkAgentParams): Promise<AgentWorkflowLinkResponse>POST /v1/workflows/{workflow_id}/agents/{agent_id}. Params: trigger_condition_description, priority, execution_mode_override.
  • updateAgentLink(workflowId: string, agentId: string, params: UpdateAgentLinkParams): Promise<AgentWorkflowLinkResponse>PATCH /v1/workflows/{workflow_id}/agents/{agent_id}. Adds is_enabled.
  • unlinkAgent(workflowId: string, agentId: string): Promise<Record<string, unknown>>DELETE /v1/workflows/{workflow_id}/agents/{agent_id}
  • listAgents(workflowId: string): Promise<AgentWorkflowListItem[]>GET /v1/workflows/{workflow_id}/agents
  • listByAgent(agentId: string): Promise<AgentWorkflowListItem[]>GET /v1/agents/{agent_id}/workflows

Scheduling

  • setSchedule(workflowId: string, config: Record<string, unknown>): Promise<ScheduleStatusResponse>POST /v1/workflows/{workflow_id}/schedule (workflow must have trigger_type: "schedule"; config supports cron/interval settings).
  • getSchedule(workflowId: string): Promise<ScheduleStatusResponse>GET /v1/workflows/{workflow_id}/schedule
  • removeSchedule(workflowId: string): Promise<Record<string, unknown>>DELETE /v1/workflows/{workflow_id}/schedule
  • listSchedules(): Promise<ScheduleStatusResponse[]>GET /v1/workflows/schedules — all scheduled workflows in the organization.

9. Analytics — client.analytics

All three methods take the same optional arguments: fromDate and toDate (ISO 8601 strings, sent as from_date / to_date) and agentId (sent as agent_id).

getSummary(fromDate?, toDate?, agentId?): Promise<AnalyticsSummary>

GET /v1/analytics/summary — aggregate stats: total_calls, completed_calls, failed_calls, total_duration_seconds, average_duration_seconds, success_rate, from_date, to_date.

const summary = await client.analytics.getSummary("2026-01-01", "2026-01-31");
console.log(`${summary.total_calls} calls, ${summary.success_rate}% success`);

getLatency(fromDate?, toDate?, agentId?): Promise<LatencyMetrics>

GET /v1/analytics/latency — latency percentiles for e2e, stt, llm, and tts, each with p50 / p90 / p95 / p99.

getCallsByDay(fromDate?, toDate?, agentId?): Promise<CallsByDay[]>

GET /v1/analytics/calls-by-day — array of { date, count }.


10. Billing — client.billing

getBalance(): Promise<CreditBalanceResponse>

GET /v1/billing/balancecurrent_balance, total_credits_added, total_credits_used, organization_id.

checkBalance(amount: number): Promise<BalanceCheckResponse>

GET /v1/billing/balance/check — is the balance sufficient for amount? Returns sufficient, balance, required, min_balance, shortfall.

listTransactions(limit = 50, offset = 0, transactionType?: string): Promise<TransactionListResponse>

GET /v1/billing/transactions — credit ledger entries (entries, total, limit, offset).

listUsage(limit = 50, offset = 0, eventType?: string): Promise<UsageListResponse>

GET /v1/billing/usage — raw usage events (events, total, limit, offset).

getUsageSummary(fromDate?: string, toDate?: string): Promise<UsageSummaryResponse>

GET /v1/billing/usage/summaryperiod, total_events, total_price_usd, breakdown.

getDailyUsage(fromDate?: string, toDate?: string): Promise<DailyUsageItem[]>

GET /v1/billing/usage/daily — array of { date, event_count, total_price_usd }.

getCallUsage(callId: string): Promise<CallUsageResponse>

GET /v1/billing/usage/calls/{call_id} — usage events and total cost for one call.

getPricing(): Promise<ServicePricingResponse>

GET /v1/billing/pricingvoice_call_pricing, service_pricing, credit_config.

estimatePricing(params: PricingEstimateParams): Promise<PricingEstimateResponse>

POST /v1/billing/pricing/estimate — estimate per-minute cost for an agent configuration. Params (all optional): agent_type, stt_provider, stt_model, llm_provider, llm_model, voice_provider, realtime_provider, realtime_model. Returns total_per_minute, pricing_type, components, post_call_analytics_per_execution.

const est = await client.billing.estimatePricing({
llm_provider: "openai",
llm_model: "gpt-4o",
stt_provider: "deepgram",
voice_provider: "elevenlabs",
});
console.log(est.total_per_minute);

11. Config — client.config

Describes what the platform supports (providers, models, languages, defaults).

getProviders(): Promise<ProviderConfig>

GET /v1/config/providers — full catalog: stt, llm, tts, realtime, analytics, languages, defaults.

getProvider(category: string): Promise<Record<string, unknown>>

GET /v1/config/providers/{category} — one category: stt, llm, tts, or realtime.

getDefaults(): Promise<DefaultsConfig>

GET /v1/config/defaults — platform default provider/model selections.

getLanguages(): Promise<LanguageEntry[]>

GET /v1/config/languages — supported languages (code, name, native_name).


12. Knowledge Bases — client.knowledgeBases

RAG document stores you can attach to agents via knowledge_base_ids.

create(params: KnowledgeBaseCreateParams): Promise<KnowledgeBase>

POST /v1/knowledge-bases — params: name (required), description.

list(): Promise<KnowledgeBase[]>

GET /v1/knowledge-bases

get(knowledgeBaseId: string): Promise<KnowledgeBase>

GET /v1/knowledge-bases/{kb_id} — includes documents (each with id, filename, file_type, file_size_bytes, status, chunk_count, created_at).

delete(knowledgeBaseId: string): Promise<Record<string, unknown>>

DELETE /v1/knowledge-bases/{kb_id}

uploadDocument(knowledgeBaseId: string, file: Blob | Buffer, filename: string): Promise<Record<string, unknown>>

POST /v1/knowledge-bases/{kb_id}/documents — multipart upload. Accepts a Blob or a Node Buffer.

import { readFileSync } from "node:fs";

const kb = await client.knowledgeBases.create({
name: "Product Docs",
description: "Product documentation for agent reference",
});
const buf = readFileSync("./docs.pdf");
await client.knowledgeBases.uploadDocument(kb.id, buf, "docs.pdf");

deleteDocument(knowledgeBaseId: string, documentId: string): Promise<Record<string, unknown>>

DELETE /v1/knowledge-bases/{kb_id}/documents/{document_id}


13. API Keys — client.apiKeys

Programmatically manage keys for your organization.

create(params: APIKeyCreateParams): Promise<APIKeyCreated>

POST /v1/api-keys

FieldTypeRequiredDescription
namestringyesFriendly name.
scopesstring[]noe.g. ["agents:read", "calls:write"]; ["*"] = full access.
expires_in_daysnumbernoKey lifetime.
rate_limit_tierstringnofree, starter, professional, enterprise.
ip_allowliststring[]noIPs/CIDRs allowed to use the key; empty = all.

Returns APIKeyCreated — includes key, the full plaintext key, shown only once. Store it immediately.

const key = await client.apiKeys.create({
name: "Production Key",
scopes: ["agents:read", "calls:write"],
expires_in_days: 90,
});
console.log(key.key); // shown only once

list(): Promise<APIKey[]>

GET /v1/api-keys — all keys, masked (only key_prefix is shown).

get(keyId: string): Promise<APIKey>

GET /v1/api-keys/{key_id}

update(keyId: string, params: APIKeyUpdateParams): Promise<APIKey>

PATCH /v1/api-keys/{key_id}name, scopes, rate_limit_tier, ip_allowlist, is_active.

delete(keyId: string): Promise<Record<string, unknown>>

DELETE /v1/api-keys/{key_id} — revoke immediately.

rotate(keyId: string): Promise<APIKeyCreated>

POST /v1/api-keys/{key_id}/rotate — generate a new secret for the key. The response includes the new plaintext key (shown only once); the old key remains valid during the grace period.

getUsage(keyId: string): Promise<APIKeyUsage>

GET /v1/api-keys/{key_id}/usagetotal_requests, last_used_at, last_used_ip, rate_limit_tier, rate_limits, is_active, expires_at.


14. Streaming — client.streaming

Real-time events over WebSocket (requires the ws package, which is a bundled dependency of the SDK).

batchCallStatus(campaignId: string): BatchCallStream

Returns a BatchCallStream, an AsyncIterable<StreamEvent> that connects to wss://api.bess-ai.com/ws/batch-call/{campaign_id} (authenticated with your API key) and yields live status events for a running batch campaign:

const batch = await client.batchCall.create({ /* ... */ });
await client.batchCall.start(batch.batch_call_id);

for await (const event of client.streaming.batchCallStatus(batch.batch_call_id)) {
console.log(event);
}

BatchCallStream also exposes connect(): Promise<this> and close(): Promise<void> for manual lifecycle control; iteration auto-connects and auto-closes.


15. Parity notes (vs. the Python SDK)

The Node and Python SDKs mirror the same resource model. Known differences in the current Node release (bessai 0.1.x):

  • No chat namespace. The Python SDK's client.chat (chat sessions: create, create_test_session, send_message, retrieve, list, close) has no Node equivalent yet. Until it ships, call the REST endpoints directly (POST /v1/chat/sessions, POST /v1/chat/sessions/{id}/messages, ...) — client.httpClient works well for this — or use the Python SDK.
  • No syncSipConnection method. Python has phone_number.sync_sip_connection(...) to force a re-sync of a SIP connection; the Node SDK does not expose it.
  • Naming: the analytics namespace is client.analytics in Node (Python uses singular client.analytic with an analytics alias).
  • Import helpers take objects, not file paths. agent.importAgent(data) and workflow.importWorkflow(data) accept parsed JSON objects; the Python equivalents accept a file path.
  • The Python reference's warning about phone_number.update_agents() is a Python-only bug — the Node phoneNumber.updateAgents() method is implemented normally.