# @chatlychat/sdk-node 中文文档

规范地址：https://npmcn.com/package/%40chatlychat/sdk-node/

版本：0\.9\.0
npm 元数据更新时间：2026-11-06T06:47:35.437Z
中文译文更新时间：2026-11-09T13:27:01.134Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40chatlychat/sdk-node
原文对照：https://npmcn.com/package/%40chatlychat/sdk-node/?lang=original
项目主页：https://github.com/chatlychat/chatly-sdk-node#readme
源码仓库：https://github.com/chatlychat/chatly-sdk-node

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：568 kB，176 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2026-10-12T16:54:57.369Z
- 最新版本发布：2026-11-06T06:47:35.146Z
- 近 30 天下载量：2553（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：16（近 12 个月 16 个）

## 中文简介

官方Node.js SDK for the Chatly API——打入了REST客户端,webhook签名验证,以及部件身份散列.

## 安装

```sh
npm install @chatlychat/sdk-node
```

也可以使用：`pnpm add @chatlychat/sdk-node`、`yarn add @chatlychat/sdk-node`、`bun add @chatlychat/sdk-node`

## README

# @chatlychat/sdk-节点

Chatly API 的服务器端客户端. 用 API 密钥认证, 所以它是 用于您的后端, 而不是浏览器, 其中键可以读取 .

```bash
pnpm add @chatlychat/sdk-node
```

## 快开始

```ts
import { ChatlyClient } from '@chatlychat/sdk-node';

const chatly = new ChatlyClient({
  apiKey: process.env.CHATLY_API_KEY!, // ck_live_…
  apiUrl: 'https://api.chatlychat.com', // optional
});

const contacts = await chatly.contacts.list({ limit: 20 });
```

在下方创建密钥 **工作空间 — 开发者 — API 密钥**符号显示 一次并仅作为散列储存——如果你失去它,撤销并再铸成一分.

制作 `whoami()` 你的第一个电话。 它返回密钥所属的工作空间 和它实际持有的瞄准镜, 所以一个误用瞄准镜的密钥 响亮的失败在 集成时间,而不是像一个不相干的人一样迷惑403 生产终点:

```ts
const { workspaceId, scopes } = await chatly.whoami();
```

7个Api-keys的路线接受一个钥匙,从那时起在其中铸造 2026-10-26 (英语) `create` 拒绝任何范围 调用密钥没有自控, 所以泄露的只读密钥不能升级. 仅 `PUT /v1/api-keys/:id/machine-user` 继续关闭密钥。 没有的 这里的键入方法可以通过 `request()`,这需要 `headers` —— 说 这就是你发送 `x-secret-recipient` 将新密钥密封 改为公用密钥,而不是以简写形式返回.

## 客户所能做的

下面的每一种方法都映射到一个接受密钥认证的终点. 范围 仅满足您整合需要的关键; 关键永远不能比 创建时瞄准镜已选中 .

| 打电话 | 所需范围 |
| --- | --- |
| `chatly.whoami()` | `workspace:read` |
| `chatly.contacts.list({ cursor?, limit? })` | `contacts:read` |
| `chatly.contacts.upsert({ email, name, … })` | `contacts:write` |
| `chatly.conversations.list({ cursor?, limit? })` | `conversations:read` |
| `chatly.conversations.messages(conversationId)` | `conversations:read` |
| `chatly.conversations.send({ conversationId, body })` | `messages:write` |
| `chatly.search.query('refund', { … })` | `search:read` |
| `chatly.search.kb('refund')` | `search:read` |
| `chatly.events.track({ contactId, name, properties })` | `contacts:write` |

您随密钥发送的信件被记录为集成发送,而非由 钥匙的创造者, 钥匙的产生者 当他们离开的时候.

## 未包括,为何

反应和频道/部件配置是故意缺失的. 反应 记录 _谁_ 反应和钥匙不是一个人,所以这些终点需要 注册用户。 频道配置需要一个无法按键的瞄准镜 。 都用过 他们都把403还给每个打电话的人.

## 目 录

写入接受一等能键, 所以网络失败后重试不进行 重复工作 :

```ts
await chatly.withIdempotencyKey('order-4471-refund').contacts.upsert({ … });
```

## 正在验证任何东西

`verifyChatlySignature` 是用来用的。 你告诉它 **表面** 你们 即将结束,将生体和请求头交给它,然后检查 表面实际使用的构造:

```ts
import { verifyChatlySignature } from '@chatlychat/sdk-node';

const result = verifyChatlySignature({
  surface: 'event_webhook',
  body: rawBody, // the EXACT bytes, before JSON.parse
  headers: req.headers,
  secret: process.env.CHATLY_WEBHOOK_SECRET!,
  method: req.method, // see "the unified v2 signature" below
  path: req.url,
});

if (!result.ok) {
  console.warn('rejected', result.reason, result.message);
  return res.status(400).end();
}
```

传递生体——重排解析 JSON 重排密钥并更改 白空间,所以文摘将不匹配. 在拒绝时, `reason` 是一个稳定 树枝上的字符串( t)`signature_mismatch`, (中文(简体) ), `timestamp_outside_tolerance`, (中文(简体) ), `missing_signature_header`,(......)和 `message` 是写成的句子 已打印过。 从来投出.

