Node SDK

View as Markdown

The @getdial/sdk package is a typed client for Node and TypeScript.

Install

$npm install @getdial/sdk

Construct a client

1import { DialClient } from "@getdial/sdk";
2
3const dial = new DialClient({ apiKey: process.env.DIAL_API_KEY! });
4// Optional: new DialClient({ apiKey, baseUrl: "https://api.getdial.ai" })

Core operations

1// List your numbers
2const numbers = await dial.listNumbers();
3
4// Send an SMS
5const message = await dial.sendMessage({
6 to: "+14155550123",
7 fromNumberId: numbers[0].id,
8 body: "Hello from Dial",
9});
10
11// Place an AI voice call
12const call = await dial.makeCall({
13 to: "+14155550123",
14 fromNumberId: numbers[0].id,
15 outboundInstruction: "You are confirming a reservation.",
16 language: "en-US",
17});
18
19// Reply or react to a message (from listMessages or a message.received event)
20const reply = await dial.replyToMessage(message.id, { body: "On my way!" });
21const reaction = await dial.replyToMessage(message.id, { reaction: "πŸ”₯" });
22
23// History
24const recentMessages = await dial.listMessages();
25const recentCalls = await dial.listCalls();
26
27// Update a number's properties (any subset)
28await dial.setNumberProperties(numbers[0].id, {
29 nickname: "Support line",
30 inboundInstruction: "You are ACME's receptionist.",
31});

sendMessage and makeCall choose the from-number with exactly one of fromNumber (a flexible reference β€” phone number ID, one of your numbers in E.164, or a nickname) or fromNumberId (ID only).

Typing indicators

Show a typing indicator while composing. iMessage numbers display it; standard (SMS) numbers have no typing concept and silently ignore it, so the calls are safe unconditionally.

1// Primitives
2await dial.startTyping({ toNumber: "+14155550123", fromNumber: "Support line" });
3await dial.stopTyping({ toNumber: "+14155550123", fromNumber: "Support line" });
4
5// Or scope it to a block (TypeScript 5.2+): starts on creation, stops on dispose
6await using session = dial.typing({
7 toNumber: "+14155550123",
8 fromNumber: "Support line",
9});
10await session.sendMessage({ body: "Working on it…" }); // to/from prefilled

Delivering a message or reaction clears the indicator natively on the recipient’s device. The session compensates: after each session.sendMessage it automatically re-starts the indicator, so inside the block the bubble persists β€” the only real stop is the scope exit (or an explicit session.stop() when not using await using). With the bare primitives, remember that a send clears it: call startTyping again to keep composing, and stopTyping when you stop without sending. There is no keep-alive, so a stale indicator may also clear on its own during a very long pause.

The client makes a single attempt per call β€” it never auto-retries. makeCall accepts an optional idempotencyKey: pass the same key on each retry and a request that actually succeeded returns the original call instead of dialing again. sendMessage has no idempotency key β€” wrapping it in a retry can send a duplicate. See Retries and idempotency.

Stream events

newEventsConnection() returns an opened, async-iterable connection. Iterate it for live events and close it when done.

1const conn = await dial.newEventsConnection();
2try {
3 for await (const event of conn) {
4 if (event.type === "message.received") {
5 console.log("inbound:", event.data.from, event.data.body);
6 }
7 }
8} finally {
9 await conn.close();
10}

On TypeScript 5.2+ you can also use await using conn = await dial.newEventsConnection(); to close it automatically.

The stream is presence-based, not at-least-once β€” missed events replay only if you reconnect within 2 minutes. For durable, off-machine delivery, register a webhook β€” signed and retried at-least-once.

Types

DialClient returns typed PhoneNumber, Message, and Call objects (and DialConfig, SendMessageParams, ReplyToMessageParams, MakeCallParams, TypingParams for inputs). See Core concepts for what each field means.