@nextxai/sdk 0.2.1-beta.29 → 0.3.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/CHANGELOG.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  Theo [Semantic Versioning](https://semver.org/lang/vi/). Chính sách phiên bản: [VERSIONING.md](VERSIONING.md).
4
4
 
5
- ## 0.2.1 (2026-10-31)
5
+ ## 0.3.0 (2026-10-31)
6
6
 
7
7
  ### Thêm mới
8
8
 
9
+ - Kiểm chữ ký webhook theo chuẩn Standard Webhooks: Node `@nextxai/sdk/webhooks` (`verifyWebhook`, `signWebhook`, kiểu sự kiện `call.*` · `agent.status_changed`), Python `nextx_sdk.webhooks` (`verify_webhook`, `sign_webhook`).
10
+ - `NextXPhone.answer()`: nhấc cuộc đang đổ chuông từ phần mềm của bạn (ví dụ nút "Nghe" trên màn khách hàng khi nhận sự kiện `incoming`). Chuyển máy dùng REST `POST /api/v1/call/partner/calls/{id}/transfer`.
9
11
  - `GET /api/v1/call/calls/{id}/transcript`: Transcript STT
10
12
  - `GET /api/v1/call/calls/{id}/summary`: AI summary + sentiment
11
13
  - `POST /api/v1/call/partner/calls`: Bấm gọi thay tổng đài viên (click-to-call)
package/README.md CHANGED
@@ -31,6 +31,32 @@ for (const c of data.items ?? []) console.log(c.fullName);
31
31
  const nx = createNextXClient({ accessToken: () => tokenStore.current() });
32
32
  ```
33
33
 
34
+ ## Nhận webhook
35
+
36
+ Kiểm chữ ký theo chuẩn [Standard Webhooks](https://www.standardwebhooks.com) (chỉ chạy trên máy chủ). Truyền thân gốc, không phải JSON đã parse:
37
+
38
+ ```ts
39
+ import { verifyWebhook } from "@nextxai/sdk/webhooks";
40
+
41
+ app.post("/nextx/webhook", express.raw({ type: "application/json" }), (req, res) => {
42
+ const event = verifyWebhook(req.body, req.headers, process.env.NEXTX_WEBHOOK_SECRET!);
43
+ if (event.type === "call.completed") console.log(event.data.object.talkSec, event.data.object.contactRef);
44
+ res.sendStatus(204);
45
+ });
46
+ ```
47
+
48
+ Sai chữ ký hoặc lệch giờ quá 5 phút thì ném `WebhookVerificationError`. Dùng `event.id` để bỏ qua lần giao trùng.
49
+
50
+ ## Softphone nhúng (trình duyệt)
51
+
52
+ ```ts
53
+ import { NextXPhone } from "@nextxai/sdk/phone";
54
+
55
+ const phone = NextXPhone.init({ clientId: "nxa_…" });
56
+ phone.on("incoming", (c) => openCustomer(c.from));
57
+ phone.call("0901234567"); // phone.answer() · phone.hangup()
58
+ ```
59
+
34
60
  ## Ghi chú
35
61
 
36
62
  - Mặc định client gọi môi trường production. Khi thử nghiệm, dùng `SANDBOX_URL` với khoá sandbox.
@@ -73,6 +73,8 @@ export declare class NextXPhone {
73
73
  call(number: string, opts?: {
74
74
  contactRef?: string;
75
75
  }): void;
76
+ /** Nhấc cuộc đang đổ chuông (sự kiện `incoming`) — y như tổng đài viên bấm "Nghe" trong khung. */
77
+ answer(): void;
76
78
  /** Gác cuộc gọi đang diễn ra. */
77
79
  hangup(): void;
78
80
  /** Đăng xuất NextX trong khung softphone. */
package/dist/cjs/phone.js CHANGED
@@ -100,6 +100,10 @@ class NextXPhone {
100
100
  call(number, opts = {}) {
101
101
  this.send({ type: "call", number: String(number), contactRef: opts.contactRef });
102
102
  }
103
+ /** Nhấc cuộc đang đổ chuông (sự kiện `incoming`) — y như tổng đài viên bấm "Nghe" trong khung. */
104
+ answer() {
105
+ this.send({ type: "answer" });
106
+ }
103
107
  /** Gác cuộc gọi đang diễn ra. */
104
108
  hangup() {
105
109
  this.send({ type: "hangup" });
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.1-beta.29";
1
+ export declare const VERSION = "0.3.0";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = "0.2.1-beta.29";
4
+ exports.VERSION = "0.3.0";
@@ -0,0 +1,92 @@
1
+ /** Hai lần giao cách nhau quá ngưỡng này (giây) bị coi là phát lại. */
2
+ export declare const DEFAULT_TOLERANCE_SEC = 300;
3
+ /** Tổng đài viên trong dữ liệu công khai. */
4
+ export interface WebhookAgent {
5
+ id: string;
6
+ userId: string;
7
+ name: string | null;
8
+ email: string | null;
9
+ extension: string;
10
+ }
11
+ /** `data.object` của các sự kiện `call.*` — cùng hình dạng với `GET /api/v1/call/partner/calls/{id}`. */
12
+ export interface WebhookCall {
13
+ callId: string;
14
+ /** 1 = ringing · 2 = answered · 3 = completed/missed · 4 = recording_ready · 5 = summary_ready — sắp xếp khi tới lệch thứ tự. */
15
+ sequence: number;
16
+ /** queued · initiated · ringing · in-progress · completed · busy · no-answer · failed · canceled · abandoned */
17
+ status: string;
18
+ direction: "inbound" | "outbound";
19
+ /** E.164 khi chuẩn hoá được. */
20
+ from: string;
21
+ to: string;
22
+ agent: WebhookAgent | null;
23
+ queueId: string | null;
24
+ queueName: string | null;
25
+ startedAt: string | null;
26
+ answeredAt: string | null;
27
+ endedAt: string | null;
28
+ durationSec: number | null;
29
+ talkSec: number | null;
30
+ waitSec: number | null;
31
+ /** Mã Q.850 (16 = gác bình thường, 17 = bận, 19 = không trả lời, 41 = lỗi tạm thời…). */
32
+ hangupCause: number | null;
33
+ hangupCauseName: string | null;
34
+ /** `contactRef` bạn gửi lúc gọi ra — trả lại nguyên văn. */
35
+ contactRef: string | null;
36
+ /** `metadata` bạn gửi lúc gọi ra — trả lại nguyên văn. */
37
+ metadata: Record<string, string> | null;
38
+ crmContactId: string | null;
39
+ hasRecording: boolean;
40
+ hasSummary: boolean;
41
+ /** Tóm tắt AI — có từ sự kiện `call.summary_ready` (null trước đó). */
42
+ summary: string | null;
43
+ }
44
+ /** `data.object` của `agent.status_changed`. */
45
+ export interface WebhookAgentStatus {
46
+ agent: WebhookAgent;
47
+ from: string;
48
+ to: string;
49
+ reason: string | null;
50
+ changedAt: string;
51
+ }
52
+ export interface WebhookObjectMap {
53
+ "call.ringing": WebhookCall;
54
+ "call.answered": WebhookCall;
55
+ "call.completed": WebhookCall;
56
+ "call.missed": WebhookCall;
57
+ "call.recording_ready": WebhookCall;
58
+ "call.summary_ready": WebhookCall;
59
+ "agent.status_changed": WebhookAgentStatus;
60
+ }
61
+ interface Envelope<K extends string, T> {
62
+ id: string;
63
+ type: K;
64
+ api_version: string;
65
+ created_at: string;
66
+ workspace_id: string;
67
+ livemode: boolean;
68
+ data: {
69
+ object: T;
70
+ };
71
+ metadata?: Record<string, unknown>;
72
+ }
73
+ /** Sự kiện đã kiểm chữ ký. Thu hẹp kiểu bằng `event.type`. Loại sự kiện khác (CRM, Bán hàng…) có `data.object` là `unknown`. */
74
+ export type WebhookEvent = {
75
+ [K in keyof WebhookObjectMap]: Envelope<K, WebhookObjectMap[K]>;
76
+ }[keyof WebhookObjectMap] | Envelope<string & {}, unknown>;
77
+ export declare class WebhookVerificationError extends Error {
78
+ constructor(message: string);
79
+ }
80
+ type HeaderBag = Headers | Record<string, string | string[] | undefined>;
81
+ /** Ký một thân webhook — dùng để viết test cho phần nhận webhook của bạn. */
82
+ export declare function signWebhook(secret: string, id: string, timestamp: number, body: string | Buffer): string;
83
+ /**
84
+ * Kiểm `webhook-id` / `webhook-timestamp` / `webhook-signature` rồi trả sự kiện đã parse.
85
+ * Ném {@link WebhookVerificationError} khi thiếu header, chữ ký sai, hoặc lệch giờ quá `toleranceSec`.
86
+ * Dùng `webhook-id` (cũng là `event.id`) để bỏ qua lần giao trùng — NextX giao lại khi bạn không trả 2xx.
87
+ */
88
+ export declare function verifyWebhook(body: string | Buffer | Uint8Array, headers: HeaderBag, secret: string, options?: {
89
+ toleranceSec?: number;
90
+ now?: Date;
91
+ }): WebhookEvent;
92
+ export {};
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebhookVerificationError = exports.DEFAULT_TOLERANCE_SEC = void 0;
4
+ exports.signWebhook = signWebhook;
5
+ exports.verifyWebhook = verifyWebhook;
6
+ /// <reference types="node" />
7
+ /**
8
+ * Kiểm chữ ký webhook NextX theo chuẩn mở Standard Webhooks (https://www.standardwebhooks.com) — chỉ chạy trên máy chủ.
9
+ *
10
+ * import { verifyWebhook } from "@nextxai/sdk/webhooks";
11
+ *
12
+ * app.post("/nextx/webhook", express.raw({ type: "application/json" }), (req, res) => {
13
+ * const event = verifyWebhook(req.body, req.headers, process.env.NEXTX_WEBHOOK_SECRET!);
14
+ * if (event.type === "call.completed") saveCall(event.data.object);
15
+ * res.sendStatus(204);
16
+ * });
17
+ *
18
+ * Phải truyền THÂN GỐC (Buffer/chuỗi đúng từng byte) — JSON đã parse rồi stringify lại sẽ lệch chữ ký.
19
+ * Bí mật dạng `whsec_…` lấy ở developers.nextx.vn › Webhooks (xoay khoá ⇒ header có nhiều chữ ký, khớp một là đủ).
20
+ */
21
+ const node_crypto_1 = require("node:crypto");
22
+ /** Hai lần giao cách nhau quá ngưỡng này (giây) bị coi là phát lại. */
23
+ exports.DEFAULT_TOLERANCE_SEC = 300;
24
+ class WebhookVerificationError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "WebhookVerificationError";
28
+ }
29
+ }
30
+ exports.WebhookVerificationError = WebhookVerificationError;
31
+ function header(headers, name) {
32
+ if (typeof headers.get === "function")
33
+ return headers.get(name) ?? undefined;
34
+ const bag = headers;
35
+ const hit = Object.keys(bag).find((k) => k.toLowerCase() === name);
36
+ const v = hit === undefined ? undefined : bag[hit];
37
+ return Array.isArray(v) ? v[0] : v;
38
+ }
39
+ function keyOf(secret) {
40
+ if (!secret?.startsWith("whsec_"))
41
+ throw new WebhookVerificationError("secret phải có dạng whsec_…");
42
+ const key = Buffer.from(secret.slice(6), "base64");
43
+ if (key.length < 24 || key.length > 64)
44
+ throw new WebhookVerificationError("secret không hợp lệ");
45
+ return key;
46
+ }
47
+ /** Ký một thân webhook — dùng để viết test cho phần nhận webhook của bạn. */
48
+ function signWebhook(secret, id, timestamp, body) {
49
+ const mac = (0, node_crypto_1.createHmac)("sha256", keyOf(secret));
50
+ mac.update(`${id}.${timestamp}.`);
51
+ mac.update(body);
52
+ return `v1,${mac.digest("base64")}`;
53
+ }
54
+ /**
55
+ * Kiểm `webhook-id` / `webhook-timestamp` / `webhook-signature` rồi trả sự kiện đã parse.
56
+ * Ném {@link WebhookVerificationError} khi thiếu header, chữ ký sai, hoặc lệch giờ quá `toleranceSec`.
57
+ * Dùng `webhook-id` (cũng là `event.id`) để bỏ qua lần giao trùng — NextX giao lại khi bạn không trả 2xx.
58
+ */
59
+ function verifyWebhook(body, headers, secret, options = {}) {
60
+ const id = header(headers, "webhook-id");
61
+ const ts = header(headers, "webhook-timestamp");
62
+ const sig = header(headers, "webhook-signature");
63
+ if (!id || !ts || !sig)
64
+ throw new WebhookVerificationError("thiếu header webhook-id / webhook-timestamp / webhook-signature");
65
+ const timestamp = Number(ts);
66
+ const now = Math.floor((options.now ?? new Date()).getTime() / 1000);
67
+ if (!Number.isInteger(timestamp) || Math.abs(now - timestamp) > (options.toleranceSec ?? exports.DEFAULT_TOLERANCE_SEC))
68
+ throw new WebhookVerificationError("webhook-timestamp lệch quá ngưỡng");
69
+ const raw = typeof body === "string" ? Buffer.from(body, "utf8") : Buffer.from(body);
70
+ const expected = Buffer.from(signWebhook(secret, id, timestamp, raw));
71
+ const ok = sig
72
+ .split(" ")
73
+ .filter(Boolean)
74
+ .some((s) => {
75
+ const candidate = Buffer.from(s);
76
+ return candidate.length === expected.length && (0, node_crypto_1.timingSafeEqual)(candidate, expected);
77
+ });
78
+ if (!ok)
79
+ throw new WebhookVerificationError("chữ ký không khớp");
80
+ return JSON.parse(raw.toString("utf8"));
81
+ }
@@ -73,6 +73,8 @@ export declare class NextXPhone {
73
73
  call(number: string, opts?: {
74
74
  contactRef?: string;
75
75
  }): void;
76
+ /** Nhấc cuộc đang đổ chuông (sự kiện `incoming`) — y như tổng đài viên bấm "Nghe" trong khung. */
77
+ answer(): void;
76
78
  /** Gác cuộc gọi đang diễn ra. */
77
79
  hangup(): void;
78
80
  /** Đăng xuất NextX trong khung softphone. */
package/dist/esm/phone.js CHANGED
@@ -97,6 +97,10 @@ export class NextXPhone {
97
97
  call(number, opts = {}) {
98
98
  this.send({ type: "call", number: String(number), contactRef: opts.contactRef });
99
99
  }
100
+ /** Nhấc cuộc đang đổ chuông (sự kiện `incoming`) — y như tổng đài viên bấm "Nghe" trong khung. */
101
+ answer() {
102
+ this.send({ type: "answer" });
103
+ }
100
104
  /** Gác cuộc gọi đang diễn ra. */
101
105
  hangup() {
102
106
  this.send({ type: "hangup" });
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.1-beta.29";
1
+ export declare const VERSION = "0.3.0";
@@ -1 +1 @@
1
- export const VERSION = "0.2.1-beta.29";
1
+ export const VERSION = "0.3.0";
@@ -0,0 +1,92 @@
1
+ /** Hai lần giao cách nhau quá ngưỡng này (giây) bị coi là phát lại. */
2
+ export declare const DEFAULT_TOLERANCE_SEC = 300;
3
+ /** Tổng đài viên trong dữ liệu công khai. */
4
+ export interface WebhookAgent {
5
+ id: string;
6
+ userId: string;
7
+ name: string | null;
8
+ email: string | null;
9
+ extension: string;
10
+ }
11
+ /** `data.object` của các sự kiện `call.*` — cùng hình dạng với `GET /api/v1/call/partner/calls/{id}`. */
12
+ export interface WebhookCall {
13
+ callId: string;
14
+ /** 1 = ringing · 2 = answered · 3 = completed/missed · 4 = recording_ready · 5 = summary_ready — sắp xếp khi tới lệch thứ tự. */
15
+ sequence: number;
16
+ /** queued · initiated · ringing · in-progress · completed · busy · no-answer · failed · canceled · abandoned */
17
+ status: string;
18
+ direction: "inbound" | "outbound";
19
+ /** E.164 khi chuẩn hoá được. */
20
+ from: string;
21
+ to: string;
22
+ agent: WebhookAgent | null;
23
+ queueId: string | null;
24
+ queueName: string | null;
25
+ startedAt: string | null;
26
+ answeredAt: string | null;
27
+ endedAt: string | null;
28
+ durationSec: number | null;
29
+ talkSec: number | null;
30
+ waitSec: number | null;
31
+ /** Mã Q.850 (16 = gác bình thường, 17 = bận, 19 = không trả lời, 41 = lỗi tạm thời…). */
32
+ hangupCause: number | null;
33
+ hangupCauseName: string | null;
34
+ /** `contactRef` bạn gửi lúc gọi ra — trả lại nguyên văn. */
35
+ contactRef: string | null;
36
+ /** `metadata` bạn gửi lúc gọi ra — trả lại nguyên văn. */
37
+ metadata: Record<string, string> | null;
38
+ crmContactId: string | null;
39
+ hasRecording: boolean;
40
+ hasSummary: boolean;
41
+ /** Tóm tắt AI — có từ sự kiện `call.summary_ready` (null trước đó). */
42
+ summary: string | null;
43
+ }
44
+ /** `data.object` của `agent.status_changed`. */
45
+ export interface WebhookAgentStatus {
46
+ agent: WebhookAgent;
47
+ from: string;
48
+ to: string;
49
+ reason: string | null;
50
+ changedAt: string;
51
+ }
52
+ export interface WebhookObjectMap {
53
+ "call.ringing": WebhookCall;
54
+ "call.answered": WebhookCall;
55
+ "call.completed": WebhookCall;
56
+ "call.missed": WebhookCall;
57
+ "call.recording_ready": WebhookCall;
58
+ "call.summary_ready": WebhookCall;
59
+ "agent.status_changed": WebhookAgentStatus;
60
+ }
61
+ interface Envelope<K extends string, T> {
62
+ id: string;
63
+ type: K;
64
+ api_version: string;
65
+ created_at: string;
66
+ workspace_id: string;
67
+ livemode: boolean;
68
+ data: {
69
+ object: T;
70
+ };
71
+ metadata?: Record<string, unknown>;
72
+ }
73
+ /** Sự kiện đã kiểm chữ ký. Thu hẹp kiểu bằng `event.type`. Loại sự kiện khác (CRM, Bán hàng…) có `data.object` là `unknown`. */
74
+ export type WebhookEvent = {
75
+ [K in keyof WebhookObjectMap]: Envelope<K, WebhookObjectMap[K]>;
76
+ }[keyof WebhookObjectMap] | Envelope<string & {}, unknown>;
77
+ export declare class WebhookVerificationError extends Error {
78
+ constructor(message: string);
79
+ }
80
+ type HeaderBag = Headers | Record<string, string | string[] | undefined>;
81
+ /** Ký một thân webhook — dùng để viết test cho phần nhận webhook của bạn. */
82
+ export declare function signWebhook(secret: string, id: string, timestamp: number, body: string | Buffer): string;
83
+ /**
84
+ * Kiểm `webhook-id` / `webhook-timestamp` / `webhook-signature` rồi trả sự kiện đã parse.
85
+ * Ném {@link WebhookVerificationError} khi thiếu header, chữ ký sai, hoặc lệch giờ quá `toleranceSec`.
86
+ * Dùng `webhook-id` (cũng là `event.id`) để bỏ qua lần giao trùng — NextX giao lại khi bạn không trả 2xx.
87
+ */
88
+ export declare function verifyWebhook(body: string | Buffer | Uint8Array, headers: HeaderBag, secret: string, options?: {
89
+ toleranceSec?: number;
90
+ now?: Date;
91
+ }): WebhookEvent;
92
+ export {};
@@ -0,0 +1,75 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Kiểm chữ ký webhook NextX theo chuẩn mở Standard Webhooks (https://www.standardwebhooks.com) — chỉ chạy trên máy chủ.
4
+ *
5
+ * import { verifyWebhook } from "@nextxai/sdk/webhooks";
6
+ *
7
+ * app.post("/nextx/webhook", express.raw({ type: "application/json" }), (req, res) => {
8
+ * const event = verifyWebhook(req.body, req.headers, process.env.NEXTX_WEBHOOK_SECRET!);
9
+ * if (event.type === "call.completed") saveCall(event.data.object);
10
+ * res.sendStatus(204);
11
+ * });
12
+ *
13
+ * Phải truyền THÂN GỐC (Buffer/chuỗi đúng từng byte) — JSON đã parse rồi stringify lại sẽ lệch chữ ký.
14
+ * Bí mật dạng `whsec_…` lấy ở developers.nextx.vn › Webhooks (xoay khoá ⇒ header có nhiều chữ ký, khớp một là đủ).
15
+ */
16
+ import { createHmac, timingSafeEqual } from "node:crypto";
17
+ /** Hai lần giao cách nhau quá ngưỡng này (giây) bị coi là phát lại. */
18
+ export const DEFAULT_TOLERANCE_SEC = 300;
19
+ export class WebhookVerificationError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "WebhookVerificationError";
23
+ }
24
+ }
25
+ function header(headers, name) {
26
+ if (typeof headers.get === "function")
27
+ return headers.get(name) ?? undefined;
28
+ const bag = headers;
29
+ const hit = Object.keys(bag).find((k) => k.toLowerCase() === name);
30
+ const v = hit === undefined ? undefined : bag[hit];
31
+ return Array.isArray(v) ? v[0] : v;
32
+ }
33
+ function keyOf(secret) {
34
+ if (!secret?.startsWith("whsec_"))
35
+ throw new WebhookVerificationError("secret phải có dạng whsec_…");
36
+ const key = Buffer.from(secret.slice(6), "base64");
37
+ if (key.length < 24 || key.length > 64)
38
+ throw new WebhookVerificationError("secret không hợp lệ");
39
+ return key;
40
+ }
41
+ /** Ký một thân webhook — dùng để viết test cho phần nhận webhook của bạn. */
42
+ export function signWebhook(secret, id, timestamp, body) {
43
+ const mac = createHmac("sha256", keyOf(secret));
44
+ mac.update(`${id}.${timestamp}.`);
45
+ mac.update(body);
46
+ return `v1,${mac.digest("base64")}`;
47
+ }
48
+ /**
49
+ * Kiểm `webhook-id` / `webhook-timestamp` / `webhook-signature` rồi trả sự kiện đã parse.
50
+ * Ném {@link WebhookVerificationError} khi thiếu header, chữ ký sai, hoặc lệch giờ quá `toleranceSec`.
51
+ * Dùng `webhook-id` (cũng là `event.id`) để bỏ qua lần giao trùng — NextX giao lại khi bạn không trả 2xx.
52
+ */
53
+ export function verifyWebhook(body, headers, secret, options = {}) {
54
+ const id = header(headers, "webhook-id");
55
+ const ts = header(headers, "webhook-timestamp");
56
+ const sig = header(headers, "webhook-signature");
57
+ if (!id || !ts || !sig)
58
+ throw new WebhookVerificationError("thiếu header webhook-id / webhook-timestamp / webhook-signature");
59
+ const timestamp = Number(ts);
60
+ const now = Math.floor((options.now ?? new Date()).getTime() / 1000);
61
+ if (!Number.isInteger(timestamp) || Math.abs(now - timestamp) > (options.toleranceSec ?? DEFAULT_TOLERANCE_SEC))
62
+ throw new WebhookVerificationError("webhook-timestamp lệch quá ngưỡng");
63
+ const raw = typeof body === "string" ? Buffer.from(body, "utf8") : Buffer.from(body);
64
+ const expected = Buffer.from(signWebhook(secret, id, timestamp, raw));
65
+ const ok = sig
66
+ .split(" ")
67
+ .filter(Boolean)
68
+ .some((s) => {
69
+ const candidate = Buffer.from(s);
70
+ return candidate.length === expected.length && timingSafeEqual(candidate, expected);
71
+ });
72
+ if (!ok)
73
+ throw new WebhookVerificationError("chữ ký không khớp");
74
+ return JSON.parse(raw.toString("utf8"));
75
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextxai/sdk",
3
- "version": "0.2.1-beta.29",
3
+ "version": "0.3.0",
4
4
  "description": "Official Node.js and TypeScript client for the NextX API",
5
5
  "keywords": [
6
6
  "nextx",
@@ -45,6 +45,16 @@
45
45
  "default": "./dist/cjs/phone.js"
46
46
  }
47
47
  },
48
+ "./webhooks": {
49
+ "import": {
50
+ "types": "./dist/esm/webhooks.d.ts",
51
+ "default": "./dist/esm/webhooks.js"
52
+ },
53
+ "require": {
54
+ "types": "./dist/cjs/webhooks.d.ts",
55
+ "default": "./dist/cjs/webhooks.js"
56
+ }
57
+ },
48
58
  "./package.json": "./package.json"
49
59
  },
50
60
  "files": [