nixflex 0.1.0
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 +80 -0
- package/dist/index.cjs +516 -0
- package/dist/index.d.cts +704 -0
- package/dist/index.d.ts +704 -0
- package/dist/index.js +481 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Nixflex Node.js SDK
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for the [Nixflex](https://nixflex.com) voice AI platform — AI phone agents, outbound campaigns, SMS, and web calls.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install nixflex
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import Nixflex from 'nixflex';
|
|
13
|
+
|
|
14
|
+
const client = new Nixflex({ apiKey: 'nxf_xxx:nxfs_xxx' });
|
|
15
|
+
|
|
16
|
+
// Create an agent (every field has a sensible default)
|
|
17
|
+
const agent = await client.agents.create({
|
|
18
|
+
name: 'Acme Dental Receptionist',
|
|
19
|
+
system_prompt: 'You are the friendly front-desk assistant at Acme Dental...',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Attach a number you own (carrier inferred from credentials)
|
|
23
|
+
await client.phoneNumbers.import({
|
|
24
|
+
phone_number: '+447446466847',
|
|
25
|
+
twilio_sid: process.env.TWILIO_SID,
|
|
26
|
+
twilio_token: process.env.TWILIO_TOKEN,
|
|
27
|
+
agent_id: agent.agent_id,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Trigger an outbound AI call (fire-and-forget)
|
|
31
|
+
const call = await client.calls.create({
|
|
32
|
+
agent_id: agent.agent_id,
|
|
33
|
+
to_number: '+447386172392',
|
|
34
|
+
prompt: 'Remind {patient_name} about their appointment on {time}.',
|
|
35
|
+
dynamic_vars: { patient_name: 'Sarah', time: 'Tuesday at 2pm' },
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Resources
|
|
40
|
+
|
|
41
|
+
| Resource | Methods |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `client.agents` | `create` `list` `get` `update` `delete` `iter` |
|
|
44
|
+
| `client.calls` | `create` (outbound) `list` `get` `iter` |
|
|
45
|
+
| `client.campaigns` | `create` `launch` — voice batch campaigns with scheduling windows |
|
|
46
|
+
| `client.phoneNumbers` | `import` `list` `update` `delete` `setMonitor` `getMonitor` `setWebCalls` `getWebCalls` |
|
|
47
|
+
| `client.sms` | `send` + `campaigns.create/launch/list/get/delete` |
|
|
48
|
+
| `client.keys` | `rotate` (Stripe-style: key_id stays, secret rotates) |
|
|
49
|
+
| `client.usage` | `get` — minutes, calls, SMS, balance |
|
|
50
|
+
| `client.webhooks` | `set` `get` `delete` — per-number post-call webhooks (2 slots) |
|
|
51
|
+
| `Nixflex.createKey()` | static — signup without auth |
|
|
52
|
+
|
|
53
|
+
## Errors — typed, catchable by class
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
import { NixflexRateLimitError, NixflexPaymentRequiredError } from 'nixflex';
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await client.calls.create({ ... });
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (e instanceof NixflexRateLimitError) {
|
|
62
|
+
console.log(`Rate limited — retry in ${e.retryAfterSeconds}s`);
|
|
63
|
+
} else if (e instanceof NixflexPaymentRequiredError) {
|
|
64
|
+
console.log('Top up your balance');
|
|
65
|
+
}
|
|
66
|
+
// Every error carries: status, code, type, message, docUrl, details, requestId
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Built in
|
|
71
|
+
|
|
72
|
+
- **Automatic retries** — 429s retried honouring `Retry-After`; network failures retried; POST never blind-retries a 5xx (no double dials)
|
|
73
|
+
- **Pagination iterators** — `for await (const call of client.calls.iter()) { ... }`
|
|
74
|
+
- **TypeScript types for every request and response** — generated from the [API docs](https://docs.nixflex.com)
|
|
75
|
+
- **Timeouts + AbortSignal support** per client or per request
|
|
76
|
+
- **Zero runtime dependencies** — native fetch, Node 18+
|
|
77
|
+
|
|
78
|
+
## Docs
|
|
79
|
+
|
|
80
|
+
Full API reference: **https://docs.nixflex.com**
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
Nixflex: () => Nixflex,
|
|
23
|
+
NixflexAuthenticationError: () => NixflexAuthenticationError,
|
|
24
|
+
NixflexConnectionError: () => NixflexConnectionError,
|
|
25
|
+
NixflexError: () => NixflexError,
|
|
26
|
+
NixflexInvalidRequestError: () => NixflexInvalidRequestError,
|
|
27
|
+
NixflexNotFoundError: () => NixflexNotFoundError,
|
|
28
|
+
NixflexPaymentRequiredError: () => NixflexPaymentRequiredError,
|
|
29
|
+
NixflexRateLimitError: () => NixflexRateLimitError,
|
|
30
|
+
NixflexServerError: () => NixflexServerError,
|
|
31
|
+
default: () => index_default,
|
|
32
|
+
errorFromResponse: () => errorFromResponse
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/errors.ts
|
|
37
|
+
var NixflexError = class extends Error {
|
|
38
|
+
/** HTTP status of the failed response (0 for network/timeout failures). */
|
|
39
|
+
status;
|
|
40
|
+
/** Machine-readable error code, e.g. 'invalid_json', 'rate_limit_exceeded'. */
|
|
41
|
+
code;
|
|
42
|
+
/** Error category from the API, e.g. 'invalid_request', 'rate_limit'. */
|
|
43
|
+
type;
|
|
44
|
+
/** Link to the error's documentation page. */
|
|
45
|
+
docUrl;
|
|
46
|
+
/** Structured extra info the API attached to this error. */
|
|
47
|
+
details;
|
|
48
|
+
/** The x-railway-request-id (or similar) header when present - quote it to support. */
|
|
49
|
+
requestId;
|
|
50
|
+
constructor(status, body, requestId, fallbackMessage) {
|
|
51
|
+
const e = body == null ? void 0 : body.error;
|
|
52
|
+
super((e == null ? void 0 : e.message) || fallbackMessage || `Nixflex API error (HTTP ${status})`);
|
|
53
|
+
this.name = "NixflexError";
|
|
54
|
+
this.status = status;
|
|
55
|
+
this.code = (e == null ? void 0 : e.code) || "unknown_error";
|
|
56
|
+
this.type = (e == null ? void 0 : e.type) || "error";
|
|
57
|
+
this.docUrl = e == null ? void 0 : e.doc_url;
|
|
58
|
+
this.details = (e == null ? void 0 : e.details) || {};
|
|
59
|
+
this.requestId = requestId;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var NixflexAuthenticationError = class extends NixflexError {
|
|
63
|
+
constructor(...args) {
|
|
64
|
+
super(...args);
|
|
65
|
+
this.name = "NixflexAuthenticationError";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var NixflexPaymentRequiredError = class extends NixflexError {
|
|
69
|
+
constructor(...args) {
|
|
70
|
+
super(...args);
|
|
71
|
+
this.name = "NixflexPaymentRequiredError";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var NixflexNotFoundError = class extends NixflexError {
|
|
75
|
+
constructor(...args) {
|
|
76
|
+
super(...args);
|
|
77
|
+
this.name = "NixflexNotFoundError";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var NixflexRateLimitError = class extends NixflexError {
|
|
81
|
+
retryAfterSeconds;
|
|
82
|
+
constructor(status, body, requestId, retryAfterSeconds) {
|
|
83
|
+
super(status, body, requestId);
|
|
84
|
+
this.name = "NixflexRateLimitError";
|
|
85
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var NixflexInvalidRequestError = class extends NixflexError {
|
|
89
|
+
constructor(...args) {
|
|
90
|
+
super(...args);
|
|
91
|
+
this.name = "NixflexInvalidRequestError";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var NixflexServerError = class extends NixflexError {
|
|
95
|
+
constructor(...args) {
|
|
96
|
+
super(...args);
|
|
97
|
+
this.name = "NixflexServerError";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var NixflexConnectionError = class extends NixflexError {
|
|
101
|
+
constructor(message) {
|
|
102
|
+
super(0, null, void 0, message);
|
|
103
|
+
this.name = "NixflexConnectionError";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function errorFromResponse(status, body, requestId, retryAfterSeconds) {
|
|
107
|
+
if (status === 401) return new NixflexAuthenticationError(status, body, requestId);
|
|
108
|
+
if (status === 402) return new NixflexPaymentRequiredError(status, body, requestId);
|
|
109
|
+
if (status === 404) return new NixflexNotFoundError(status, body, requestId);
|
|
110
|
+
if (status === 429) return new NixflexRateLimitError(status, body, requestId, retryAfterSeconds);
|
|
111
|
+
if (status === 400 || status === 422) return new NixflexInvalidRequestError(status, body, requestId);
|
|
112
|
+
if (status >= 500) return new NixflexServerError(status, body, requestId);
|
|
113
|
+
return new NixflexError(status, body, requestId);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/client.ts
|
|
117
|
+
var DEFAULT_BASE_URL = "https://api.nixflex.com";
|
|
118
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
119
|
+
var SDK_VERSION = "0.1.0";
|
|
120
|
+
var HttpClient = class {
|
|
121
|
+
apiKey;
|
|
122
|
+
baseUrl;
|
|
123
|
+
timeoutMs;
|
|
124
|
+
maxRetries;
|
|
125
|
+
constructor(opts) {
|
|
126
|
+
if (!opts || typeof opts.apiKey !== "string" || !opts.apiKey.includes(":")) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
'Nixflex: apiKey is required in the form "key_id:key_secret" (both parts, joined by a colon). Find yours at https://dashboard.nixflex.com under API Keys.'
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
this.apiKey = opts.apiKey;
|
|
132
|
+
this.baseUrl = (opts.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
133
|
+
this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
134
|
+
this.maxRetries = opts.maxRetries ?? 1;
|
|
135
|
+
}
|
|
136
|
+
async request(method, path, body, query, reqOpts) {
|
|
137
|
+
const url = new URL(this.baseUrl + "/v1" + path);
|
|
138
|
+
if (query) {
|
|
139
|
+
for (const [k, v] of Object.entries(query)) {
|
|
140
|
+
if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const isIdempotent = method === "GET" || method === "DELETE";
|
|
144
|
+
let attempt = 0;
|
|
145
|
+
while (true) {
|
|
146
|
+
attempt++;
|
|
147
|
+
let res;
|
|
148
|
+
try {
|
|
149
|
+
res = await this.fetchWithTimeout(url.toString(), method, body, reqOpts);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
if (attempt <= this.maxRetries) {
|
|
152
|
+
await sleep(300 * attempt);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
throw new NixflexConnectionError(
|
|
156
|
+
`Could not reach the Nixflex API (${(err == null ? void 0 : err.message) || "network error"}). Check connectivity and https://nixflex.com/status`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (res.ok) {
|
|
160
|
+
if (res.status === 204) return void 0;
|
|
161
|
+
return await res.json();
|
|
162
|
+
}
|
|
163
|
+
const requestId = res.headers.get("x-railway-request-id") || void 0;
|
|
164
|
+
const retryAfter = parseInt(res.headers.get("retry-after") || "0", 10) || 0;
|
|
165
|
+
let errBody = null;
|
|
166
|
+
try {
|
|
167
|
+
errBody = await res.json();
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
const retryable429 = res.status === 429;
|
|
171
|
+
const retryable5xx = res.status >= 500 && isIdempotent;
|
|
172
|
+
if ((retryable429 || retryable5xx) && attempt <= this.maxRetries) {
|
|
173
|
+
const waitMs = retryable429 ? Math.min(retryAfter, 30) * 1e3 || 1e3 : 500 * attempt;
|
|
174
|
+
await sleep(waitMs);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
throw errorFromResponse(res.status, errBody, requestId, retryAfter);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async fetchWithTimeout(url, method, body, reqOpts) {
|
|
181
|
+
const controller = new AbortController();
|
|
182
|
+
const timeoutMs = (reqOpts == null ? void 0 : reqOpts.timeoutMs) ?? this.timeoutMs;
|
|
183
|
+
const timer = setTimeout(() => controller.abort(new Error(`timeout after ${timeoutMs}ms`)), timeoutMs);
|
|
184
|
+
if (reqOpts == null ? void 0 : reqOpts.signal) {
|
|
185
|
+
if (reqOpts.signal.aborted) controller.abort(reqOpts.signal.reason);
|
|
186
|
+
else reqOpts.signal.addEventListener("abort", () => controller.abort(reqOpts.signal.reason), { once: true });
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
return await fetch(url, {
|
|
190
|
+
method,
|
|
191
|
+
headers: {
|
|
192
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
193
|
+
"Content-Type": "application/json",
|
|
194
|
+
"User-Agent": `nixflex-node/${SDK_VERSION}`
|
|
195
|
+
},
|
|
196
|
+
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
197
|
+
signal: controller.signal
|
|
198
|
+
});
|
|
199
|
+
} finally {
|
|
200
|
+
clearTimeout(timer);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
function sleep(ms) {
|
|
205
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/resources/agents.ts
|
|
209
|
+
var Agents = class {
|
|
210
|
+
constructor(http) {
|
|
211
|
+
this.http = http;
|
|
212
|
+
}
|
|
213
|
+
http;
|
|
214
|
+
/** Create an agent. Every field has a sensible default - `{ name }` alone works.
|
|
215
|
+
* The agent is immediately usable for numbers and outbound calls. */
|
|
216
|
+
create(params = {}, opts) {
|
|
217
|
+
return this.http.request("POST", "/agents", params, void 0, opts);
|
|
218
|
+
}
|
|
219
|
+
/** List active agents, newest first. Default 100, hard cap 200 per page. */
|
|
220
|
+
list(params = {}, opts) {
|
|
221
|
+
return this.http.request("GET", "/agents", void 0, { limit: params.limit, offset: params.offset }, opts);
|
|
222
|
+
}
|
|
223
|
+
/** Iterate ALL agents across pages: `for await (const a of client.agents.iter()) { ... }` */
|
|
224
|
+
async *iter(pageSize = 100, opts) {
|
|
225
|
+
let offset = 0;
|
|
226
|
+
while (true) {
|
|
227
|
+
const page = await this.list({ limit: pageSize, offset }, opts);
|
|
228
|
+
for (const item of page) yield item;
|
|
229
|
+
if (page.length < pageSize) return;
|
|
230
|
+
offset += page.length;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/** Fetch one agent with full configuration. Throws NixflexNotFoundError if the ID is not yours. */
|
|
234
|
+
get(agentId, opts) {
|
|
235
|
+
return this.http.request("GET", `/agents/${encodeURIComponent(agentId)}`, void 0, void 0, opts);
|
|
236
|
+
}
|
|
237
|
+
/** Update an agent. Send ONLY the fields you want to change - omitted fields
|
|
238
|
+
* keep their values. Active calls are unaffected; new calls use the new config.
|
|
239
|
+
* Unknown field names return 200 and change nothing - check spelling. */
|
|
240
|
+
update(agentId, params, opts) {
|
|
241
|
+
return this.http.request("PUT", `/agents/${encodeURIComponent(agentId)}`, params, void 0, opts);
|
|
242
|
+
}
|
|
243
|
+
/** PERMANENTLY delete an agent. Attached numbers detach and stop routing;
|
|
244
|
+
* historical calls stay accessible. To disable without losing setup, use
|
|
245
|
+
* update(agentId, { is_active: false }) instead. */
|
|
246
|
+
delete(agentId, opts) {
|
|
247
|
+
return this.http.request("DELETE", `/agents/${encodeURIComponent(agentId)}`, void 0, void 0, opts);
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// src/resources/calls.ts
|
|
252
|
+
var Calls = class {
|
|
253
|
+
constructor(http) {
|
|
254
|
+
this.http = http;
|
|
255
|
+
}
|
|
256
|
+
http;
|
|
257
|
+
/** Trigger an outbound AI call. FIRE-AND-FORGET: returns immediately with a
|
|
258
|
+
* call_id; ringing, conversation and ending happen asynchronously. Set a
|
|
259
|
+
* webhook_url on the agent or number to receive call.completed when it ends.
|
|
260
|
+
* The dialling number must be OUTBOUND-ENABLED (inbound working does not
|
|
261
|
+
* mean outbound works - separate switches). */
|
|
262
|
+
create(params, opts) {
|
|
263
|
+
return this.http.request("POST", "/calls/outbound", params, void 0, opts);
|
|
264
|
+
}
|
|
265
|
+
/** List calls, newest first. Default 50, hard cap 200. The response is a
|
|
266
|
+
* bare array with no total - page until you get fewer rows than you asked
|
|
267
|
+
* for (or use iter()). Call data is retained 90 days. */
|
|
268
|
+
list(params = {}, opts) {
|
|
269
|
+
return this.http.request("GET", "/calls", void 0, { limit: params.limit, offset: params.offset }, opts);
|
|
270
|
+
}
|
|
271
|
+
/** Iterate ALL calls across pages: `for await (const c of client.calls.iter()) { ... }` */
|
|
272
|
+
async *iter(pageSize = 100, opts) {
|
|
273
|
+
let offset = 0;
|
|
274
|
+
while (true) {
|
|
275
|
+
const page = await this.list({ limit: pageSize, offset }, opts);
|
|
276
|
+
for (const item of page) yield item;
|
|
277
|
+
if (page.length < pageSize) return;
|
|
278
|
+
offset += page.length;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/** Fetch one call: transcript, recording URL, post-call analysis. Note:
|
|
282
|
+
* duration_ms is time CONNECTED (what you are billed on) - ring time is
|
|
283
|
+
* excluded, so it will not equal end_timestamp - start_timestamp on
|
|
284
|
+
* outbound calls. */
|
|
285
|
+
get(callId, opts) {
|
|
286
|
+
return this.http.request("GET", `/calls/${encodeURIComponent(callId)}`, void 0, void 0, opts);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
var Campaigns = class {
|
|
290
|
+
constructor(http) {
|
|
291
|
+
this.http = http;
|
|
292
|
+
}
|
|
293
|
+
http;
|
|
294
|
+
/** Create a batch campaign - many outbound calls under one campaign_id.
|
|
295
|
+
* schedule_type 'now' launches immediately; 'schedule' waits for
|
|
296
|
+
* scheduled_date and fires INSIDE the calling window in the campaign's
|
|
297
|
+
* timezone (yours > the agent's > Europe/London). Overnight windows
|
|
298
|
+
* supported. One campaign = one timezone - split multi-country lists. */
|
|
299
|
+
create(params, opts) {
|
|
300
|
+
return this.http.request("POST", "/calls/batch", params, void 0, opts);
|
|
301
|
+
}
|
|
302
|
+
/** Launch a scheduled campaign immediately, overriding its schedule.
|
|
303
|
+
* Already running = no-op returning current status. */
|
|
304
|
+
launch(campaignId, opts) {
|
|
305
|
+
return this.http.request("POST", `/calls/batch/${encodeURIComponent(campaignId)}/launch`, void 0, void 0, opts);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// src/resources/stage3.ts
|
|
310
|
+
function enc(phoneNumber) {
|
|
311
|
+
return encodeURIComponent(phoneNumber);
|
|
312
|
+
}
|
|
313
|
+
var PhoneNumbers = class {
|
|
314
|
+
constructor(http) {
|
|
315
|
+
this.http = http;
|
|
316
|
+
}
|
|
317
|
+
http;
|
|
318
|
+
/** Attach a number you already own. Carrier inferred from credentials
|
|
319
|
+
* (twilio_sid+twilio_token OR telnyx_api_key+telnyx_connection_id).
|
|
320
|
+
* WARNING: Twilio import REPLACES the number's existing webhooks - another
|
|
321
|
+
* system using this number stops receiving calls and SMS. */
|
|
322
|
+
import(params, opts) {
|
|
323
|
+
return this.http.request("POST", "/phone-numbers", params, void 0, opts);
|
|
324
|
+
}
|
|
325
|
+
/** All numbers on your account (optionally one agent's), newest first.
|
|
326
|
+
* Not paginated - returns up to 1,000; filter by agent_id above that. */
|
|
327
|
+
list(params = {}, opts) {
|
|
328
|
+
return this.http.request("GET", "/phone-numbers", void 0, { agent_id: params.agent_id }, opts);
|
|
329
|
+
}
|
|
330
|
+
/** Update per-number settings. Send ONLY what changes (empty body is
|
|
331
|
+
* rejected). null clears/inherits - see each field's JSDoc; speaking_rate
|
|
332
|
+
* null INHERITS the agent's speed (send 1, not null, to force normal). */
|
|
333
|
+
update(phoneNumber, params, opts) {
|
|
334
|
+
return this.http.request("PATCH", `/phone-numbers/${enc(phoneNumber)}`, params, void 0, opts);
|
|
335
|
+
}
|
|
336
|
+
/** Disconnect from Nixflex (clears carrier webhooks + our record). Does NOT
|
|
337
|
+
* release the number from your carrier - carrier billing continues until
|
|
338
|
+
* you release it in Twilio/Telnyx yourself. Reversible by re-importing. */
|
|
339
|
+
delete(phoneNumber, opts) {
|
|
340
|
+
return this.http.request("DELETE", `/phone-numbers/${enc(phoneNumber)}`, void 0, void 0, opts);
|
|
341
|
+
}
|
|
342
|
+
/** Turn live call monitoring on/off for a number (off by default).
|
|
343
|
+
* NOTE: enabling bills that number's inbound calls at $0.09/min. */
|
|
344
|
+
setMonitor(phoneNumber, enabled, opts) {
|
|
345
|
+
return this.http.request("PUT", `/integrations/monitor/number/${enc(phoneNumber)}`, { enabled }, void 0, opts);
|
|
346
|
+
}
|
|
347
|
+
/** Current monitoring state for a number. */
|
|
348
|
+
getMonitor(phoneNumber, opts) {
|
|
349
|
+
return this.http.request("GET", `/integrations/monitor/number/${enc(phoneNumber)}`, void 0, void 0, opts);
|
|
350
|
+
}
|
|
351
|
+
/** Enable/disable browser (web) calls for a number (off by default).
|
|
352
|
+
* NOTE: enabling bills that number's calls at $0.09/min (same rule as
|
|
353
|
+
* Live Monitor - either on means 0.09, both on is still 0.09). */
|
|
354
|
+
setWebCalls(phoneNumber, enabled, opts) {
|
|
355
|
+
return this.http.request("PUT", `/integrations/web-calls/number/${enc(phoneNumber)}`, { enabled }, void 0, opts);
|
|
356
|
+
}
|
|
357
|
+
/** Current web-calls state for a number. */
|
|
358
|
+
getWebCalls(phoneNumber, opts) {
|
|
359
|
+
return this.http.request("GET", `/integrations/web-calls/number/${enc(phoneNumber)}`, void 0, void 0, opts);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
var SmsCampaigns = class {
|
|
363
|
+
constructor(http) {
|
|
364
|
+
this.http = http;
|
|
365
|
+
}
|
|
366
|
+
http;
|
|
367
|
+
/** Create a one-time SMS broadcast ({{variable}} templating per recipient,
|
|
368
|
+
* up to 10,000 recipients). Requires a TWILIO from_number - Telnyx numbers
|
|
369
|
+
* are rejected at create (single sends support both carriers). */
|
|
370
|
+
create(params, opts) {
|
|
371
|
+
return this.http.request("POST", "/sms/campaigns", params, void 0, opts);
|
|
372
|
+
}
|
|
373
|
+
/** Launch a draft/scheduled campaign immediately. */
|
|
374
|
+
launch(campaignId, opts) {
|
|
375
|
+
return this.http.request("POST", `/sms/campaigns/${encodeURIComponent(campaignId)}/launch`, void 0, void 0, opts);
|
|
376
|
+
}
|
|
377
|
+
/** All campaigns, newest first, with live-computed delivery counts. */
|
|
378
|
+
list(params = {}, opts) {
|
|
379
|
+
return this.http.request("GET", "/sms/campaigns", void 0, { status: params.status, limit: params.limit }, opts);
|
|
380
|
+
}
|
|
381
|
+
/** One campaign with per-recipient statuses. Twilio 'sent' (handed to
|
|
382
|
+
* carrier, never confirmed on the handset) counts as FAILED, not delivered. */
|
|
383
|
+
get(campaignId, opts) {
|
|
384
|
+
return this.http.request("GET", `/sms/campaigns/${encodeURIComponent(campaignId)}`, void 0, void 0, opts);
|
|
385
|
+
}
|
|
386
|
+
/** Cancel a scheduled/running campaign. Already-sent messages cannot be
|
|
387
|
+
* recalled; cancelled_count = pending recipients that will not be messaged. */
|
|
388
|
+
delete(campaignId, opts) {
|
|
389
|
+
return this.http.request("DELETE", `/sms/campaigns/${encodeURIComponent(campaignId)}`, void 0, void 0, opts);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
var Sms = class {
|
|
393
|
+
constructor(http) {
|
|
394
|
+
this.http = http;
|
|
395
|
+
this.campaigns = new SmsCampaigns(http);
|
|
396
|
+
}
|
|
397
|
+
http;
|
|
398
|
+
/** SMS campaigns - one-time bulk broadcasts. */
|
|
399
|
+
campaigns;
|
|
400
|
+
/** Send a single SMS from one of your numbers (both carriers). NOTE: this
|
|
401
|
+
* endpoint uses `to` - NOT to_number (that is the outbound-call field).
|
|
402
|
+
* Replies are answered automatically by the agent's prompt. */
|
|
403
|
+
send(params, opts) {
|
|
404
|
+
return this.http.request("POST", "/sms", params, void 0, opts);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
var Keys = class {
|
|
408
|
+
constructor(http) {
|
|
409
|
+
this.http = http;
|
|
410
|
+
}
|
|
411
|
+
http;
|
|
412
|
+
/** Rotate the key SECRET (key_id stays stable - the Stripe model). The old
|
|
413
|
+
* secret stops working THE INSTANT this returns; the new one is shown ONCE.
|
|
414
|
+
* Update every deployed app before rotating in production. */
|
|
415
|
+
rotate(opts) {
|
|
416
|
+
return this.http.request("POST", "/keys/rotate", void 0, void 0, opts);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
var UsageResource = class {
|
|
420
|
+
constructor(http) {
|
|
421
|
+
this.http = http;
|
|
422
|
+
}
|
|
423
|
+
http;
|
|
424
|
+
/** Usage + balance: calls, minutes, SMS, credit. minutes is fractional;
|
|
425
|
+
* cost_usd excludes carrier charges (Twilio/Telnyx bill you directly). */
|
|
426
|
+
get(opts) {
|
|
427
|
+
return this.http.request("GET", "/usage", void 0, void 0, opts);
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var Webhooks = class {
|
|
431
|
+
constructor(http) {
|
|
432
|
+
this.http = http;
|
|
433
|
+
}
|
|
434
|
+
http;
|
|
435
|
+
/** Point a number's post-call events at your HTTPS endpoint.
|
|
436
|
+
* slot 2 = the second destination (webhook2). */
|
|
437
|
+
set(phoneNumber, url, slot = 1, opts) {
|
|
438
|
+
const base = slot === 2 ? "webhook2" : "webhook";
|
|
439
|
+
return this.http.request("PUT", `/integrations/${base}/number/${encodeURIComponent(phoneNumber)}`, { url }, void 0, opts);
|
|
440
|
+
}
|
|
441
|
+
/** Read the webhook configured on a number. */
|
|
442
|
+
get(phoneNumber, slot = 1, opts) {
|
|
443
|
+
const base = slot === 2 ? "webhook2" : "webhook";
|
|
444
|
+
return this.http.request("GET", `/integrations/${base}/number/${encodeURIComponent(phoneNumber)}`, void 0, void 0, opts);
|
|
445
|
+
}
|
|
446
|
+
/** Remove the webhook from a number. */
|
|
447
|
+
delete(phoneNumber, slot = 1, opts) {
|
|
448
|
+
const base = slot === 2 ? "webhook2" : "webhook";
|
|
449
|
+
return this.http.request("DELETE", `/integrations/${base}/number/${encodeURIComponent(phoneNumber)}`, void 0, void 0, opts);
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// src/index.ts
|
|
454
|
+
var Nixflex = class {
|
|
455
|
+
/** AI agents - create, list, get, update, delete. */
|
|
456
|
+
agents;
|
|
457
|
+
/** Calls - trigger outbound, list history, fetch transcripts + analysis. */
|
|
458
|
+
calls;
|
|
459
|
+
/** Voice batch campaigns - many calls under one campaign, scheduling windows. */
|
|
460
|
+
campaigns;
|
|
461
|
+
/** Phone numbers - import (Twilio/Telnyx), settings, monitor + web-calls toggles. */
|
|
462
|
+
phoneNumbers;
|
|
463
|
+
/** SMS - single sends + bulk campaigns. */
|
|
464
|
+
sms;
|
|
465
|
+
/** API key management - rotate the secret. */
|
|
466
|
+
keys;
|
|
467
|
+
/** Usage + balance. */
|
|
468
|
+
usage;
|
|
469
|
+
/** Per-number post-call webhook configuration. */
|
|
470
|
+
webhooks;
|
|
471
|
+
constructor(options) {
|
|
472
|
+
const http = new HttpClient(options);
|
|
473
|
+
this.agents = new Agents(http);
|
|
474
|
+
this.calls = new Calls(http);
|
|
475
|
+
this.campaigns = new Campaigns(http);
|
|
476
|
+
this.phoneNumbers = new PhoneNumbers(http);
|
|
477
|
+
this.sms = new Sms(http);
|
|
478
|
+
this.keys = new Keys(http);
|
|
479
|
+
this.usage = new UsageResource(http);
|
|
480
|
+
this.webhooks = new Webhooks(http);
|
|
481
|
+
}
|
|
482
|
+
/** Create a brand-new API key (unauthenticated signup endpoint - most
|
|
483
|
+
* developers use the dashboard instead). The key_secret is shown ONCE.
|
|
484
|
+
* Rate-limited per IP to prevent abuse. */
|
|
485
|
+
static async createKey(params = {}, baseUrl = "https://api.nixflex.com") {
|
|
486
|
+
const res = await fetch(baseUrl.replace(/\/+$/, "") + "/v1/keys", {
|
|
487
|
+
method: "POST",
|
|
488
|
+
headers: { "Content-Type": "application/json" },
|
|
489
|
+
body: JSON.stringify(params)
|
|
490
|
+
});
|
|
491
|
+
const body = await res.json();
|
|
492
|
+
if (!res.ok) {
|
|
493
|
+
throw errorFromResponse(
|
|
494
|
+
res.status,
|
|
495
|
+
body,
|
|
496
|
+
res.headers.get("x-railway-request-id") || void 0,
|
|
497
|
+
parseInt(res.headers.get("retry-after") || "0", 10) || 0
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
return body;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
var index_default = Nixflex;
|
|
504
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
505
|
+
0 && (module.exports = {
|
|
506
|
+
Nixflex,
|
|
507
|
+
NixflexAuthenticationError,
|
|
508
|
+
NixflexConnectionError,
|
|
509
|
+
NixflexError,
|
|
510
|
+
NixflexInvalidRequestError,
|
|
511
|
+
NixflexNotFoundError,
|
|
512
|
+
NixflexPaymentRequiredError,
|
|
513
|
+
NixflexRateLimitError,
|
|
514
|
+
NixflexServerError,
|
|
515
|
+
errorFromResponse
|
|
516
|
+
});
|