# MomoVector API v1

在线阅读：`/docs`（无需登录）；密钥管理：`/developers`；Markdown 下载：`/docs.md`；Python 客户端下载：`/examples/momovector_api.py`。机器可读文档：`GET /api/v1/openapi.json`，无需登录。

API 使用与网页相同的账户额度、生成工作流和历史，按生成扣费，查询和下载不扣额度。它不会暴露模型名称、中间图片或内部步骤。

## 1. 创建 API Key

在网页登录后，打开「API → 创建密钥」。可设置名称和 30 / 90 / 365 天有效期，或永久有效。默认 90 天，每个账户最多 10 个有效密钥。

完整密钥仅显示一次，数据库只保存 SHA-256 哈希、掩码和使用时间。关闭窗口后无法找回原密钥；丢失时撤销并重新创建。在服务端环境变量中配置 `MOMOVECTOR_API_KEY`，不要放在网页 JavaScript、URL 参数或公开仓库中。

请求头：

```http
Authorization: Bearer mv_sk_...
```

密钥可提交生成、查询额度及读取本账户作品。不能用它购买额度、修改认证、创建或撤销其他密钥，也不能删除作品。管理密钥仍需要网页登录会话和同源请求。

公共 API **不接受 Cookie 替代 Bearer Key**，也不会从 URL 查询参数中读取密钥。它面向服务端和脚本调用，不开放跨域浏览器调用。所有正式环境调用应使用 HTTPS。

撤销立即阻止后续鉴权；已通过鉴权的进行中请求和已提交任务仍会完成，生成结果保留在原账户中。

## 2. 提交任务

```text
POST /api/v1/generations
Content-Type: application/json
Idempotency-Key: <唯一请求标识>
```

`Idempotency-Key` 必填，16–80 位字母、数字、下划线或连字符，建议使用 UUID。为每个新任务生成一次，**网络重试时保留同一个值**；不要在每次重试中重新生成。

同一账户下，网页与不同 API Key 共用幂等范围：重复的相同请求返回原任务且不重复扣费；同一标识对应不同内容、旧版本没有请求指纹的任务或已删除作品时返回 `409 idempotency_conflict`。失败任务若想重新创作，也需要新的请求标识。

先在本机设置 `MOMOVECTOR_API_KEY`，并设置地址与当前任务标识：

```bash
export MOMOVECTOR_BASE_URL='http://localhost:4399'
export MOMOVECTOR_REQUEST_ID="$(uuidgen)"
```

文字生成：

```bash
curl "$MOMOVECTOR_BASE_URL/api/v1/generations" \
  -H "Authorization: Bearer $MOMOVECTOR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $MOMOVECTOR_REQUEST_ID" \
  -d '{"mode":"text","prompt":"一只可爱的小橘猫","style":"playful"}'
```

图片生成的 `image-request.json`：

```json
{
  "mode": "image",
  "image": "data:image/png;base64,...",
  "prompt": "保留主体，使用温暖的颜色",
  "style": "flat"
}
```

`...` 需替换为图片实际 Base64 内容，也可以直接使用下文 Python 脚本读取图片。

```bash
curl "$MOMOVECTOR_BASE_URL/api/v1/generations" \
  -H "Authorization: Bearer $MOMOVECTOR_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $MOMOVECTOR_REQUEST_ID" \
  --data-binary @image-request.json
```

| 字段     | 规则                                                                  |
| -------- | --------------------------------------------------------------------- |
| `mode`   | 必填：`text` 或 `image`                                               |
| `prompt` | 文字模式必填，trim 后 3–1200 字；图片模式可不填，最多 1200 字         |
| `image`  | 图片模式必填：PNG / JPEG / WebP Base64 Data URL；解码后最多 8 MiB     |
| `style`  | 必填：`playful`、`flat`、`minimal`、`retro`、`business`、`tech`、`3d` |

新增风格：`business`（商业插画）、`tech`（科技未来）、`3d`（通过立体视角与块面光影呈现的 SVG 插画）。文字和图片模式均可选择。

成功返回 `202 Accepted`，`Location` 指向查询地址，`Retry-After: 2` 建议两秒后查询：

