@dialgem/sdk 0.1.0-beta.0 → 0.1.0-beta.2

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,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-beta.2
4
+
5
+ ### Not in this SDK, on purpose
6
+ 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`.
7
+
8
+ ### Breaking changes
9
+ - **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`.
10
+ - **Webhook signatures are timestamped.** `x-dialgem-signature` is now `v=<timestamp_ms>,d=<hex>`, where the HMAC covers the raw body plus the timestamp. `verify` and `constructEvent` also reject signatures more than 5 minutes old; set `toleranceMs` or `now` to change that. Signatures from 0.1.0-beta.0 no longer verify.
11
+ - **Webhook payloads carry the full call record.** Every event is `{ event, call_id, agent_id, call }`. The flat fields (`status`, `duration_s`, `analysis`) now live under `call`.
12
+ - **`calls.list` pages are cursor-based.** A page is `{ data, has_more, pagination_key }`. `page`, `page_size` and `total` are gone; use `limit`.
13
+
14
+ ### Added
15
+ - `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.
16
+
17
+ - `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.
18
+ - **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.
19
+ - `agents.mcpServers.listTools(agentId, serverId)`: the server's tools with descriptions and argument schemas, from a live connection; stores nothing.
20
+ - **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.
21
+ - **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.
22
+ - 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`.
23
+ - `calls.rerunAnalysis(callId)`, `contacts.retrieveByPhone(phone, { default_country })` and `apiKey.retrieve()` (who this key is; works for any scope).
24
+ - **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.
25
+ - **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.
26
+ - `calls.update` (replace metadata), `calls.delete` and `calls.stop`.
27
+ - `metadata` on `createPhoneCall` and `register`.
28
+ - 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`.
29
+ - `calls.list` filters: `agent_id`, `status` (one or several), `direction`, `call_type`, `disconnection_reason`, `from_number`, `to_number`, `user_sentiment`, `call_successful`, `start_after`, `start_before`, `metadata`, `sort_order`.
30
+ - `webhooks.constructFunctionCall` verifies and parses signed requests to your custom functions (`{ name, call, args }`).
31
+ - `AgentFunction.args_at_root`.
32
+ - **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.
33
+ - `phoneNumbers.retrieve(phoneNumber)` looks a number up by its E.164 value.
34
+ - **`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.
35
+ - **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.
36
+ - **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.
37
+ - **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.
38
+
3
39
  ## 0.1.0-beta.0 (unreleased)
4
40
 
5
41
  The first release.
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
 
@@ -37,7 +71,6 @@ const agent = await dialgem.agents.create({
37
71
  | Option | Default | |
38
72
  |---|---|---|
39
73
  | `apiKey` | `process.env.DIALGEM_API_KEY` | Organization API key. |
40
- | `baseURL` | `process.env.DIALGEM_BASE_URL`, then `https://api.dialgem.com` | Point at a self-hosted or local stack. |
41
74
  | `timeout` | `60000` | Milliseconds per attempt. |
