← Public packages
@kody/blandPublic
src/send-call.ts
99 lines · 2.5 KB · TypeScript/**
* Place an outbound Bland AI phone call (`POST /v1/calls`).
* Dry-run by default — pass `confirm: true` to dial live.
* Provide `task` or `pathway_id` (not both required together; Bland needs one).
*
* @param input - phone_number plus task or pathway_id, optional voice/webhook
* @returns Call create response (`call_id`) or dry-run preview
*
* @example
* import { sendCall } from 'kody:@kody/bland/send-call'
* await sendCall({
* phone_number: '+15551234567',
* task: 'Ask whether they carry 10mm hex keys. Thank them and hang up.',
* voice: 'maya',
* max_duration: 5,
* confirm: true,
* })
*/
import { blandRequest, type BlandAuthInput, type BlandObject } from './core.ts'
import { packageStorage } from 'kody:runtime'
import { SETTINGS_DEFAULT_VOICE } from './core.ts'
export type SendCallInput = BlandAuthInput & {
body?: BlandObject
phone_number?: string
task?: string
pathway_id?: string
pathway_version?: number
voice?: string
max_duration?: number
webhook?: string
webhook_events?: string[]
keywords?: string[]
from?: string
voicemail?: BlandObject
wait_for_greeting?: boolean
record?: boolean
dryRun?: boolean
confirm?: boolean
}
async function defaultVoice(): Promise<string | null> {
try {
const v = String((await packageStorage().get(SETTINGS_DEFAULT_VOICE)) ?? '').trim()
return v || null
} catch {
return null
}
}
function buildBody(input: SendCallInput, voiceFallback: string | null): BlandObject {
if (input.body && typeof input.body === 'object') {
return { ...input.body }
}
const body: BlandObject = {}
const keys = [
'phone_number',
'task',
'pathway_id',
'pathway_version',
'voice',
'max_duration',
'webhook',
'webhook_events',
'keywords',
'from',
'voicemail',
'wait_for_greeting',
'record',
] as const
for (const key of keys) {
const value = input[key]
if (value !== undefined) body[key] = value
}
if (body.voice == null && voiceFallback) body.voice = voiceFallback
return body
}
export async function sendCall(input: SendCallInput = {}) {
const voiceFallback = await defaultVoice()
const body = buildBody(input, voiceFallback)
const live = input.confirm === true && input.dryRun !== true
if (live) {
if (!body.phone_number) {
throw new Error('phone_number is required (E.164, e.g. +15551234567).')
}
if (!body.task && !body.pathway_id) {
throw new Error('Provide task (prompt) or pathway_id before placing a live call.')
}
}
return blandRequest({
...input,
method: 'POST',
path: '/v1/calls',
body,
})
}
export default sendCall