@osmapi/osmtalk-sdk 0.3.0 → 0.3.1
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/README.md +140 -28
- package/dist/index.d.mts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,11 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Official TypeScript / JavaScript SDK for the [osmTalk](https://osmtalk.com) voice AI platform.
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/@osmapi/osmtalk-sdk)
|
|
6
|
+
|
|
5
7
|
```bash
|
|
6
8
|
npm install @osmapi/osmtalk-sdk
|
|
7
9
|
# or: pnpm add @osmapi/osmtalk-sdk
|
|
8
10
|
```
|
|
9
11
|
|
|
12
|
+
Works in **Node 18+, Deno, Bun, Cloudflare Workers, and browsers**.
|
|
13
|
+
|
|
10
14
|
## Quick start
|
|
11
15
|
|
|
12
16
|
```ts
|
|
@@ -34,11 +38,25 @@ console.log("Call started:", call.callId);
|
|
|
34
38
|
| Resource | Operations |
|
|
35
39
|
|---|---|
|
|
36
40
|
| `client.agents` | `list`, `get`, `create`, `update`, `delete`, `connect`, `publishVersion`, `listVersions`, `getVersion`, `rollbackToVersion` |
|
|
37
|
-
| `client.calls` | `list`, `get`, `outbound`, `end`, `transfer
|
|
41
|
+
| `client.calls` | `list`, `get`, `outbound`, `end`, `transfer`, **`waitUntilEnded`** |
|
|
38
42
|
| `client.campaigns` | `list`, `get`, `create`, `update`, `delete`, `start`, `pause`, `resume`, `stop`, `report`, `uploadLeadsCsv`, `uploadLeads`, `listLeads` |
|
|
39
43
|
| `client.dnc` | `list`, `add`, `bulkAdd`, `remove` |
|
|
40
44
|
| `client.eval` | `simulate`, `createTestCase`, `listTestCases`, `runTestCase`, `runAll`, `listRuns`, `getRun` |
|
|
41
45
|
| `client.settings` | `get`, `getStorage`, `updateStorage`, `getWebhook`, `updateWebhook`, `getCompliance`, `updateCompliance` |
|
|
46
|
+
| `client.platform` | `getRates`, `listProviders`, `getPresets`, `getModelHealth`, `getTemplates`, `getTemplate`, `saveTemplate`, `deleteTemplate` |
|
|
47
|
+
|
|
48
|
+
Plus the standalone helpers `verifyWebhookSignature` / `verifyWebhookSignatureAsync` (see below).
|
|
49
|
+
|
|
50
|
+
## What's new in 0.3.0
|
|
51
|
+
|
|
52
|
+
- **Auto-retry** on 5xx, 429, and network errors with `Retry-After` honored and exponential backoff. No more hand-rolling retry wrappers.
|
|
53
|
+
- **`client.calls.waitUntilEnded(callId)`** — one-line polling helper for the "place call → wait → get result" pattern.
|
|
54
|
+
- **`AbortSignal`** support on every method via `RequestOptions.signal`.
|
|
55
|
+
- **`User-Agent`** header sent automatically.
|
|
56
|
+
- **Per-org request override** via `RequestOptions.organizationId`.
|
|
57
|
+
- **`OsmtalkError.isRetryable` / `.isClientError` / `.retryAttempts`** for cleaner error branching.
|
|
58
|
+
|
|
59
|
+
Full version history: [CHANGELOG.md](./CHANGELOG.md).
|
|
42
60
|
|
|
43
61
|
## Examples
|
|
44
62
|
|
|
@@ -68,6 +86,45 @@ const report = await client.campaigns.report(camp.id);
|
|
|
68
86
|
console.log(report.counts.byStatus);
|
|
69
87
|
```
|
|
70
88
|
|
|
89
|
+
### Wait for a call to finish (without writing a poll loop)
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const { callId } = await client.calls.outbound({
|
|
93
|
+
agentId: "agent_xxx",
|
|
94
|
+
phoneNumberId: "pn_xxx",
|
|
95
|
+
destination: "+919876543210",
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Default: poll every 5s, give up after 30 minutes. All configurable.
|
|
99
|
+
const final = await client.calls.waitUntilEnded(callId, {
|
|
100
|
+
pollIntervalMs: 5_000,
|
|
101
|
+
timeoutMs: 15 * 60 * 1000,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
console.log("Final status:", final.status);
|
|
105
|
+
console.log("Duration: ", final.durationSeconds, "s");
|
|
106
|
+
console.log("Disposition: ", final.disposition);
|
|
107
|
+
console.log("Recording: ", final.recordingUrl);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
For production, prefer webhooks — see the receiver example below.
|
|
111
|
+
|
|
112
|
+
### Cancel an in-flight request
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
const ctrl = new AbortController();
|
|
116
|
+
setTimeout(() => ctrl.abort(), 2_000);
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
await client.agents.list({ signal: ctrl.signal });
|
|
120
|
+
} catch (err) {
|
|
121
|
+
if (ctrl.signal.aborted) console.log("Cancelled by us");
|
|
122
|
+
else throw err;
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`signal`, `timeoutMs`, and `organizationId` are accepted on every method via the trailing `RequestOptions` argument.
|
|
127
|
+
|
|
71
128
|
### Publish a new agent version and A/B test
|
|
72
129
|
|
|
73
130
|
```ts
|
|
@@ -75,7 +132,12 @@ console.log(report.counts.byStatus);
|
|
|
75
132
|
const v2 = await client.agents.publishVersion("agent_xxx", { label: "Tighter qualifier" });
|
|
76
133
|
|
|
77
134
|
// Call with v2 explicitly
|
|
78
|
-
await client.calls.outbound({
|
|
135
|
+
await client.calls.outbound({
|
|
136
|
+
agentId: "agent_xxx",
|
|
137
|
+
phoneNumberId: "pn_xxx",
|
|
138
|
+
destination: "+919876543210",
|
|
139
|
+
agentVersion: v2.version,
|
|
140
|
+
});
|
|
79
141
|
```
|
|
80
142
|
|
|
81
143
|
### Simulate before going live
|
|
@@ -91,33 +153,54 @@ for (const turn of sim.transcript) {
|
|
|
91
153
|
}
|
|
92
154
|
```
|
|
93
155
|
|
|
94
|
-
###
|
|
156
|
+
### Verify webhooks (Node, sync)
|
|
95
157
|
|
|
96
158
|
```ts
|
|
97
|
-
import crypto from "node:crypto";
|
|
98
159
|
import express from "express";
|
|
160
|
+
import { verifyWebhookSignature } from "@osmapi/osmtalk-sdk";
|
|
99
161
|
|
|
100
162
|
const app = express();
|
|
101
|
-
|
|
163
|
+
// IMPORTANT: use express.raw() — NOT express.json(). The signature was
|
|
164
|
+
// computed over the exact bytes; re-serialized JSON will not match.
|
|
165
|
+
app.use("/webhooks/osmtalk", express.raw({ type: "application/json" }));
|
|
102
166
|
|
|
103
167
|
app.post("/webhooks/osmtalk", (req, res) => {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return res.sendStatus(401);
|
|
112
|
-
}
|
|
168
|
+
const ok = verifyWebhookSignature(
|
|
169
|
+
req.body,
|
|
170
|
+
req.header("x-osmtalk-signature"),
|
|
171
|
+
process.env.OSMTALK_WEBHOOK_SECRET!,
|
|
172
|
+
);
|
|
173
|
+
if (!ok) return res.status(401).end();
|
|
174
|
+
|
|
113
175
|
const event = JSON.parse(req.body.toString());
|
|
114
|
-
if (event.event === "
|
|
115
|
-
console.log("
|
|
176
|
+
if (event.event === "call.completed") {
|
|
177
|
+
console.log("Call ended:", event.call.id, event.analysis?.disposition);
|
|
116
178
|
}
|
|
117
179
|
res.json({ ok: true });
|
|
118
180
|
});
|
|
119
181
|
```
|
|
120
182
|
|
|
183
|
+
### Verify webhooks in Workers / Deno / Bun (async, WebCrypto)
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { verifyWebhookSignatureAsync } from "@osmapi/osmtalk-sdk";
|
|
187
|
+
|
|
188
|
+
export default {
|
|
189
|
+
async fetch(req: Request) {
|
|
190
|
+
const raw = await req.text();
|
|
191
|
+
const ok = await verifyWebhookSignatureAsync(
|
|
192
|
+
raw,
|
|
193
|
+
req.headers.get("x-osmtalk-signature"),
|
|
194
|
+
env.OSMTALK_WEBHOOK_SECRET,
|
|
195
|
+
);
|
|
196
|
+
if (!ok) return new Response("invalid signature", { status: 401 });
|
|
197
|
+
const event = JSON.parse(raw);
|
|
198
|
+
// …handle event
|
|
199
|
+
return new Response("ok");
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
121
204
|
## Error handling
|
|
122
205
|
|
|
123
206
|
```ts
|
|
@@ -128,32 +211,61 @@ try {
|
|
|
128
211
|
} catch (err) {
|
|
129
212
|
if (err instanceof OsmtalkError) {
|
|
130
213
|
console.log("HTTP", err.status, err.body);
|
|
214
|
+
console.log("Retries attempted:", err.retryAttempts);
|
|
215
|
+
if (err.isRetryable) console.log("Server might recover — try again later.");
|
|
216
|
+
if (err.isClientError) console.log("Bad input — check err.body.details.");
|
|
131
217
|
} else {
|
|
132
218
|
throw err;
|
|
133
219
|
}
|
|
134
220
|
}
|
|
135
221
|
```
|
|
136
222
|
|
|
137
|
-
| Status | Meaning |
|
|
138
|
-
|
|
139
|
-
| 400 | Validation — `err.body.details` has zod field errors |
|
|
140
|
-
| 401 | Bad API key |
|
|
141
|
-
| 402 | Insufficient credits |
|
|
142
|
-
| 404 | Resource not found |
|
|
143
|
-
|
|
|
144
|
-
|
|
|
223
|
+
| Status | Meaning | `OsmtalkError` flag |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| 400 | Validation — `err.body.details` has zod field errors | `isClientError` |
|
|
226
|
+
| 401 | Bad API key | `isClientError` |
|
|
227
|
+
| 402 | Insufficient credits | `isClientError` |
|
|
228
|
+
| 404 | Resource not found | `isClientError` |
|
|
229
|
+
| 408 | Request timeout | `isRetryable` |
|
|
230
|
+
| 429 | Concurrency or rate limit | `isRetryable` |
|
|
231
|
+
| 5xx | Server error / provider outage | `isRetryable` |
|
|
232
|
+
|
|
233
|
+
The SDK already auto-retries 408/429/5xx and network errors twice by default. Mutating requests (POST/PUT/DELETE) are only retried when you pass `idempotencyKey` so the SDK never silently double-charges.
|
|
145
234
|
|
|
146
235
|
## Options
|
|
147
236
|
|
|
148
237
|
```ts
|
|
149
238
|
new Osmtalk({
|
|
150
|
-
apiKey: "
|
|
151
|
-
baseUrl: "https://api.osmtalk.com",
|
|
152
|
-
timeoutMs: 30_000,
|
|
153
|
-
|
|
239
|
+
apiKey: "osm_live_…",
|
|
240
|
+
baseUrl: "https://api.osmtalk.com", // default
|
|
241
|
+
timeoutMs: 30_000, // per-request, 0 to disable
|
|
242
|
+
maxRetries: 2, // auto-retry count for 5xx/429
|
|
243
|
+
retryInitialDelayMs: 250, // doubles per retry, jittered
|
|
244
|
+
organizationId: "org_xxx", // for multi-org accounts
|
|
245
|
+
defaultHeaders: { "X-Trace-Id": "…" },// added to every request
|
|
246
|
+
fetch: customFetch, // optional, defaults to global fetch
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Per-request overrides:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
await client.calls.outbound(input, {
|
|
254
|
+
idempotencyKey: `dest-${destination}-${date}`,
|
|
255
|
+
signal: controller.signal,
|
|
256
|
+
timeoutMs: 60_000,
|
|
257
|
+
organizationId: "org_yyy",
|
|
154
258
|
});
|
|
155
259
|
```
|
|
156
260
|
|
|
261
|
+
## Runnable examples
|
|
262
|
+
|
|
263
|
+
See [github.com/osm-API/osmtalk-examples](https://github.com/osm-API/osmtalk-examples) for three end-to-end projects:
|
|
264
|
+
|
|
265
|
+
1. **Personalized outbound call** — dynamic per-user prompts
|
|
266
|
+
2. **Bulk campaign from CSV** — scale to thousands
|
|
267
|
+
3. **Verified webhook receiver** — close the loop with `verifyWebhookSignature`
|
|
268
|
+
|
|
157
269
|
## License
|
|
158
270
|
|
|
159
271
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
* dynamicVariables: { first_name: "Arjun" },
|
|
11
11
|
* });
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Current SDK version, sent in the User-Agent header and useful for
|
|
15
|
+
* runtime version checks (e.g. logging which SDK build is talking to the
|
|
16
|
+
* API, or feature-detecting against minimum versions in shared code).
|
|
17
|
+
*/
|
|
18
|
+
declare const SDK_VERSION = "0.3.1";
|
|
15
19
|
interface OsmtalkOptions {
|
|
16
20
|
apiKey: string;
|
|
17
21
|
baseUrl?: string;
|
|
@@ -253,7 +257,15 @@ declare class AgentsResource {
|
|
|
253
257
|
callId: string;
|
|
254
258
|
}>;
|
|
255
259
|
}
|
|
256
|
-
/**
|
|
260
|
+
/**
|
|
261
|
+
* Call statuses that mean "no more state changes are coming" — the
|
|
262
|
+
* record is final and safe to consume. `waitUntilEnded()` polls until
|
|
263
|
+
* the call's status matches one of these.
|
|
264
|
+
*
|
|
265
|
+
* Exported so downstream code can mirror the SDK's definition of "done"
|
|
266
|
+
* without copy-pasting strings (e.g. when reacting to webhook events or
|
|
267
|
+
* filtering call lists in a dashboard).
|
|
268
|
+
*/
|
|
257
269
|
declare const TERMINAL_CALL_STATUSES: readonly ["completed", "failed", "ended", "cancelled"];
|
|
258
270
|
declare class CallsResource {
|
|
259
271
|
private readonly http;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
* dynamicVariables: { first_name: "Arjun" },
|
|
11
11
|
* });
|
|
12
12
|
*/
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Current SDK version, sent in the User-Agent header and useful for
|
|
15
|
+
* runtime version checks (e.g. logging which SDK build is talking to the
|
|
16
|
+
* API, or feature-detecting against minimum versions in shared code).
|
|
17
|
+
*/
|
|
18
|
+
declare const SDK_VERSION = "0.3.1";
|
|
15
19
|
interface OsmtalkOptions {
|
|
16
20
|
apiKey: string;
|
|
17
21
|
baseUrl?: string;
|
|
@@ -253,7 +257,15 @@ declare class AgentsResource {
|
|
|
253
257
|
callId: string;
|
|
254
258
|
}>;
|
|
255
259
|
}
|
|
256
|
-
/**
|
|
260
|
+
/**
|
|
261
|
+
* Call statuses that mean "no more state changes are coming" — the
|
|
262
|
+
* record is final and safe to consume. `waitUntilEnded()` polls until
|
|
263
|
+
* the call's status matches one of these.
|
|
264
|
+
*
|
|
265
|
+
* Exported so downstream code can mirror the SDK's definition of "done"
|
|
266
|
+
* without copy-pasting strings (e.g. when reacting to webhook events or
|
|
267
|
+
* filtering call lists in a dashboard).
|
|
268
|
+
*/
|
|
257
269
|
declare const TERMINAL_CALL_STATUSES: readonly ["completed", "failed", "ended", "cancelled"];
|
|
258
270
|
declare class CallsResource {
|
|
259
271
|
private readonly http;
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
verifyWebhookSignatureAsync: () => verifyWebhookSignatureAsync
|
|
30
30
|
});
|
|
31
31
|
module.exports = __toCommonJS(index_exports);
|
|
32
|
-
var SDK_VERSION = "0.3.
|
|
32
|
+
var SDK_VERSION = "0.3.1";
|
|
33
33
|
var OsmtalkError = class extends Error {
|
|
34
34
|
status;
|
|
35
35
|
body;
|
package/dist/index.mjs
CHANGED