42
75
  | `maxRetries` | `2` | See [Retries](#retries). |
43
76
  | `fetch` | global `fetch` | Custom implementation (proxy agent, instrumentation). |
@@ -94,9 +127,18 @@ const { call_id } = await dialgem.calls.createPhoneCall({
94
127
  to: "+14155550199",
95
128
  agent_id: agent.id,
96
129
  dialgem_llm_dynamic_variables: { customer_name: "Sam" },
130
+ metadata: { crm_id: "42" }, // your own data; returned on the call and in every webhook
97
131
  })
98
132
  ```
99
133
 
134
+ Manage it afterwards:
135
+
136
+ ```ts
137
+ await dialgem.calls.update(call_id, { metadata: { crm_id: "42", campaign: "renewals" } })
138
+ await dialgem.calls.stop(call_id) // end it now; disconnection_reason becomes "manual_stopped"
139
+ await dialgem.calls.delete(call_id) // a finished call: record, transcript and recording
140
+ ```
141
+
100
142
  ### 3. Bring your own Twilio number
101
143
 
102
144
  Point your Twilio number's Voice webhook at your backend. Register the call there, then return TwiML that connects Twilio's media stream to Dialgem:
@@ -118,7 +160,13 @@ Your Twilio credentials are never shared with Dialgem. `stream_url` is signed an
118
160
 
119
161
  ### 4. Verify webhooks
120
162
 
121
- Set `webhook_url`, `webhook_events` and `webhook_signing_secret` on the agent. Dialgem then POSTs `call_started`, `call_ended` and `call_analyzed` events, each signed with HMAC-SHA256 in the `x-dialgem-signature` header.
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:
164
+
165
+ ```
166
+ x-dialgem-signature: v=<timestamp_ms>,d=<hex> hex = HMAC-SHA256(secret, raw_body + timestamp_ms)
167
+ ```
168
+
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`.
122
170
 
123
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.
124
172
 
@@ -130,7 +178,7 @@ import { Dialgem, WebhookVerificationError } from "@dialgem/sdk"
130
178
  app.post("/webhooks/dialgem", express.raw({ type: "application/json" }), async (req, res) => {
131
179
  try {
132
180
  const event = await Dialgem.webhooks.constructEvent(req.body, req.header("x-dialgem-signature"), SECRET)
133
- if (event.event === "call_analyzed") await crm.update(event.call_id, event.analysis.variables)
181
+ if (event.event === "call_analyzed") await crm.update(event.call.metadata.crm_id, event.call.analysis?.variables)
134
182
  res.sendStatus(204)
135
183
  } catch (err) {
136
184
  if (err instanceof WebhookVerificationError) return res.status(400).send(err.message)
@@ -155,26 +203,109 @@ export async function POST(request: Request) {
155
203
 
156
204
  **Fastify**: register a buffer parser for `application/json` on the webhook route. See `examples/webhook-fastify.ts`.
157
205
 
158
- `event` is a discriminated union, so `switch (event.event)` narrows the payload type. A failed delivery is retried once, so make your handler idempotent on `(event, call_id)`. Respond within 10 seconds and queue any slow work. `webhooks.verify(body, signature, secret)` returns a boolean if you prefer to handle rejection yourself.
206
+ `event` is a discriminated union, so `switch (event.event)` narrows the payload type. A failed delivery is retried up to three times; every attempt carries the same `x-dialgem-delivery` header, so drop an id you have already processed. Respond within 10 seconds and queue any slow work. `webhooks.verify(body, signature, secret)` returns a boolean if you prefer to handle rejection yourself.
207
+
208
+ **Verifying your custom functions.** When the agent calls one of your functions, the request is signed the same way, and its body is `{ name, call, args }`:
209
+
210
+ ```ts
211
+ app.post("/functions/lookup-order", express.raw({ type: "application/json" }), async (req, res) => {
212
+ const { args, call } = await Dialgem.webhooks.constructFunctionCall<{ order_id: string }>(
213
+ req.body, req.header("x-dialgem-signature"), SECRET,
214
+ )
215
+ res.json(await orders.find(args.order_id, call.metadata.crm_id))
216
+ })
217
+ ```
218
+
219
+ A function created with `args_at_root: true` receives the arguments object alone instead.
159
220
 
160
221
  ### 5. Export calls and transcripts
161
222
 
162
223
  `calls.list()` returns one page when you `await` it. Iterate over it with `for await` to walk every page:
163
224
 
164
225
  ```ts
165
- const cutoff = "2026-09-01T00:00:00"
166
- for await (const call of dialgem.calls.list({ page_size: 50 })) {
167
- if (call.started_at < cutoff) break // newest first: the rest are older
168
- console.log(call.id, call.duration_s, call.analysis?.call_successful, call.transcript)
226
+ const since = Date.parse("2026-09-01T00:00:00Z")
227
+ for await (const call of dialgem.calls.list({ start_after: since, status: "completed", limit: 50 })) {
228
+ console.log(call.id, call.duration_ms, call.analysis?.call_summary, call.transcript_text)
169
229
  }
170
230
 
171
- const page = await dialgem.calls.list({ page_size: 50 })
231
+ const page = await dialgem.calls.list({ agent_id: agent.id, metadata: { crm_id: "42" }, limit: 50 })
172
232
  page.data // Call[]
173
- page.total // number
174
233
  if (page.hasNextPage()) await page.getNextPage()
175
234
  ```
176
235
 
177
- Pages are offset-based and ordered newest first. A call that starts mid-export shifts later pages by one, so de-duplicate on `id` if exactness matters.
236
+ Filters: `agent_id`, `status`, `direction`, `call_type`, `disconnection_reason`, `from_number`, `to_number`, `user_sentiment`, `call_successful`, `start_after` / `start_before` (epoch ms), `metadata`, `search` and `sort_order`. Pages are cursor-based, so walking them is stable while new calls arrive.
237
+
238
+ ### 6. Ship a change without touching live calls
239
+
240
+ The agent you edit is the **draft**. Publishing freezes it — configuration, functions and MCP servers — as a numbered version that never changes afterwards:
241
+
242
+ ```ts
243
+ const v1 = await dialgem.agents.versions.publish(agent.id, { title: "Refund flow" })
244
+ v1.version // 1
245
+
246
+ // Point production at the frozen copy, then keep editing the draft freely.
247
+ await dialgem.phoneNumbers.assign(number.id, { agent_id: agent.id, agent_version: v1.version })
248
+ await dialgem.agents.update(agent.id, { system_prompt: "…work in progress…" })
249
+
250
+ await dialgem.agents.versions.list(agent.id) // one page, newest first
251
+ await dialgem.agents.versions.retrieve(agent.id, 1) // with its snapshot
252
+
253
+ // Nothing prunes versions, so the list is paged. `listPage` keeps `has_more`.
254
+ const { versions, has_more } = await dialgem.agents.versions.listPage(agent.id, { limit: 20 })
255
+ ```
256
+
257
+ A number can also follow the newest publish instead of naming a version, so shipping moves it with nothing to update:
258
+
259
+ ```ts
260
+ await dialgem.phoneNumbers.assign(number.id, {
261
+ agent_id: agent.id,
262
+ agent_version: "latest_published",
263
+ })
264
+ ```
265
+
266
+ A version is also pinnable per call — `createPhoneCall({ …, agent_id, agent_version: 1 })` and `register({ …, agent_version: 1 })` — and the version a call ran comes back on the call record as `agent_version`, so a recording, its transcript and its analysis all name the configuration that produced them. Version `0` everywhere means the draft, which is what an unpinned call runs.
267
+
268
+ ### 7. Give an agent a cloned voice
269
+
270
+ ```ts
271
+ import { readFile } from "node:fs/promises"
272
+
273
+ const voice = await dialgem.voices.clone({
274
+ name: "Ada",
275
+ files: [new Blob([await readFile("./sample.mp3")], { type: "audio/mpeg" })],
276
+ // You are asserting that you hold the rights to this audio and have the
277
+ // speaker's consent. It is recorded against your API identity.
278
+ consent_confirmed: true,
279
+ })
280
+
281
+ await dialgem.agents.update(agent.id, { voice_id: voice.provider_voice_id })
282
+ ```
283
+
284
+ The new voice is private to your organization — it never appears in another
285
+ one's catalog, and `voices.list()` puts yours first. ElevenLabs (the default)
286
+ takes up to 25 files; **Cartesia takes exactly one** and refuses more rather
287
+ than quietly picking one.
288
+
289
+ `voices.delete(voice.id)` removes it at the provider and here. It refuses with a
290
+ 409 while an agent still uses it, rather than leaving that agent without a voice.
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.
178
309
 
179
310
  ## Errors
180
311
 
@@ -223,13 +354,20 @@ The SDK never follows redirects. The API does not issue them, and following one
223
354
  | `agents.functions` | `list`, `create`, `update`, `delete`, `reorder` |
224
355
  | `agents.knowledge` | `list`, `create`, `delete` |
225
356
  | `agents.mcpServers` | `list`, `create`, `update`, `delete`, `test` |
226
- | `calls` | `list`, `retrieve`, `createPhoneCall`, `register` |
227
- | `phoneNumbers` | `create`, `list`, `update`, `assign`, `delete` |
228
- | `voices` | `list` |
357
+ | `agents.versions` | `publish`, `list`, `listPage`, `retrieve` |
358
+ | `calls` | `list`, `retrieve`, `update`, `delete`, `stop`, `rerunAnalysis`, `createPhoneCall`, `register` |
359
+ | `phoneNumbers` | `create`, `list`, `retrieve`, `update`, `assign`, `delete` |
360
+ | `voices` | `list`, `clone`, `delete` |
229
361
  | `flowTemplates`, `promptTemplates` | `list`, `retrieve` |
230
- | `webhooks` | `verify`, `constructEvent` |
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) |
368
+ | `webhooks` | `verify`, `constructEvent`, `constructFunctionCall` |
231
369
 
232
- 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).
233
371
 
234
372
  ## Development
235
373
 
@@ -245,7 +383,20 @@ pnpm sync:openapi --from https://api.dialgem.com/openapi.json
245
383
  pnpm gen:types
246
384
 
247
385
  # Against a running API, with an organization key:
248
- DIALGEM_API_KEY=zk_… DIALGEM_BASE_URL=http://localhost:8002 pnpm test:live
386
+ DIALGEM_API_KEY=zk_… pnpm test:live # runs against api.dialgem.com — use a test org's key
387
+ ```
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
+ })
249
400
  ```
250
401
 
251
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.
@@ -255,3 +406,11 @@ To release, bump `version` in `package.json` and `src/version.ts`, add a CHANGEL
255
406
  ## License
256
407
 
257
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>