UNPKG

bland-voice

Version:

The official SDK for Bland AI phone calls.

325 lines (302 loc) 11.7 kB
const fetch = require('node-fetch'); /** * A Node.js client for the Bland AI API. */ class Bland { /** * Constructs a new Bland client instance. * @param {string} apiKey - Your Bland AI API key. */ constructor(apiKey) { if (!apiKey) { throw new Error('API key is required'); } this.apiKey = apiKey; this.baseUrl = 'https://api.bland.ai'; } /** * Starts a phone call using the Bland AI API. * @param {string} phoneNumber - The target phone number, including country code. * @param {boolean} reduceLatency - If true, reduces response latency. * @param {number} voiceId - The ID of the voice to be used. * @param {Object} additionalParams - Additional parameters for the call. * @returns {Promise<Object>} - The API response. */ async startCall(phoneNumber, reduceLatency, voiceId, additionalParams) { if (!phoneNumber) { throw new Error('Phone number is required'); } const payload = { phone_number: phoneNumber, reduce_latency: reduceLatency, voice_id: voiceId, ...additionalParams, }; try { const response = await fetch(this.baseUrl + "/call", { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error making the call:', error); throw error; } } /** * Fetches the logs for a specific call. If 'poll' is true, it will poll the logs until the call is completed or an error occurs. * @param {string} callId - The unique identifier for the call. * @param {boolean} poll - If true, continually polls the logs. * @returns {Promise<Object>} - The logs data or error. */ async fetchLogs(callId, poll = false) { const fetchLogData = async () => { try { const response = await fetch(`${this.baseUrl}/logs`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify({ call_id: callId }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error fetching logs:', error); throw error; } }; if (poll) { return new Promise((resolve, reject) => { const interval = setInterval(async () => { const data = await fetchLogData(); if (data.error) { clearInterval(interval); reject(data.error); } else if (data.completed) { clearInterval(interval); resolve(data); } }, 2000); // Poll every 2 seconds as per API recommendation }); } else { return fetchLogData(); } } /** * Ends an ongoing phone call using the Bland AI API. * @param {string} callId - The unique identifier for the call to be ended. * @returns {Promise<Object>} - The API response. */ async endCall(callId) { if (!callId) { throw new Error('Call ID is required'); } try { const response = await fetch(`${this.baseUrl}/end`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify({ call_id: callId }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error ending the call:', error); throw error; } } /** * Initiates a batch of phone calls. * @param {string} basePrompt - The base prompt used for all calls. * @param {Array} callData - An array of objects defining the calls. * @param {Object} options - Optional parameters for the batch call. * @returns {Promise<Object>} - The API response. */ async batchCall(basePrompt, callData, options = {}) { const payload = { base_prompt: basePrompt, call_data: callData, ...options, }; try { const response = await fetch(`${this.baseUrl}/batch`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error initiating batch call:', error); throw error; } } /** * Retrieves the recording of a completed call. * @param {string} callId - The unique identifier for the call. * @returns {Promise<Object>} - The API response with the recording URL. */ async getRecording(callId) { if (!callId) { throw new Error('Call ID is required'); } try { const response = await fetch(`${this.baseUrl}/recording`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify({ call_id: callId }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error retrieving the recording:', error); throw error; } } /** * Updates the inbound prompt for a specific phone number. * @param {string} phoneNumber - The phone number associated with the prompt. * @param {string} prompt - The new prompt text. * @param {number} [voiceId=0] - (Optional) The ID for the custom voice. * @returns {Promise<Object>} - The API response. */ async updateInboundPrompt(phoneNumber, prompt, voiceId = 0) { if (!phoneNumber || !prompt) { throw new Error('Phone number and prompt are required'); } const payload = { phone_number: phoneNumber, prompt: prompt, voice_id: voiceId, }; try { const response = await fetch(`${this.baseUrl}/inbound/update`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error updating inbound prompt:', error); throw error; } } /** * Makes an outbound call, waits on hold, and connects once a human answers. * @param {string} phoneNumber - The phone number to call. * @param {string} holdConnect - The phone number to connect to once hold ends. * @param {string} [task=""] - (Optional) Additional instructions for the call. * @returns {Promise<Object>} - The API response with status and call ID. */ async hold(phoneNumber, holdConnect, task = "") { if (!phoneNumber || !holdConnect) { throw new Error('Phone number and hold connect number are required'); } const payload = { phone_number: phoneNumber, hold_connect: holdConnect, task: task, }; try { const response = await fetch(`${this.baseUrl}/hold`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error executing hold for me:', error); throw error; } } /** * Retrieves details for a specific batch of calls. * @param {string} batchId - The unique identifier for the batch. * @param {boolean} [includeCalls=false] - Whether to include individual call data. * @returns {Promise<Object>} - The batch details. */ async getBatch(batchId, includeCalls = false) { if (!batchId) { throw new Error('Batch ID is required'); } const queryParams = new URLSearchParams({ batch_id: batchId, include_calls: includeCalls }).toString(); try { const response = await fetch(`${this.baseUrl}/batch?${queryParams}`, { method: 'GET', headers: { 'Authorization': this.apiKey }, }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error retrieving batch:', error); throw error; } } /** * Cancels all calls in a specific batch. * @param {string} batchId - The unique identifier for the batch to be cancelled. * @returns {Promise<Object>} - The API response with status and details. */ async cancelBatch(batchId) { if (!batchId) { throw new Error('Batch ID is required'); } try { const response = await fetch(`${this.baseUrl}/batch/stop`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify({ batch_id: batchId }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error cancelling batch:', error); throw error; } } /** * Purchases and configures a new inbound phone number. * @param {Object} options - The options for purchasing the number. * @returns {Promise<Object>} - The API response with the new phone number. */ async purchaseInboundNumber(options) { if (!options || !options.area_code) { throw new Error('Area code is required'); } try { const response = await fetch(`${this.baseUrl}/purchasenumber`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': this.apiKey }, body: JSON.stringify(options), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return response.json(); } catch (error) { console.error('Error purchasing inbound number:', error); throw error; } } } module.exports = Bland;