@dialgem/sdk 0.1.0-beta.1 → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0-beta.1 (unreleased)
3
+ ## 0.1.0-beta.3
4
+
5
+ ### Added
6
+ - `phoneNumbers.search({ country?, type?, area_code?, contains?, limit? })` lists numbers that can be bought, with the monthly price, and `phoneNumbers.buy({ phone_number, type?, nickname? })` buys one. **Buying spends the organization's credit**: the first month is debited when you buy and the rental renews monthly until the number is released; nothing is charged if the purchase fails. Both need the deployment to have number purchasing switched on (`search` throws `NotFoundError`, `number_purchase_disabled`, otherwise). Types: `PhoneNumberSearchParams`, `PhoneNumberBuyParams`, `AvailablePhoneNumbers`, `PurchasedPhoneNumber`.
7
+ - A number looked up with `phoneNumbers.retrieve` now carries `own_twilio` (whether it is on a Twilio account you connected yourself). Connecting your own Twilio account and importing its numbers is a dashboard feature; it is not in the SDK, because it handles your Twilio credentials.
8
+
9
+ ## 0.1.0-beta.2
10
+
11
+ ### Not in this SDK, on purpose
12
+ Campaigns, webhook endpoint management, usage, billing, analytics, and contact erasure / do-not-call are dashboard-only. See the surface policy in the Python SDK's `docs/api-surface.md`.
4
13
 
5
14
  ### Breaking changes
6
15
  - **The API address is fixed at `https://api.dialgem.com`.** The `baseURL` option and the `DIALGEM_BASE_URL` environment variable are gone. Passing `baseURL` now throws rather than being ignored, so a leftover staging or local URL cannot quietly send your key to production. `DEFAULT_BASE_URL` is renamed `BASE_URL`.
@@ -9,6 +18,17 @@
9
18
  - **`calls.list` pages are cursor-based.** A page is `{ data, has_more, pagination_key }`. `page`, `page_size` and `total` are gone; use `limit`.
10
19
 
11
20
  ### Added
21
+ - `sessions.say(sessionId, { text, interrupt? })` and `sessions.release(sessionId)`: make the agent speak a line on a live call, and hand a taken-over call back. Off unless the deployment enables live control (`live_control_disabled`). Listening in and taking over run over a WebSocket this SDK does not wrap. `sessions.update` now reaches a call on any service instance.
22
+
23
+ - `sessions.update(sessionId, { system_prompt?, dynamic_variables? })`: change a live call's prompt and/or variables from the agent's next turn. `sessionId` is the session's id or the call's `call_id`. Only that call changes and it is not saved.
24
+ - **Saved LLMs.** `llms` (`create`, `list`, `retrieve`, `update`, `delete`): a prompt and model set up once, attached with `llm_id` on `agents.create` / `agents.update`. Attaching copies the values; later edits or deletion do not change the agent, and `llm_id` cannot be sent with the fields it carries.
25
+ - `agents.mcpServers.listTools(agentId, serverId)`: the server's tools with descriptions and argument schemas, from a live connection; stores nothing.
26
+ - **Saved flows.** `flows` (`create`, `list`, `retrieve`, `update`, `delete`): build and check a conversation flow once, then attach it with `flow_id` on `agents.create` / `agents.update`. Attaching copies the flow; later edits or deletion do not change the agent.
27
+ - **Chats.** `chats` (`create`, `list`, `retrieve`, `update`, `delete`, `sendMessage`, `end`, `rerunAnalysis`): text conversations with a prompt agent, with the agent's own prompt, model and functions.
28
+ - Chats are billed per agent reply (a spent balance is a 402 `insufficient_credit`); `Chat.chat_cost` shows the spend (`null` for a team-member session). Webhook events `chat_started`, `chat_ended` and `chat_analyzed` are in the `WebhookEvent` union (`ChatStartedEvent`, `ChatEndedEvent`, `ChatAnalyzedEvent`); they carry `chat_id` and `chat`, not `call_id`, so narrow on `event` before reading `call`.
29
+ - `calls.rerunAnalysis(callId)`, `contacts.retrieveByPhone(phone, { default_country })` and `apiKey.retrieve()` (who this key is; works for any scope).
30
+ - **Agent simulations.** `simulations.createSuite`, `createCase`, `run` and `retrieveRun` (with the rest of `listSuites`, `retrieveSuite`, `updateSuite`, `deleteSuite`, `updateCase`, `deleteCase`, `listRuns`, `cancelRun`): save conversations a simulated caller has with your agent, run them in text against the draft or a published version, and check what it said and did. A suite's `publish_gate` can warn or block publishing a version until the draft has passed. Nothing is dialled or billed and the agent's tools are never really called.
31
+ - **Inbound routing webhook.** `phoneNumbers.update(id, { inbound_webhook_url })` sets (or, with `null`, removes) a URL Dialgem asks, before an inbound call is answered, which agent should take it, with variables or metadata, or to reject it. `phoneNumbers.testInboundWebhook(id)` sends one signed test request; `phoneNumbers.rotateInboundWebhookSecret(id)` replaces the signing secret. `PhoneNumber` gains `inbound_webhook_url` and `inbound_webhook_secret_hint`, and `inbound_webhook_secret` in the one response that creates or rotates it.
12
32
  - `calls.update` (replace metadata), `calls.delete` and `calls.stop`.