### 8个表面

| `surface` | 页眉 | 已签名字符串 | `t` 单位 |
| --- | --- | --- | --- |
| `unified_v2` | `x-chatly-signature-v2: t=…,v1=…` | `<t>.<METHOD>.<path>.<rawBody>` | 秒数 |
| `agent_delivery` | `x-chatly-signature: t=…,v1=…` | `<t>.<rawBody>` | 秒数 |
| `event_webhook` | `x-livechat-signature: t=…,v1=…` | `<t>.<rawBody>` | 秒数 |
| `ai_tool_call` | `x-chatly-signature: t=…,v1=…` | `<t>.<METHOD>.<path>.<rawBody>` | 秒数 |
| `canvas_step` | `x-chatly-canvas-signature: sha256=…` 财务报告和审定财务报表 `x-chatly-canvas-ts` | `<ts>.<rawBody>` | **毫秒** |
| `customer_card` | `x-chatly-signature: sha256=…` 财务报告和审定财务报表 `x-chatly-timestamp` | `<ts>.<rawBody>` | **毫秒** |
| `workflow_webhook` | `x-chatly-signature-v2: t=…,v1=…` | `<t>.<rawBody>` | 秒数 |
| `workflow_webhook_legacy` | `x-chatly-signature: sha256=…` | `<rawBody>` - 没有时间戳 | 无 |

三个人派来了 **`x-chatly-signature`** 并签署三条不同的线条, 这就是为什么 `surface` 需要而不是从标题中推断:a 验证页眉名称上的密钥可以指向错误 建筑设计,同时看起来完全正确.

`ai_tool_call` 额外需求 `method` 财务报告和审定财务报表 `path` (单位:千美元)`pathname + search`, (中文(简体) ), 永远不要主机),因为两者都在已签名的字符串内. `toleranceSeconds` 默认为 300 并且总是在 **秒数**无论表面的单位 timerstamp使用——转换是此函数的工作,并把它弄错了 用手来检查新鲜度 大约54,000年 并拒绝 一切众生.

**偏爱 `workflow_webhook` 结束 `workflow_webhook_legacy`。 。 。 .** 每个工作流程 webhook 呼叫发送两个信头。 留下的遗物在身体上标出没有 时间戳,所以一个被捕获的请求 永远验证,没有什么可以约束一个 重新播放; `verifyChatlySignature` 拒绝,除非你通过 `allowUnprotectedReplay: true`,如果你这样做, 使端点一能.

### 统一 v2 签名

每个地表都在移动 **一个** 建筑业:

```
x-chatly-signature-v2: t=<unix seconds>,v1=<hex>
v1 = HMAC-SHA256(secret, `${t}.${METHOD}.${path}.${rawBody}`)
```

已经发送 **并行** 每个表层都有头,所以没有断裂 当发送者迁移时. `path` 实值 `pathname + search` 请求全文 来到了... 从来没有计划,也没有东道主。 方法与路径在内 文摘是因为 `GET` 没有身体标志 `"<t>.."`,这是 与每次会议相同 `GET` {\\fn华文楷体\\fs20\\bord1\\shad1\\1che0e0e0\\2ch000000}而地表能使出并可以回放出相對相對出相 路径.

过 `method` 财务报告和审定财务报表 `path` 打开 **每个** 呼叫时不需要更改代码 发件人移动 : `v2` 默认为 `'prefer'`,所以 v2 头被校验 并核查遗留的建筑.

```ts
const result = verifyChatlySignature({
  surface: 'customer_card',
  body: rawBody,
  headers: req.headers,
  secret: process.env.CHATLY_CARD_SECRET!,
  method: req.method,
  path: req.url,
  v2: 'prefer', // the default: 'require' once you know the sender has moved
});
// result.surface is 'unified_v2' when the v2 header was the one that verified.
```

`'prefer'` 永不退后:如果v2头存在而不验证, 这是拒绝而不是尝试遗产标题的理由。 两个头 同一个秘密,所以一个好的遗产摘要 除了一个坏v2是 篡改或发送错误, 并接受它会隐藏两者。 使用 `v2: 'ignore'` 将旧行为归结.

一个皱纹,这是我们的: `workflow_webhook` 已经发送 `x-chatly-signature-v2`结束 `<t>.<rawBody>` - 那个标题比 统一基地。 所以在那个地上 **两者** 无论何时,都接受基地 供应 `method` 财务报告和审定财务报表 `path`,以及 `result.base` (单位:千美元)`'body'` 或者说 `'target'`告诉 发件人用的那个.

该值带有 **基准标记**数字 : `t=…,v1=…,b=t` 为统一基地, `b=b` 页:1 `<t>.<rawBody>`。缺失标记意味着 `<t>.<rawBody>`,因为 在标记存在之前发送的每个 v2 值就是这个. 校对员看了 缩小其企图,并在拒绝时点出原因——a `b=t` 工作流程请求无 `method`编号 :`path` 实值 `missing_request_target`,而不是一个 不匹配——但绝不接受比表面允许的更多,因为任何人在 电线可以写出标记.

