SupDesk SDK 正式发布

SupDesk Team

任何成熟的产品最终都会编写一层集成逻辑——从收件箱读取数据的脚本、同步看板的 cron 任务、或是从自定义 UI 提交反馈的接口。当你坐在键盘前时,SupDesk 控制台非常高效;但只要你打算实现自动化,就必须依赖 API,而 API 的使用体验完全取决于与其交互的客户端 SDK。

今天,我们正式发布了四款 SDK。

SDK 阵容

  • JavaScript / TypeScript — npm 上的 supdesk。无需修改即可运行在 Node 18+、Deno、Bun 和 Cloudflare Workers 上,零运行时依赖。
  • Python — PyPI 上的 supdesk。包含同步客户端 SupDesk 与异步客户端 AsyncSupDesk,两者共享同一个 httpx 核心。
  • Gogithub.com/rabinapps/supdesk-go。仅依赖标准库,因此只要能运行 Go 1.23+ 的地方都可以编译——包括通过 GOOS=wasip1 运行在 Cloudflare Workers 上。
  • Dart — pub.dev 上的 supdesk。基于 dio 构建,拦截器、CancelToken 和代理适配器均能如预期般工作。

安装

npm install supdesk
pip install supdesk
go get github.com/rabinapps/supdesk-go
dependencies:
  supdesk: ^0.1.0

一个 API,四种语言

这些 SDK 并非四个独立的包装器,而是跨所有语言具备统一语义的同一套 API。只要学会了一种语言中的读取提交操作,就能在全部四种语言中熟练运用。

自动分页。 list() 返回一个同时支持异步迭代的页面对象——可以使用 for await 遍历所有页面,或者在只需首页数据时仅拉取第一页。

类型化资源。 提交、反馈、更新日志条目、消息、候补名单注册、Beta 计划与测试者、以及帮助中心文章和分类——每个资源均包含符合预期的 API 方法与类型化参数,拼写错误会在编译阶段直接报错,而非拖到线上环境。

类型化异常。 所有错误均继承自同一个基类——JS 和 Python 中的 SupDeskError、Go 中的 APIError、Dart 中的 SupDeskException——因此一个 catch 块即可捕获全部异常,同时依然支持通过 instanceoferrors.As 精确识别具体错误。

Webhook 签名校验。 constructEventFromRequestconstruct_event_from_headersConstructEventconstructEventFromHeaders 均能在常数时间内校验 SupDesk 签名,确保接收端处理的数据真实可靠。

内置容错与重试。 所有客户端均支持指数退避与抖动重试,遵循 Retry-After 响应头,且绝不会重复重试可能已被接收的计费 POST 请求——不稳定的网络绝不会导致用户的工单被重复提交。

专为服务端设计

SupDesk API 密钥拥有整个项目的操作权限,因此这些 SDK 拒绝在浏览器环境中运行——JS 和 Dart 的构造函数在检测到 DOM 或客户端构建环境时会直接抛出异常,README 中也明确解释了原因。请务必将密钥保存在服务端环境变量中,隐藏在你自己控制的接口之后。

还有两点需要说明:所有方案均支持读取操作,而写入操作(POST/PATCH/DELETE)则需要付费方案;此外,密钥作用域限定在项目级别,建议为每个环境配置独立的密钥,以便在撤销其中一个时不会影响其他环境。

SupDesk 的应用场景

这些 SDK 是 SupDesk SaaS 集成指南 的后端支撑部分——为希望在自家系统中嵌入支持功能的产品提供多租户支持、API 访问及 Webhook 机制。完整参考请查阅 API 文档

开始使用

前往 工作区设置 → API 密钥 获取 API 密钥,安装适配你技术栈的客户端,并运行本页面的快速入门。随后可以克隆仓库并查阅 README。

前往 supdesk.app 开始使用

用于后端集成的服务端SDK

从每个SDK的GitHub存储库动态加载的实时代码示例。

RabinApps/supdesk-nodeJavaScript / TypeScript
import { SupDesk } from "supdesk";

const supdesk = new SupDesk({ apiKey: process.env.SUPDESK_API_KEY! });

// Auto-pages: iterating walks every page for you.
for await (const submission of await supdesk.submissions.list({
  status: "open",
})) {
  console.log(submission.title);
}

await supdesk.submissions.create({
  type: "bug",
  title: "Export button does nothing",
  email: "user@example.com",
  body: "Clicking Export on the reports page has no effect.",
});
RabinApps/supdesk-pythonPython
from supdesk import SupDesk

supdesk = SupDesk()  # api_key=... or $SUPDESK_API_KEY

# Auto-pages: iterating walks every page for you.
for submission in supdesk.submissions.list(status="open"):
    print(submission.title)

supdesk.submissions.create(
    type="bug",
    title="Export button does nothing",
    email="user@example.com",
    body="Clicking Export on the reports page has no effect.",
)
RabinApps/supdesk-goGo
import "github.com/rabinapps/supdesk-go/supdesk"

client, err := supdesk.New(os.Getenv("SUPDESK_API_KEY"))
if err != nil {
    log.Fatal(err)
}

page, err := client.Submissions.List(ctx, supdesk.SubmissionsListParams{
    Status: ptr("open"),
})
if err != nil {
    return err
}
// Auto-pages: All walks every page for you.
for sub, err := range page.All(ctx) {
    if err != nil {
        return err
    }
    fmt.Println(sub.Title)
}

_, err = client.Submissions.Create(ctx, supdesk.SubmissionsCreateParams{
    Type:  supdesk.SubmissionTypeBug,
    Title: "Export button does nothing",
    Email: "user@example.com",
    Body:  "Clicking Export on the reports page has no effect.",
})
RabinApps/supdesk-dartDart
import 'dart:io';
import 'package:supdesk/supdesk.dart';

final supdesk = SupDesk(apiKey: Platform.environment['SUPDESK_API_KEY']!);

// Auto-pages: the stream walks every page for you.
final page = await supdesk.submissions.list(status: PostStatus.open);
await for (final submission in page.autoPaging()) {
  print(submission.title);
}

await supdesk.submissions.create(
  type: SubmissionType.bug,
  title: 'Export button does nothing',
  email: 'user@example.com',
  body: 'Clicking Export on the reports page has no effect.',
);

标签

#sdk#api#developers