13
33
  - `metadata` on `createPhoneCall` and `register`.
14
34
  - New call record fields: `metadata`, `dynamic_variables`, `start_timestamp`, `end_timestamp`, `ended_at`, `duration_ms`, `transcript_text`, `in_voicemail` and `latency` (per-stage p50/p90/p95/p99). Post-call analysis gains `call_summary` and `in_voicemail`.
@@ -18,6 +38,8 @@
18
38
  - **Voice cloning.** `voices.clone({ name, files, consent_confirmed: true })` uploads audio and returns a voice private to your organization; `voices.delete(voiceId)` removes it at the provider and here. `Voice` gains `is_custom` and `created_by`. The upload is never retried, whatever `maxRetries` says.
19
39
  - `phoneNumbers.retrieve(phoneNumber)` looks a number up by its E.164 value.
20
40
  - **`agent_version: "latest_published"`** on a phone number and on `createPhoneCall` / `register`: a pointer that follows the newest publish rather than naming a version. A call still reports the concrete version it ran.
41
+ - **Contacts.** `contacts` (`create`, `list`, `retrieve`, `update`, `delete`, `calls`), `contactLists` (`list`, `create`, `retrieve`, `update`, `delete`, `addContacts`, `removeContact`) and CSV import under `contacts.imports` (`inspect`, `create`, `list`, `retrieve`, `errors`, `commit`, `cancel`). A contact is keyed on its phone number, stored in E.164 however it is written (pass `default_country` for numbers without a `+`). `do_not_call` on a contact is read from your do-not-call list, not stored on it. Imports run in the background: start one with `dry_run: true`, poll `retrieve` until it is `previewed`, then `commit`. `contacts.list` pages by cursor like `calls.list`. CSV downloads (the export, and the rejected-rows report) are available over the REST API only.
42
+ - **Browser calls.** `webCalls.create({ agent_id, metadata, dialgem_llm_dynamic_variables })` creates a call for one visitor and returns a one-use `access_token` (60 seconds) for `@dialgem/client`, so a web page can talk to an agent without your API key ever reaching the browser. Allow the pages that may start calls with `webCalls.updateSettings({ allowed_origins })` (`retrieveSettings` reads them back); `create` answers 409 `web_origins_not_configured` until at least one is set.
21
43
  - **Agent versions.** `agents.versions.publish` freezes an agent — its configuration, functions and MCP servers — as an immutable numbered snapshot; `list` and `retrieve` read them back. Pin one with `inbound_agent_version` / `outbound_agent_version` on a phone number, or `agent_version` on `createPhoneCall` and `register`. The version a call ran is reported as `agent_version` on the call record and in every webhook. The agent itself is the draft, version 0, and stays what every unpinned call runs.
