@mtdsdev/ai-telecom
v1.0.1
Published
Node.js SDK for the AI Telecom voice API — place AI phone calls with a phone number and a prompt.
Maintainers
Readme
npm install @mtdsdev/ai-telecomimport { Client } from '@mtdsdev/ai-telecom';
const client = new Client({ apiKey: 'sk_live_...' });
const call = await client.calls.create({
to: '+84901234567',
prompt: `
Bạn là nhân viên tư vấn của MTDS.
Chào khách, hỏi xem anh chị có quan tâm gói internet không.
`,
});
console.log(call.id, call.status); // call_Ku3n8pQ7... queuedThat is the whole getting-started. No campaign, no contact list, no workflow to build first — a phone number and a prompt is the complete API surface you need.
baseUrl defaults to https://telecom.mtds.vn; pass it only when developing
against a local deployment. Requires Node.js 18+ (native fetch).
The TypeScript API uses camelCase (agentId, fromNumber); the HTTP wire
format remains snake_case as in the OpenAPI spec.
Calls
// Prompt inline — omit fromNumber → platform uses your first active SIP line
await client.calls.create({ to: '+84901234567', prompt: '...' });
// Pick which number to call from (display number, SIP extension, or trunk name)
await client.calls.create({
to: '+84901234567',
prompt: '...',
fromNumber: '0592014235',
});
await client.calls.create({ to: '+84901234567', agentId: 'agent_9fQz...' });
// Per-call script; keep the agent's voice / KB / config
await client.calls.create({
to: '+84901234567',
agentId: 'agent_9fQz...',
prompt: 'Hôm nay chỉ nói về gói mùa hè…',
});
// Skip scoring / detach the agent's knowledge base for this call
await client.calls.create({
to: '+84901234567',
agentId: 'agent_9fQz...',
evaluation: '',
knowledgeBaseId: '',
});
// Many numbers at once → returns an array
await client.calls.create({
to: ['+84901234567', '+84912345678'],
agentId: 'agent_9fQz...',
});
// Attach your own IDs (schedule in your own system — call create when ready)
await client.calls.create({
to: '+84901234567',
prompt: '...',
metadata: { crm_id: 'C-42' },
});Prompt resolution order: inline prompt → inline agent → agentId → your default agent.
Empty string "" on evaluation, knowledgeBase, or knowledgeBaseId means
explicitly off. Omit the field to inherit from the agent.
Reading results
const call = await client.calls.get('call_Ku3n8pQ7...');
call.status; // completed | no_answer | failed | …
call.duration;
call.transcript;
call.score;
call.outcome;
call.usage?.costUsd; // null = pricing not configured, NOT free
call.usage?.latencyMs;
(await client.calls.recordingUrl(call.id)).url;Listing and filtering
await client.calls.list({ status: 'completed', scoreMin: 70 });
await client.calls.list({ from: '2026-07-01', until: '2026-07-31' });
for await (const call of client.calls.iter({ outcome: 'potential' })) {
console.log(call.to, call.score);
}Cancel / wait
await client.calls.cancel('call_Ku3n8pQ7...');
const done = await client.calls.wait('call_Ku3n8pQ7...', { timeout: 600 });Prefer the call.completed webhook in production.
Agents (optional)
const agent = await client.agents.create({
name: 'Fibre internet outreach',
prompt: 'Bạn là nhân viên tư vấn của MTDS...',
evaluation: 'Chấm 0–100 theo mức độ quan tâm.',
isDefault: true,
});
await client.calls.create({ to: '+84901234567', agentId: agent.id });
await client.agents.list();
await client.agents.update(agent.id, { prompt: '...' });
await client.agents.delete(agent.id);Knowledge bases (optional)
const kb = await client.knowledgeBases.create({ name: 'Bảng giá 2026' });
await client.knowledgeBases.documents.create(kb.id, {
name: 'Gói cước',
content: 'Gói Cơ bản: hai trăm nghìn đồng một tháng...',
});
await client.knowledgeBases.documents.upload(kb.id, 'bang-gia.pdf');
await client.knowledgeBases.wait(kb.id);
// Shortcut: create + upload + wait
const kb2 = await client.knowledgeBases.createWithUpload('bang-gia.pdf', {
name: 'Bảng giá 2026',
});Attach with knowledgeBaseId on an agent or call; pass knowledgeBaseId: '' to detach for one call.
Phone numbers
Read-only; provisioned by an administrator.
for (const number of await client.phoneNumbers.list()) {
console.log(number.id, number.number, number.trunk, number.quotaRemaining);
}
await client.calls.create({
to: '+84901234567',
prompt: '...',
fromNumber: '0592014235', // or extension "1001" or trunk name
});Usage
const usage = await client.usage.get();
const usage2 = await client.usage.get({ from: '2026-07-01' });
console.log(usage.total?.calls, usage.total?.minutes);minutes are actual talk time: duration_sec / 60 per answered call, not rounded up.
Webhooks
import { webhooks, WebhookVerificationError } from '@mtdsdev/ai-telecom';
import express from 'express';
const app = express();
app.post(
'/hooks/ai-telecom',
express.raw({ type: '*/*' }),
(req, res) => {
try {
const event = webhooks.verify(
req.body as Buffer,
String(req.headers['x-signature'] ?? ''),
process.env.WEBHOOK_SECRET!,
);
console.log(event.event, event.data.id, event.data.score);
res.sendStatus(200);
} catch (e) {
if (e instanceof WebhookVerificationError) return res.sendStatus(400);
throw e;
}
},
);Always verify the raw body before trusting the payload.
Errors
import {
AITelecomError,
AuthenticationError,
PermissionError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
APIConnectionError,
} from '@mtdsdev/ai-telecom';
try {
await client.calls.create({ to: 'not-a-number', prompt: '...' });
} catch (e) {
if (e instanceof ValidationError) console.log(e.code, e.message);
}Network errors and 429/5xx are retried with backoff. 401/403/404/400/422 are not.
Configuration
const client = new Client({
apiKey: 'sk_live_...',
baseUrl: 'http://localhost:3000',
timeoutMs: 30_000,
maxRetries: 2,
});Versioning
Pinned to /api/v1. Unknown response fields are ignored (forward-compatible).
License
MIT © MTDS
