# @creativekit/server 中文文档

规范地址：https://npmcn.com/package/%40creativekit/server/

版本：1\.1\.0
npm 元数据更新时间：2026-10-21T13:44:47.067Z
中文译文更新时间：2026-11-11T00:05:36.866Z
软件包许可：MIT

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

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

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

- 包体积（解压后）：459 kB，20 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=18\.0\.0
- 运行依赖：0 个
- 首次发布：2025-11-12T13:18:27.194Z
- 最新版本发布：2026-10-21T13:44:46.706Z
- 近 30 天下载量：3573（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：40（近 12 个月 40 个）

## 中文简介

服务器端 CreativeKit SDK 有 OAuth 认证

## 安装

```sh
npm install @creativekit/server
```

也可以使用：`pnpm add @creativekit/server`、`yarn add @creativekit/server`、`bun add @creativekit/server`

## README

# @ creativekit/ 服务器

**OAuth2 认证的服务器端 CreativeKit SDK**

⚠️ **只有节点.js** - 从不暴露在浏览器中

* * *

## 快速启动

```bash
npm install @creativekit/server
```

```typescript
import { CreativeKitServer } from "@creativekit/server";

const ck = new CreativeKitServer({
  apiBase: "https://api.adswag.nl",
  clientId: process.env.CREATIVEKIT_CLIENT_ID!,
  clientSecret: process.env.CREATIVEKIT_CLIENT_SECRET!,
});

// Create creative with presigned upload URL (AWS-style)
const creative = await ck.creatives.create("audio:podcast-aac");

// Return to frontend (client uploads directly to presigned URL)
// IMPORTANT: After upload, client must call commit to start processing
res.json({
  creativeId: creative.id,
  uploadUrl: creative.uploadSlots?.[0]?.url,
  uploadHeaders: creative.uploadSlots?.[0]?.headers,
});

// Commit endpoint - triggers processing
await ck.creatives.commit(creative.id);
```

* * *

## API 参考

### 创意

```typescript
// Create creative + get presigned URL (AWS-style)
const creative = await ck.creatives.create(profile, options);
// Options:
//   multipart?: boolean     - Use multipart upload for large files
//   fileSize?: number       - File size (required for multipart)
//   metadata?: object       - Custom metadata to attach
//   autoFinalize?: boolean  - Auto-finalize when processing succeeds (skip draft workflow)

// Commit after upload (starts processing)
await ck.creatives.commit(id);

// Or fill the draft from an https link instead of uploading (scope creative:ingest).
// CreativeKit downloads the file server-side, then continues as if commit() ran.
const status = await ck.creatives.ingest(id, {
  sourceUrl: "https://assets.example.com/spot-30s.mp4",
  // slot?: "video"          - optional when the profile has one required slot
  // idempotencyKey?: string
});
// status.status === "QUEUED"; status.source.status === "PENDING"
// On a failed download the creative ends FAILED; failure names step "source_fetch"
// and status.source.cause gives the reason (HTTP_STATUS, TOO_LARGE, ...).

// Get creative status and artifacts
const creative = await ck.creatives.get(id);
// Status values: "AWAITING_UPLOAD" | "QUEUED" | "PROCESSING" | "SUCCEEDED" | "FAILED" | "CANCELED"
// AWAITING_UPLOAD = created with directUpload, waiting for file upload + commit
// Progress is 0.0-1.0 (multiply by 100 for percentage)

// List creatives with filtering
const list = await ck.creatives.list({ status: "SUCCEEDED", limit: 20 });
// Options: status?, profile?, finalized?, limit?, pageToken?

// Finalize (mark as permanent, prevent auto-cleanup)
await ck.creatives.finalize(id);

// Generate preview URL for completed creative
const preview = await ck.creatives.preview(id);
// Returns: { url: string, expiresAt: number }

// Management
await ck.creatives.delete(id);
await ck.creatives.cancel(id);
await ck.creatives.retry(id);

// Edit a draft: reopen one slot → (client uploads) → replace to reprocess
const { uploadSlots } = await ck.creatives.reopenSlot(id, "frame_2");
await ck.creatives.replaceSlot({ id, slot: "frame_2" }); // file already re-uploaded
// …or do the whole thing server-side in one call (reopen + upload + reprocess):
await ck.creatives.replaceSlot({ id, slot: "frame_2", file: newImage });

// Edit a FINALIZED creative: clone into a fresh draft, change a subset of
// slots, then commit + finalize (the original is never modified).
const draft = await ck.creatives.clone(finalizedId); // copies all raw uploads
// draft.uploadSlots covers every slot; PUT new files only for the changed ones
await ck.creatives.commit(draft.id);
await ck.creatives.finalize(draft.id);

// Server-side upload (Node.js scripts/workers); supports slotCounts + slotTransforms
const result = await ck.creatives.upload({ profile, files });

// SSE proxy to frontend (recommended)
await ck.creatives.getEventsStream(id, res);
```