22
44
 
23
45
  ## 0.1.0-beta.0 (unreleased)
package/README.md CHANGED
@@ -1,11 +1,45 @@
1
- # @dialgem/sdk
1
+ <div align="center">
2
2
 
3
- The official TypeScript SDK for the [Dialgem](https://dialgem.com) voice AI API. Use it from your backend to create voice agents, put them on phone numbers, place and register calls, pull transcripts, and verify webhooks.
3
+ # Dialgem for TypeScript
4
4
 
5
- - Written in TypeScript. Request bodies are typed from the API's own schema, including the full conversation-flow node union.
6
- - Zero runtime dependencies. Ships ESM and CommonJS builds.
7
- - Node.js 20+. It also runs on Bun, Deno and serverless runtimes that provide `fetch` and Web Crypto.
8
- - Server-side only. Your API key grants full access to your organization.
5
+ **The official TypeScript SDK for the [Dialgem](https://dialgem.com) voice AI API.**
6
+
7
+ Build voice agents, put them on phone numbers, place calls, steer them live, and read transcripts, all from your backend.
8
+
9
+ [![npm](https://img.shields.io/npm/v/@dialgem/sdk?include_prereleases&color=CB3837&logo=npm&logoColor=white)](https://www.npmjs.com/package/@dialgem/sdk)
10
+ [![Node](https://img.shields.io/node/v/@dialgem/sdk?logo=node.js&logoColor=white)](https://www.npmjs.com/package/@dialgem/sdk)
11
+ [![CI](https://img.shields.io/github/actions/workflow/status/DialgemAI/dialgem-sdk-typescript/ci.yml?branch=main&label=CI&logo=github)](https://github.com/DialgemAI/dialgem-sdk-typescript/actions/workflows/ci.yml)
12
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
13
+ [![TypeScript](https://img.shields.io/badge/TypeScript-typed-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
14
+ [![Stars](https://img.shields.io/github/stars/DialgemAI/dialgem-sdk-typescript?style=flat&logo=github)](https://github.com/DialgemAI/dialgem-sdk-typescript/stargazers)
15
+
16
+ [Website](https://dialgem.com) · [Issues](https://github.com/DialgemAI/dialgem-sdk-typescript/issues) · [Changelog](CHANGELOG.md) · [Python SDK](https://github.com/DialgemAI/dialgem-sdk-python)
17
+
18
+ </div>
19
+
20
+ ---
21
+
22
+ ```ts
23
+ import { Dialgem } from "@dialgem/sdk"
24
+
25
+ const dialgem = new Dialgem() // reads DIALGEM_API_KEY
26
+
27
+ const agent = await dialgem.agents.create({ name: "Front desk", system_prompt: "You answer for Acme Dental." })
28
+ const { call_id } = await dialgem.calls.createPhoneCall({ from_: "+14155550100", to: "+14155550123", agent_id: agent.id })
29
+ ```
30
+
31
+ ## Why this SDK
32
+
33
+ - **Typed end to end.** Request bodies are typed from the API's own schema, including the full conversation-flow node union, and webhook events are a discriminated union you can `switch` on.
34
+ - **Zero runtime dependencies.** ESM and CommonJS builds, so it works from any Node project.
35
+ - **Runs anywhere with `fetch`.** Node.js 20+, Bun, Deno and serverless runtimes that provide `fetch` and Web Crypto.
36
+ - **Safe by default.** `POST` is never retried (a retry could ring someone twice), other requests retry with backoff and `Retry-After`, and redirects are never followed so your key can't be sent elsewhere.
37
+ - **Everything a backend needs.** Agents (with tools, knowledge, MCP servers and versions), phone numbers, calls and chats, saved flows and LLMs, voices, contacts, live call control, and webhook signature verification.
38
+ - **Server-side only.** Your API key grants full access to your organization. To talk to an agent from a web page, use [`@dialgem/client`](https://github.com/DialgemAI/dialgem-client-sdk).
39
+
40
+ ## Contents
41
+
42
+ [Install](#install) · [Quickstart](#quickstart) · [Recipes](#recipes) · [Live call control](#live-call-control) · [Errors](#errors) · [Retries](#retries) · [Coverage](#coverage) · [Development](#development) · [License](#license)
9
43
 
10
44
  ## Install
11
45
 
@@ -126,13 +160,13 @@ Your Twilio credentials are never shared with Dialgem. `stream_url` is signed an
126
160
 
127
161
  ### 4. Verify webhooks
128
162
 
129
- Set `webhook_url`, `webhook_events` and `webhook_signing_secret` on the agent. Dialgem then POSTs `call_started`, `call_ended` and `call_analyzed` events. Each body is `{ event, call_id, agent_id, call }`, where `call` is the full call record, and each request is signed:
163
+ Set `webhook_url`, `webhook_events` and `webhook_signing_secret` on the agent (and, optionally, `webhook_timeout_ms`, 1000–30000, default 10000, to bound how long each delivery attempt waits for your server). Dialgem then POSTs the events you subscribe to: `call_started`, `call_ended`, `call_analyzed`, `transcript_updated`, `transfer_started`, `transfer_bridged`, `transfer_cancelled` and `transfer_ended`. Each body is `{ event, call_id, agent_id, call }`, where `call` is the full call record; `transcript_updated` also carries `role`, `turn_index` and the `transcript` so far, and the transfer events carry `transfer: { target?, mode?, reason? }`. Each request is signed:
130
164
 
131
165
  ```
132
166
  x-dialgem-signature: v=<timestamp_ms>,d=<hex> hex = HMAC-SHA256(secret, raw_body + timestamp_ms)
133
167
  ```
134
168
 
135
- `constructEvent` and `verify` check the HMAC and reject signatures more than 5 minutes old, so a captured request cannot be replayed. `call_ended` arrives as soon as the call ends; `call_analyzed` follows once the summary, sentiment and your extracted variables are ready.
169
+ `constructEvent` and `verify` check the HMAC and reject signatures more than 5 minutes old, so a captured request cannot be replayed. `call_ended` arrives as soon as the call ends; `call_analyzed` follows once the summary, sentiment and your extracted variables are ready. `transcript_updated` is sent per turn and may be retried out of order, so keep the highest `turn_index`. For a cold transfer, `transfer_bridged` is sent when the transferred leg finishes, followed by `transfer_ended`.
136
170
 
137
171
  > **Verify the raw body.** The signature covers the exact bytes Dialgem sent. A body that was parsed and re-serialized, for example by `express.json()`, will never verify, even when the JSON is identical.
138
172
 
@@ -255,6 +289,24 @@ than quietly picking one.
255
289
  `voices.delete(voice.id)` removes it at the provider and here. It refuses with a
256
290
  409 while an agent still uses it, rather than leaving that agent without a voice.
257
291
 
292
+
293
+ ## Live call control
294
+
295
+ Steer a call while it is happening. `sessionId` is the session's id from `sessions.list()` or the call's own `call_id`, and the call can be on any service instance.
296
+
297
+ ```ts
298
+ // Change this call's prompt or variables from the agent's next turn (not saved, only this call).
299
+ await dialgem.sessions.update(call_id, { dynamic_variables: { plan: "pro" } })
300
+
301
+ // Make the agent say a line in its own voice; interrupt: true cuts in now.
302
+ await dialgem.sessions.say(call_id, { text: "One moment while I check that." })
303
+
304
+ // Give a taken-over call back to the agent.
305
+ await dialgem.sessions.release(call_id)
306
+ ```
307
+
308
+ `say` and `release` are **off unless your deployment enables live control** (`PermissionDeniedError` with `errCode` `live_control_disabled`), and are for owners, admins and API keys only. Listening in and taking a call over run over a WebSocket (`/v1/agent/sessions/{id}/live`), which this SDK does not wrap.
309
+
258
310
  ## Errors
259
311
 
260
312
  Every error extends `DialgemError`. When the API returns an HTTP error, the SDK throws an `APIError` subclass chosen by status code:
@@ -303,13 +355,19 @@ The SDK never follows redirects. The API does not issue them, and following one
303
355
  | `agents.knowledge` | `list`, `create`, `delete` |
304
356
  | `agents.mcpServers` | `list`, `create`, `update`, `delete`, `test` |
305
357
  | `agents.versions` | `publish`, `list`, `listPage`, `retrieve` |
306
- | `calls` | `list`, `retrieve`, `update`, `delete`, `stop`, `createPhoneCall`, `register` |
358
+ | `calls` | `list`, `retrieve`, `update`, `delete`, `stop`, `rerunAnalysis`, `createPhoneCall`, `register` |
307
359
  | `phoneNumbers` | `create`, `list`, `retrieve`, `update`, `assign`, `delete` |
308
360
  | `voices` | `list`, `clone`, `delete` |
309
361
  | `flowTemplates`, `promptTemplates` | `list`, `retrieve` |
362
+ | `llms` | `create`, `list`, `retrieve`, `update`, `delete` |
363
+ | `flows` | `create`, `list`, `retrieve`, `update`, `delete` |
364
+ | `chats` | `create`, `list`, `retrieve`, `update`, `delete`, `sendMessage`, `end`, `rerunAnalysis` |
365
+ | `sessions` | `list`, `update`, `say`, `release` |
366
+ | `apiKey` | `retrieve` |
367
+ | `contacts` | `retrieveByPhone` (and the rest: see the reference) |
310
368
  | `webhooks` | `verify`, `constructEvent`, `constructFunctionCall` |
311
369
 
312
- In-browser web calls are not part of this package.
370
+ In-browser web calls are not part of this package: see [`@dialgem/client`](https://github.com/DialgemAI/dialgem-client-sdk).
313
371
 
314
372
  ## Development
315
373
 
@@ -328,6 +386,19 @@ pnpm gen:types
328
386
  DIALGEM_API_KEY=zk_… pnpm test:live # runs against api.dialgem.com — use a test org's key
329
387
  ```
330
388
 
389
+ The API address is fixed at `https://api.dialgem.com` and `baseURL` is refused,
390
+ so a key can never start hitting production by accident. To exercise the SDK
391
+ against a local or staging stack, pass a `fetch` that rewrites the origin — it
392
+ is explicit, it stays in your test setup, and it cannot be switched on by an
393
+ environment variable someone forgot to unset:
394
+
395
+ ```ts
396
+ const local = new Dialgem({
397
+ apiKey: "zk_local",
398
+ fetch: (url, init) => fetch(String(url).replace("https://api.dialgem.com", "http://localhost:8002"), init),
399
+ })
400
+ ```
401
+
331
402
  Request-body types are generated from `openapi/openapi.json`. Response types in `src/types/` are written by hand, so a change to a response shape needs a matching edit there.
332
403
 
333
404
  To release, bump `version` in `package.json` and `src/version.ts`, add a CHANGELOG entry, merge to `main`, and push a matching tag (`git tag v0.1.0 && git push origin v0.1.0`). A version with a hyphen (`0.2.0-beta.1`) publishes to the `next` dist-tag.
@@ -335,3 +406,11 @@ To release, bump `version` in `package.json` and `src/version.ts`, add a CHANGEL
335
406
  ## License
336
407
 
337
408
  Apache-2.0
409
+
410
+ ---
411
+
412
+ <div align="center">
413
+
414
+ If this SDK saves you time, a ⭐ on [GitHub](https://github.com/DialgemAI/dialgem-sdk-typescript) helps other developers find it.
415
+
416
+ </div>