**哪个表面发送 `x-chatly-signature-v2` 现时:** 代理人交付 事件 webhook、客户卡获取和画布/步骤回调,全部 `b=t`· ; 工作流程动作, `b=b`. AI工具呼叫尚未发出——他们的遗产 `x-chatly-signature` 已签名统一字符串, 因此 `'prefer'` 坠落 通过一个相同的支票,你不会失去任何东西通过 `method` 财务报告和审定财务报表 `path` 现在.

**如果您终止了多个表面, 请全部验证为 `unified_v2`。 。 。 .** 那个 是一个 v2 标题的已记录路径; 平面名称用于 遗产头。 单一例外,直到发送者移动到 `b=t`,为 工作流程动作——核实为 `workflow_webhook`。 。 。 .

### 旋转一个秘密

旋转的两端都得到支持:

```ts
const result = verifyChatlySignature({
  surface: 'agent_delivery',
  body: rawBody,
  headers: req.headers,
  secret: [process.env.CHATLY_SECRET_NEW!, process.env.CHATLY_SECRET_OLD!],
});
if (result.ok) {
  console.log('verified with secret', result.secretIndex); // 0 = the new one
}
```

-   **`secret` 需要一个阵列.** 接受这个新旧秘密 重叠持续; `result.secretIndex` 你是如何学会的旧的 停止使用.
-   **标题可以包含摘要列表** —— 说 `t=…,v1=…,v1=…` - 对于发送者 签了两个秘密 任何匹配的验证 它们必须形成良好的结构.

### `verifyWebhookSignature` (仍在支持)

原作"单相"校正. 它处理 `event_webhook` 仅取 头值而不是头包。 出版、使用、不 离开:

```ts
import { verifyWebhookSignature } from '@chatlychat/sdk-node';

const ok = verifyWebhookSignature({
  body: rawBody, // the EXACT bytes, before JSON.parse
  signatureHeader: req.headers['x-livechat-signature'] as string,
  secret: process.env.CHATLY_WEBHOOK_SECRET!,
});
```

计划是 `t=<unix seconds>,v1=<hex>`时 `v1` hMAC -SHA256 已过 `` `${t}.${body}` ``。时间戳在签名的字符串内,所以一个俘虏 一旦外出就停止核查 `toleranceSeconds`。 。 。 .

> **这个例子在2026-10-12之前是错的,在三个方面** 上面说 `payload` (现场是: `body`, (中文(简体) ), `signature` (这是 `signatureHeader`和 `x-chatly-signature` (网络用户) **`x-livechat-signature`**) (中文(简体) ). 单位 TypeScript 旧片段是一个编译错误; 在平版 JavaScript 中是 比回去还糟 `false`,因为核查者分裂 `input.signatureHeader` 首先,一个缺席的投出一个 `TypeError` 调出 你的网络操作员.
> 
> 标题是值得知道的陷阱: **`x-chatly-signature` 这是真实的, 但属于另外四个地表** ——外方代理交货,定制 AI工具呼叫、客户卡和工作流程 `fire_webhook` 行动——和 每一个都以不同的方式构建其基础字符串。 境外事件 webhoks 是一个使用 `x-livechat-signature`不共享一个 核查员在检查时不检查施工情况.

* * *

## 关于此寄存器

这是一个 **镜像**。 。 。 . `@chatlychat/sdk-node` 由私人独身建筑 其余的 月台的迁移和合同检查现场直播 无法直接合并, 但补丁仍然是获得东西最快的方法 固定了,我们会把它 与归属.

您可以在这里做什么: 读取源代码, 运行测试, 打开一个问题, 或打开一个 PR 并且我们将把diff当作提案.

未镜像 : src/ signition-vectors- public. spec.ts —— 仍然在此重述的类别 与单列波的原作相匹配,这仅仅是其中的一个有意义的主张. 他们 继续跑.

问题: [https://github.com/chatlychat/chatly-sdk-node/issues (中文(中国大陆) )](https://github.com/chatlychat/chatly-sdk-node/issues) \* 文件: [https://chatlychat.com/docs (中文(简体) )](https://chatlychat.com/docs)

## 常见问题

### @chatlychat/sdk-node 是什么？

官方Node\.js SDK for the Chatly API——打入了REST客户端,webhook签名验证,以及部件身份散列\.

### 如何安装 @chatlychat/sdk-node？

在项目目录运行 npm install @chatlychat/sdk-node，或使用 pnpm add / yarn add / bun add。

### @chatlychat/sdk-node 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @chatlychat/sdk-node 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @chatlychat/sdk-node 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=18。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @chatlychat/sdk-node 可以商用吗？

@chatlychat/sdk-node 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @chatlychat/sdk-node 的下载量有多少？

根据 npm 官方下载统计，@chatlychat/sdk-node 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,553 次，日均约 85 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @chatlychat/sdk-node 还在维护吗？

最新版本 0\.9\.0 发布于 2026/11/06（3 天前）；至今共发布 16 个版本，其中过去 12 个月发布了 16 个；首次发布于 2026/10/13。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