```json
{
  "job": {
    "id": "2a357e9c-a431-42c4-9d73-bfb2b265712a",
    "mode": "text",
    "prompt": "一只可爱的小橘猫",
    "style": "playful",
    "status": "queued",
    "cost": 2,
    "source": "api",
    "demo": false,
    "error": null,
    "createdAt": "2026-09-26T20:00:00.000Z",
    "url": null
  }
}
```

本地演示响应 `demo: true`，会使用内置示例而不是实际模型；演示 API 同样运行真实的本地任务和扣费逻辑。

## 3. 查询并下载

```bash
curl "$MOMOVECTOR_BASE_URL/api/v1/generations/JOB_ID" \
  -H "Authorization: Bearer $MOMOVECTOR_API_KEY"
```

状态：`queued` → `running` → `completed` 或 `failed`。只有 `completed` 的 `job.url` 非空；`failed` 的 `job.error` 是用户可读消息，扣除的额度自动返还。

建议每隔 2–3 秒查询一次，并设置自己的总体等待时限。超时后不必重新创建任务，可以稍后查询原 `job.id`。

下载也需要鉴权，结果地址不是公开分享链接：

```bash
curl "$MOMOVECTOR_BASE_URL/api/v1/generations/JOB_ID/file" \
  -H "Authorization: Bearer $MOMOVECTOR_API_KEY" \
  --output artwork.svg
```

返回 `Content-Type: image/svg+xml`。未完成时返回 `409 result_not_ready`；其他账户、已删除或不存在的作品返回 `404`。

## 4. 额度和历史

```bash
curl "$MOMOVECTOR_BASE_URL/api/v1/credits" \
  -H "Authorization: Bearer $MOMOVECTOR_API_KEY"

curl "$MOMOVECTOR_BASE_URL/api/v1/generations?offset=0" \
  -H "Authorization: Bearer $MOMOVECTOR_API_KEY"
```

余额响应包含 `balance`、`costs`、`demo` 和 `limits`。当前文字生成 2 额度，图片生成 1 额度，网页购买的套餐额度直接可用于 API。

历史返回 `{ items, hasMore, nextOffset }`，每页 24 条，按创建时间倒序，包含同一账户的网页和 API 作品，`source` 区分 `web` / `api`。`offset` 为 0–100000 的整数。

## 5. 限额和错误

每个账户每个 UTC 自然分钟最多 60 次公共 API 请求，所有密钥共用；查询、提交和下载都会计入。OpenAPI 文档不计入。每个账户最多 4 个同时进行的生成，与网页共用。

鉴权成功的响应附带：

```http
X-Request-Id: <请求编号>
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: <当前窗口结束的 Unix 秒>
```

错误格式：

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "额度不足，请在网页购买额度后重试。",
    "requestId": "fdba965d-0c4c-439b-81d1-c2bc3db23ae0"
  }
}
```

| HTTP | code                                 | 处理方式                       |
| ---- | ------------------------------------ | ------------------------------ |
| 400  | `invalid_request` / `invalid_image` / `invalid_reference`  | 修正参数或图片                 |
| 401  | `invalid_api_key`                    | 检查密钥、有效期和撤销状态     |
| 402  | `insufficient_credits`               | 先在网页补充额度               |
| 404  | `not_found`                          | 检查接口、任务 ID 和所属账户   |
| 405  | `method_not_allowed`                 | 使用文档列出的 HTTP 方法       |
| 409  | `idempotency_conflict`               | 新任务使用新的请求标识         |
| 409  | `result_not_ready`                   | 先查询任务状态                 |
| 413  | `payload_too_large`                  | 缩小请求或图片                 |
| 429  | `rate_limited` / `concurrency_limit` | 遵守 `Retry-After`，等待后重试 |
| 500  | `internal_error`                     | 保留请求标识，有限重试         |
| 503  | `service_unavailable`                | 生成服务尚未配置或暂不可用     |

在固定窗口边界前后可能短时间接受两批请求，这是自然分钟限流的定义。需要持续高吞吐量时，可在 `src/shared/api.ts` 中调整策略；付费额度仍按同样方式计算。

## Python 完整示例

仓库提供无第三方依赖的 [`examples/momovector_api.py`](../examples/momovector_api.py)，支持提交、限时轮询和下载：

```bash
# 密钥从 MOMOVECTOR_API_KEY 环境变量读取
python3 examples/momovector_api.py --prompt '一只抱着花的小橘猫' --output cat.svg
python3 examples/momovector_api.py --image ./photo.png --style flat --output photo.svg
```

生产环境设置 `MOMOVECTOR_BASE_URL=https://你的正式域名`。`--request-id` 可指定持久化的请求标识，网络故障后重新运行时使用相同值；脚本会把新生成的标识打印到 stderr，方便记录。不要在一次新的创作中沿用旧标识。