> **编辑只是草稿 .** `reopenSlot`编号 :`replaceSlot` 需要创造力 `SUCCEEDED` 或者说 `FAILED` 财务报告和审定财务报表 **未定稿**. 后处理总是从原生上传出再运行,所以没有世代质量损失. 见 [终到终流](https://npmcn.com/package/%40creativekit/server/#readme-end-to-end-flows)。 。 。 .

### Auth (密钥生成)

在不暴露客户秘密的情况下, 生成用于客户端的短寿命符号 :

```typescript
// Generate a scoped access token
const tokenData = await ck.auth.generateToken("creative:create creative:read creative:commit");

// Return to frontend
res.json({
  token: tokenData.accessToken,
  expiresIn: tokenData.expiresIn, // seconds
});
```

### 简介

```typescript
// List available processing profiles
const profiles = await ck.profiles.list();
// Returns: Profile[] with id, type, description, etc.

// Pre-validate declared file metadata against a profile WITHOUT uploading.
// Advisory pre-flight check — the authoritative validation still runs on the
// real bytes after upload. Returns the same { errors, warnings } shapes the
// processing pipeline emits, namespaced per slot (e.g. "standard.aspectRatio").
const result = await ck.profiles.validateMetadata("native:image", {
  slots: {
    standard: {
      filename: "banner.png",
      mimeType: "image/png",
      sizeBytes: 1_048_576,
      width: 1200,
      height: 628,
    },
  },
});
// Returns: { passed: boolean, errors: ValidationError[], warnings?: ValidationWarning[] }

// For dynamic-slot profiles (e.g. native:carousel), pass slotCounts so per-frame
// slots (frame_1..frame_N) and slot-group rules resolve:
await ck.profiles.validateMetadata("native:carousel", {
  slotCounts: { frame: 4 },
  slots: { frame_1: { width: 1080, height: 1080, sizeBytes: 500_000 } },
});
```

### 套装

Bundles集团相关配置文件一起销售(例如: `native` 广告包 : `standard` + 键 `large`) (中文(简体) ).

```typescript
// List all bundles. Pass { expandProfiles: true } to inline each member's
// full profile spec (upload slots + validation rules) in one request.
const bundles = await ck.profiles.listBundles({ expandProfiles: true });

// Get a single bundle by id (throws ApiError with status 404 if missing)
const bundle = await ck.profiles.getBundle("native:standard", {
  expandProfiles: true,
});
```

### 卫生

```typescript
// Comprehensive health check (API + credentials)
const health = await ck.health.check();
// Returns: { isHealthy, apiReachable, credentialsValid, details }

// Basic connectivity test
const reachable = await ck.health.ping();
// Returns: boolean

// Validate OAuth credentials
const validation = await ck.health.validateCredentials();
// Returns: { valid: boolean, error?: string }
```

### 用户

```typescript
// Check if client has valid credentials configured
const authenticated = ck.utils.isAuthenticated();

// Get current API configuration
const config = ck.utils.getConfig();
// Returns: { apiBase, hasCredentials }

// Get last known connection status
const status = ck.utils.getConnectionStatus();
```

### 调试

诊断认证问题的解决问题的方法:

```typescript
// Validate credentials with detailed error info
const result = await ck.debug.validateCredentials();
// Returns: { valid, error?, details? }

// Test API connection with diagnostics
const conn = await ck.debug.testConnection();
// Returns: { apiReachable, credentialsValid, responseTime?, error?, details? }

// Get current token information
const tokenInfo = await ck.debug.getTokenInfo();
// Returns: { hasCredentials, tokenCached, tokenExpiry?, error? }
```

* * *

## 终到终流

下方的每道流可复制-可复制. 它们涵盖整个创造性生命周期: **创建 /上传 / 承诺 / 监视 / 预览 / 定稿**附加时 **编辑** (重新开放/更换), **多相相/相机**, (中文(简体) ), **预加载验证**,以及 **取消/重试/删除**。 。 。 .

### 方法-终点参考

| SDK 方法 | REST 终点 |
| --- | --- |
| `ck.creatives.create(profile, opts)` | `POST /v1/creatives` |
| `ck.creatives.commit(id, body?)` | `POST /v1/creatives/{id}/commit` |
| `ck.creatives.ingest(id, opts)` | `POST /v1/creatives/{id}/ingest` |
| `ck.creatives.upload(params)` | 创建 + PUT + 承诺(服务器侧方便) |
| `ck.creatives.get(id)` | `GET /v1/creatives/{id}` |
| `ck.creatives.list(opts)` | `GET /v1/creatives` |
| `ck.creatives.cancel(id)` 编号 : `retry(id)` | `PATCH /v1/creatives/{id}` |
| `ck.creatives.delete(id)` | `DELETE /v1/creatives/{id}` |
| `ck.creatives.reopenSlot(id, slot)` | `POST /v1/creatives/{id}/slots/{slot}/reopen` |
| `ck.creatives.replaceSlot(params)` | `POST /v1/creatives/{id}/slots/{slot}/replace` |
| `ck.creatives.preview(id)` | `POST /v1/creatives/{id}/preview` |
| `ck.creatives.finalize(id)` | `POST /v1/creatives/{id}/finalize` |
| `ck.creatives.clone(id)` | `POST /v1/creatives/{id}/clone` |
| `ck.creatives.getEventsStream(id, res)` | `GET /v1/creatives/{id}/events` (SSE) (英语) |
| `ck.profiles.list(opts?)` | `GET /v1/profiles` |
| `ck.profiles.validateMetadata(id, body)` | `POST /v1/profiles/{id}/validate` |
| `ck.profiles.listBundles(opts?)` | `GET /v1/bundles` |
| `ck.profiles.getBundle(id, opts?)` | `GET /v1/bundles/{id}` |
| `ck.auth.generateToken(scopes)` | `POST /oauth/token` |

### 1\. 浏览器上传(建议)

浏览器通过预先签名的 URL 直接上传到存储; 您的后端 发布 URL 和 代理 生命周期 调用 。 用这个 `@creativekit/client`。 。 。 .

**后端( Express) — 薄的代理 :**

```typescript
// Create + return presigned upload slot(s)
app.post("/api/creatives", async (req, res) => {
  const { profile, slotCounts } = req.body;
  const creative = await ck.creatives.create(profile, { slotCounts });
  res.json({ creativeId: creative.id, uploadSlots: creative.uploadSlots });
});

// Commit to start processing (optionally pass slotTransforms for crops)
app.post("/api/creatives/:id/commit", async (req, res) => {
  await ck.creatives.commit(req.params.id, { slotTransforms: req.body?.slotTransforms });
  res.status(204).send();
});

// Status + live events + finalize
app.get("/api/creatives/:id", async (req, res) => res.json(await ck.creatives.get(req.params.id)));
app.get("/api/creatives/:id/events", (req, res) => ck.creatives.getEventsStream(req.params.id, res, { corsOrigin: req.headers.origin }));
app.post("/api/creatives/:id/finalize", async (req, res) => { await ck.creatives.finalize(req.params.id); res.status(204).send(); });
```

浏览器侧( O)`client.upload(...)`, (中文(简体) ), `client.streamEvents(...)`) 位于 **[@ creativekit/ 客户端 README](https://npmcn.com/package/%40creativekit/creativekit-client/README.md)**。 。 。 .

### 2\. 服务器端完整上传(文字、工人、队列)

`ck.creatives.upload()` 正在创建 ^ PUT 每个文件 ^ 承诺 ^ 等待,全部在 节点. 过 `slotCounts` 动态剖面图和 `slotTransforms` 用于农作物.

```typescript
import { readFile } from "node:fs/promises";

// Single-file profile
const audio = await ck.creatives.upload({
  profile: "audio:podcast-aac",
  files: { audio: new File([await readFile("ep.mp3")], "ep.mp3", { type: "audio/mpeg" }) },
  // `progress` (0-100) is the canonical progress field across CreativeKit
  // SDKs (the client SDK's `percent` is a deprecated alias of it).
  onProgress: (p) => console.log(`${p.stage} ${p.progress}%`),
});

// Native ad pack (multi-slot): "standard" + "large" are sold as one bundle.
// Each slot is a separate file; optionally crop each to its target.
const native = await ck.creatives.upload({
  profile: "native:image",
  files: { standard: standardFile, large: largeFile },
  slotTransforms: {
    // crop rect is in SOURCE pixels; target is the output resolution after crop+scale
    standard: { aspectRatio: "1.91:1", cropX: 0, cropY: 0, cropW: 1200, cropH: 628, targetWidth: 1200, targetHeight: 628 },
  },
});

if (native.status === "SUCCEEDED") console.log(native.artifacts);
```

### 3\. 木马(动力槽)

木马宣布 `frame` 模板插槽. `slotCounts: { frame: N }` 扩展它 输入 `frame_1 … frame_N`上传或预先签名 浏览器上传( 通过) `slotCounts` 改为 `create`) (中文(简体) ).

```typescript
const carousel = await ck.creatives.upload({
  profile: "native:carousel",
  slotCounts: { frame: 4 },
  files: { frame_1: f1, frame_2: f2, frame_3: f3, frame_4: f4 },
});
```

> 修改一个框架后将整个管道重新运行(所有框架从 他们的生上传。 参见流量 4.

### 4\. 编辑已有的创意( 重新打开 替换)

交换一个空档/空档 **草案** (未定稿) 重新创造它。 创造力必须是 `SUCCEEDED` 或者说 `FAILED`,另一个 插槽的生上传必须仍然在TTL草稿内.

**选项A——完全服务器侧(一个呼叫):**

```typescript
const updated = await ck.creatives.replaceSlot({
  id: creativeId,
  slot: "frame_2",
  file: newFrameFile,           // reopen + upload + reprocess + wait
  slotTransform: { aspectRatio: "1:1", cropX: 0, cropY: 0, cropW: 1080, cropH: 1080, targetWidth: 1080, targetHeight: 1080 }, // optional
});
```

**选项B——浏览器驱动(已签名) :**

```typescript
// 1) Backend reopens the slot and returns fresh presigned URLs
app.post("/api/creatives/:id/slots/:slot/reopen", async (req, res) => {
  const { uploadSlots } = await ck.creatives.reopenSlot(req.params.id, req.params.slot);
  res.json({ uploadSlots });
});

// 2) Browser PUTs the new file to that URL (client.replaceSlot handles this)

// 3) Backend triggers reprocessing (no file arg → just reprocess)
app.post("/api/creatives/:id/slots/:slot/replace", async (req, res) => {
  await ck.creatives.replaceSlot({
    id: req.params.id,
    slot: req.params.slot,
    slotTransform: req.body?.slotTransform, // optional crop from the browser
  });
  res.status(204).send();
});
```

> **什么可以编辑?** 只有插槽 **目录** 草稿(重新装入文件至 (a) 插槽) 配置/类型在创建时间被固定 。 一次 `finalize`d,一个创造性是 不可改变, **克隆** (流程4b)。 公开 `GET /c/{id}/{filename}` 只服务于最终的创意, 所以活资产永远不会改变 在观看器下面.

### 4b. "Edit" 已定稿的创意(clone ~ 更改子集 ~ 已定稿)

最终的创意是不可改变的,所以编辑一个意味着产生一个 **新设** 创意. `clone` 使这个便宜:它把源头复制成新的草稿 (新身份证, `AWAITING_UPLOAD`和 **服务器侧复制每个原始上传**,所以请 没有改变的插槽不需要重新加载, 并且从原位置进行无损失的再处理 来源. 只更改您想要的槽, 然后承诺 + 完成 .

```typescript
// 1) Clone the finalized creative → new draft seeded with all original frames
app.post("/api/creatives/:id/clone", async (req, res) => {
  const draft = await ck.creatives.clone(req.params.id);
  // draft.uploadSlots covers EVERY slot (e.g. frame_1..frame_8)
  res.status(201).json({ id: draft.id, uploadSlots: draft.uploadSlots });
});

// 2) Browser PUTs new files ONLY for the changed slots (e.g. 4 of 8 frames)
//    to their presigned URLs. The other slots keep the copied raw uploads.

// 3) Commit reprocesses all slots from raw (one clean pass), then finalize.
await ck.creatives.commit(draftId);
await ck.creatives.finalize(draftId);
// Result: a brand-new finalized creative (new ID/URL). The original is untouched;
// the platform should swap its reference to the new creative.
```

删除或重排自動滑行( 不仅仅是互换文件内容), pass `slotPlan` 复制时:

```typescript
// 5-frame source → 3-frame draft (drops frame_2 and frame_4)
const draft = await ck.creatives.clone(finalizedId, {
  slotPlan: { frame: ["frame_1", "frame_3", "frame_5"] },
});
// draft.uploadSlots = frame_1..frame_3, seeded from source frame_1, frame_3, frame_5
```

> 克隆人依赖源头仍然有生上传(最终的创意) (续) 如果他们走了, `clone` 返回 409 ——创建新的创意 换句话说.

### 5\. 上载前验证(失效,无上载)

根据配置文件的规则检查已申报的元数据 **在此之前** 推动字节. 仅咨询——权威检查仍在实际字节执行时运行.

```typescript
const check = await ck.profiles.validateMetadata("native:image", {
  slots: { standard: { mimeType: "image/png", sizeBytes: 1_048_576, width: 1200, height: 628 } },
});

if (!check.passed) {
  // surface check.errors to the user and skip the upload
  return res.status(422).json({ errors: check.errors, warnings: check.warnings });
}
// else proceed with create/upload
```

### 6\. 取消、重试、删除、列表

```typescript
await ck.creatives.cancel(id);        // stop in-flight processing
await ck.creatives.retry(id);         // retry a FAILED creative
await ck.creatives.delete(id);        // remove a creative + its artifacts

// Paginate finalized creatives for a profile
let pageToken: string | undefined;
do {
  const page = await ck.creatives.list({ profile: "native:image", finalized: true, limit: 50, pageToken });
  page.items.forEach((c) => console.log(c.id, c.status));
  pageToken = page.nextPageToken;
} while (pageToken);
```

* * *

## SSE 事件代理服务器

代理实时处理事件到您的前端, 使用单个方法调用 :

```typescript
// NestJS
@Get(':id/events')
async streamEvents(@Param('id') id: string, @Res() res: Response) {
  await ck.creatives.getEventsStream(id, res);
}

// Express
app.get('/creatives/:id/events', async (req, res) => {
  await ck.creatives.getEventsStream(req.params.id, res);
});
```

SDK的手柄:

-   SSE 标题( Content- Type, 快取控制, 连接)
-   \* 终端状态检测(自动关闭SUCCEED、故障、CANCED)
-   QQ 自动整流
-   QQ 客户端断开处理
-   QQ CORS 信头( 可选)

在CORS的支持和回调下:

```typescript
await ck.creatives.getEventsStream(id, res, {
  corsOrigin: req.headers.origin,
  onComplete: (status) => console.log(`Creative ${id}: ${status}`),
  onError: (error) => console.error(`Stream error: ${error.message}`),
});
```

> **说明:** SSE事件是最小信号(`id`, (中文(简体) ), `status`, (中文(简体) ), `progress`) (中文(简体) ). 客户端应该打电话 `GET /creatives/:id` 关于终点站的详情.

* * *

## 补全 Webhooks

而不是投票或SSE,在创建时注册一个网页。 后端 邮递到您的 `callbackUrl` 当创意达到终端状态时 (单位:千美元)`SUCCEEDED` 编号 : `FAILED` 编号 : `CANCELED`),与您签名 `callbackSecret`。 。 。 .

```typescript
const creative = await ck.creatives.create("video:web-1080p-h264", {
  callbackUrl: "https://your-backend.example.com/webhooks/creativekit",
  callbackSecret: process.env.CREATIVEKIT_WEBHOOK_SECRET!, // required with callbackUrl
});
```

> `callbackSecret` 实值 **所需经费** 任何时候 `callbackUrl` 设置—— API 拒绝未签名的webhook注册(400).

**接收网点** - 核实 `X-CreativeKit-Signature` 标题 (hex HMAC-SHA256 over the bird body) 与SDK帮助者:

```typescript
import express from "express";
import { parseWebhookEvent, WEBHOOK_SIGNATURE_HEADER } from "@creativekit/server";

// Use a raw body parser: the signature covers the exact bytes received.
app.post(
  "/webhooks/creativekit",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    let event;
    try {
      event = parseWebhookEvent(req.body, {
        secret: process.env.CREATIVEKIT_WEBHOOK_SECRET!,
        signature: req.header(WEBHOOK_SIGNATURE_HEADER),
      });
    } catch {
      return res.sendStatus(401);
    }

    // Delivery is at-least-once — dedupe on event.eventId.
    // The payload is minimal ({ eventId, id, status, error }); on SUCCEEDED,
    // fetch the creative for artifacts and CDN URLs:
    if (event.status === "SUCCEEDED") {
      const creative = await ck.creatives.get(event.id);
      // ...store creative.artifacts...
    }

    res.sendStatus(204);
  }
);
```

`verifyWebhookSignature(secret, rawBody, signature)` 也导出 只想要布尔检查。 交付以指数回转 (~10个尝试)在5xx/网络错误上;2xx和4xx都算已交付,所以 将终端状态与 `GET /v1/creatives/{id}` 作为后退.

* * *

## 最终确定工作流程(草案-永久)

已处理的创意开始为"草稿",可以预览,但在草稿TTL过期后会自动被清理(默认:24h). 为了永久保持创造性:

```typescript
// After user reviews the preview and confirms
await ck.creatives.finalize(creativeId);

// Check finalization status
const creative = await ck.creatives.get(creativeId);
console.log(creative.finalized); // true
console.log(creative.finalizedAt); // timestamp in ms
```

**为什么要最后决定?**

-   **成本控制**: 仅用于确认的创意的存储费用
-   **用户确认**: 让用户在承诺前预览
-   **《同上》**: 安全呼叫多次

**通过自动定稿跳过工作流程草稿 :**

如果您不需要用户预览确认, 请自定义成功 :

```typescript
const creative = await ck.creatives.create("video:web-1080p-h264", {
  autoFinalize: true, // Automatically finalized when processing succeeds
});
```

**表示端点示例 :**

```typescript
app.post("/creatives/:id/finalize", async (req, res) => {
  await ck.creatives.finalize(req.params.id);
  res.status(204).send();
});
```

* * *

## 预览 URL

生成已完成的创建的签名预览 URL :

```typescript
// Get a signed preview URL (expires after a period)
const { url, expiresAt } = await ck.creatives.preview(creativeId);

// Return to frontend for display
res.json({ previewUrl: url, expiresAt });
```

**表示端点示例 :**

```typescript
// Note: The SDK uses POST internally to generate the preview URL
app.post("/creatives/:id/preview", async (req, res) => {
  const preview = await ck.creatives.preview(req.params.id);
  res.json(preview);
});
```

* * *

## 处理失败的创意

当创作失败时, 请检查两者 `results.validation` (用于验证错误)和 `failure` (用于管道错误):

### 校验错误

详细校验错误在 `results.validation.errors`数字 :

```typescript
const creative = await ck.creatives.get(id);

if (creative.status === "FAILED" && creative.results?.validation) {
  console.log("Validation failed:");
  for (const error of creative.results.validation.errors) {
    console.log(`  ${error.field}: ${error.message}`);
    console.log(`    Expected: ${error.expected}, Got: ${error.actual}`);
    console.log(`    Code: ${error.code}`);
  }
}
```

**校验出错字段 :**

| 外地 | 说明 |
| --- | --- |
| `field` | 失败的财产(例如, `aspectRatio`, (中文(简体) ), `duration`(中文(简体) ) |
| `rule` | 违反验证规则(例如:, `allowedValues`, (中文(简体) ), `range`(中文(简体) ) |
| `expected` | 剖面图需要什么 |
| `actual` | 在上传文件中发现了什么 |
| `code` | 机器可读错误代码 |
| `message` | 可读错误信息 |
| `profileHint` | 概况限制的参考 |

### 管道错误

对于无法恢复的错误( 译码失败等) , 请检查 `failure`数字 :

```typescript
if (creative.status === "FAILED" && creative.failure) {
  console.log(`Pipeline failed at step: ${creative.failure.step}`);
  console.log(`Error: ${creative.failure.message}`);
  console.log(`Retriable: ${creative.failure.retriable}`);
}
```

### 快速端点示例

```typescript
app.get("/api/creatives/:id", async (req, res) => {
  const creative = await ck.creatives.get(req.params.id);

  // Return validation errors in a client-friendly format
  if (creative.status === "FAILED") {
    const validationErrors = creative.results?.validation?.errors || [];
    const pipelineError = creative.failure;

    return res.json({
      ...creative,
      // Flatten for easier client consumption
      errorSummary: validationErrors.length
        ? validationErrors.map((e) => e.message).join("; ")
        : pipelineError?.message || "Unknown error",
    });
  }

  res.json(creative);
});
```

* * *

## 完整文档

**[参见 SDK 主要文档](https://npmcn.com/package/%40creativekit/README.md)** 用于:

-   完整 API 参考
-   建筑格局
-   完整实例( Express, Next.js, NestJS)
-   安保最佳做法
-   解决问题

* * *

## 警卫

-   使用环境变量获取证书
-   QQ 从不向浏览器曝光此 SDK
-   保留 `clientSecret` 安全
-   在您的后端添加认证的中间软件

* * *

## 许可证

麻省理工学院

## 常见问题

### @creativekit/server 是什么？

服务器端 CreativeKit SDK 有 OAuth 认证

### 如何安装 @creativekit/server？

在项目目录运行 npm install @creativekit/server，或使用 pnpm add / yarn add / bun add。

### @creativekit/server 支持 TypeScript 吗？

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

### @creativekit/server 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @creativekit/server 需要什么版本的 Node\.js？

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

### @creativekit/server 可以商用吗？

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

### @creativekit/server 的下载量有多少？

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

### @creativekit/server 还在维护吗？

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