## 更新现有部署

本次增加 `migrations/0003_api.sql`，在更新 Worker 前执行：

```bash
npm run db:migrate          # 本地
npm run db:migrate:remote   # 你的正式 D1
```

无需新增 Cloudflare 绑定或外部服务密钥。真实模型仍由原有的三个服务端端点负责。

## 手绘 Logo

`POST /api/v1/generations` 支持 `purpose: "logo"`，需同时设置 `mode: "image"`、`style: "minimal"`，将画布 PNG 或已有草图放入 `image`。不传 purpose 时保持原有 illustration 行为。

```json
{
  "mode": "image",
  "purpose": "logo",
  "style": "minimal",
  "primaryColor": "#24483F",
  "prompt": "山峰轮廓的咖啡店标志",
  "image": "data:image/png;base64,..."
}
```

- 每次消耗 1 额度，失败返还。描述选填，最多 1200 字；图片限制与普通图片生成一致。
- `primaryColor` 仅 Logo 模式可用，格式为 `#RRGGBB`，默认 `#202623`；颜色会规范为大写并加入请求幂等校验。它是生成指导，不能保证逐像素精确色值。
- 响应增加 `purpose`、`primaryColor`、`inputUrl`；Logo 原始草图通过 `GET /api/v1/generations/{id}/input` 读取，使用同一账户的 Bearer Key。普通作品 inputUrl 为 null，其他账户或已删除作品返回 404。
- 修改草图或颜色后应使用新的 Idempotency-Key。最终 SVG 仍从 `/file` 下载。
- 首版聚焦图形标志；不包含字标排版编辑器。

Python：`python momovector_api.py --image sketch.png --purpose logo --primary-color '#24483F' --output logo.svg`。

## 风格参考图

文字、图片和手绘 Logo 均可在生成请求中传入 `styleReferences: string[]`，最多两张，可混合以下格式：

- PNG / JPG / WebP 的 Base64 Data URL，每张解码后最多 2 MiB。
- 公开可访问的 HTTPS 图片直链（最多 4096 字符），不接受网页、IP、本地地址、非默认端口或 URL 用户凭据。由模型服务读取；处理期间需保持可用。

```json
{
  "mode": "text",
  "prompt": "一只坐在月亮上的狐狸",
  "style": "flat",
  "styleReferences": ["https://images.example.com/style.png", "data:image/webp;base64,..."]
}
```

主体由 prompt / image 决定，参考图指导配色、线条和质感，不将参考图当作第二个主体。参考风格优先于风格预设；用户描述与 Logo 主色保持优先。费用仍为文字 2 额度，图片 / Logo 1 额度。

响应和历史中的 `styleReferences` 为 `{kind: "upload" | "url", url: string}[]`。上传文件经 `GET /api/v1/generations/{id}/references/{index}`（index 为 0 或 1）读取，需同账户 Bearer Key；其他账户或删除作品后返回 404。外部链接直接保留 URL，不会归档，**不得向外部地址发送 MomoVector API Key**。修改参考内容或顺序需要新的 Idempotency-Key。省略或传空数组保持原有行为。

Python：`python momovector_api.py --prompt '月亮上的狐狸' --reference style.png --reference https://images.example.com/lines.webp --output fox.svg`。

升级先执行 `npm run db:migrate`；部署前执行 `npm run db:migrate:remote`，应用 `0009_style_references.sql`。
