> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sub.jerrylu.app/llms.txt
> Use this file to discover all available pages before exploring further.

# 订阅 CRUD

> 通过开放 API 创建、读取、更新和删除订阅，并提供适合 AI Agent 的任务语义。

# 订阅 CRUD

订阅 API 使用 camelCase 字段。服务端管理字段会在响应中返回，但不能出现在写请求里。

## 操作语义

| 操作                                  | 适用场景                         | 风险  | AI 是否需要确认 |
| ----------------------------------- | ---------------------------- | --- | --------- |
| `GET /api/v1/subscriptions`         | 查询、搜索、汇总、分析或识别订阅             | 低   | 不需要       |
| `GET /api/v1/subscriptions/{id}`    | 已知 UUID，读取单条订阅               | 低   | 不需要       |
| `POST /api/v1/subscriptions`        | 记录一个新的订阅                     | 中   | 需要        |
| `PATCH /api/v1/subscriptions/{id}`  | 修改可写字段，包括 `status`（取消/暂停/恢复） | 中–高 | 需要        |
| `DELETE /api/v1/subscriptions/{id}` | 永久删除本地追踪记录（如需保留历史请改用取消）      | 高   | 需要        |

AI 集成在更新或删除前，应该先查询并确认目标订阅；除非用户已经提供了准确的订阅 UUID。写操作需要带 `write` 权限的 API Key；只读 Key 会收到 `403 insufficient_scope`。

## 分页

`GET /api/v1/subscriptions` 按创建时间倒序返回，支持两个可选查询参数：

| 参数       | 默认值  | 规则              |
| -------- | ---- | --------------- |
| `limit`  | `50` | 每页数量，范围 1 到 100 |
| `offset` | `0`  | 从开头跳过的记录数       |

响应中包含 `pagination` 对象。当 `hasMore` 为 `true` 时，用 `offset + limit` 获取下一页。

```json theme={null}
{
  "data": [ /* ... */ ],
  "pagination": { "limit": 50, "offset": 0, "hasMore": false },
  "requestId": "request-..."
}
```

```bash theme={null}
curl -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  "https://your-site.example/api/v1/subscriptions?limit=50&offset=50"
```

超出范围的取值会返回 `400 invalid_pagination`。

## 过滤与排序

`GET /api/v1/subscriptions` 在分页之外，还支持以下可选过滤器和排序：

| 参数               | 规则                                                                                                                              |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `status`         | `active`、`paused`、`cancelled` 之一                                                                                                |
| `category`       | 分类精确匹配，如 `Streaming`                                                                                                            |
| `period`         | `monthly`、`yearly`、`custom` 之一                                                                                                  |
| `q`              | 对订阅名称不区分大小写搜索                                                                                                                   |
| `expiringBefore` | `YYYY-MM-DD`；返回 `nextPaymentDate` 在该日期当天或之前的记录                                                                                  |
| `sort`           | `createdAt`、`-createdAt`、`nextPaymentDate`、`-nextPaymentDate`、`amount`、`-amount`、`name`、`-name` 之一（默认 `-createdAt`，前缀 `-` 表示降序） |

将 `status=active` 与 `expiringBefore` 组合即可回答「哪些即将续费」。非法取值返回 `400 invalid_query`，并带 `field` 与 `suggestedFix`。

```bash theme={null}
curl -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  "https://your-site.example/api/v1/subscriptions?status=active&expiringBefore=2026-07-01&sort=nextPaymentDate"
```

响应会回显实际生效的查询，便于 Agent 确认结果来源：

```json theme={null}
{
  "data": [ /* ... */ ],
  "pagination": { "limit": 50, "offset": 0, "hasMore": false },
  "query": { "sort": "nextPaymentDate", "filters": { "status": "active", "expiringBefore": "2026-07-01" } },
  "requestId": "request-..."
}
```

## 订阅对象

```json theme={null}
{
  "id": "33333333-3333-4333-8333-333333333333",
  "name": "Netflix",
  "category": "Streaming",
  "amount": 15.99,
  "currency": "USD",
  "period": "monthly",
  "lastPaymentDate": "2026-06-01",
  "nextPaymentDate": "2026-07-01",
  "notificationEnabled": true,
  "status": "active",
  "createdAt": "2026-06-16T00:00:00.000Z",
  "updatedAt": "2026-06-16T00:00:00.000Z"
}
```

## 可写字段

```json theme={null}
{
  "name": "Netflix",
  "category": "Streaming",
  "amount": 15.99,
  "currency": "USD",
  "period": "monthly",
  "lastPaymentDate": "2026-06-01",
  "customDate": null,
  "notificationEnabled": true,
  "status": "active"
}
```

服务端管理这些字段：

* `id`
* `nextPaymentDate`
* `createdAt`
* `updatedAt`

不要在 `POST` 或 `PATCH` 请求里发送服务端管理字段。

## 字段规则

| 字段                    | 规则                                                                |
| --------------------- | ----------------------------------------------------------------- |
| `name`                | 必填，1-120 个字符                                                      |
| `category`            | 必填，1-80 个字符                                                       |
| `amount`              | 必填数字，范围 0 到 999999.99，最多 2 位小数                                    |
| `currency`            | 必填，可选 `CNY`、`USD`、`EUR`、`JPY`、`GBP`、`AUD`、`CAD`、`CHF`、`HKD`、`SGD` |
| `period`              | 必填，可选 `monthly`、`yearly`、`custom`                                 |
| `lastPaymentDate`     | 必填，格式为 `YYYY-MM-DD`                                               |
| `customDate`          | 仅当 `period` 为 `custom` 时必填；表示间隔天数，需为正整数字符串                        |
| `notificationEnabled` | 可选布尔值，默认 `true`                                                   |
| `status`              | 可选，`active`、`paused`、`cancelled` 之一，默认 `active`                   |

`nextPaymentDate` 由服务端根据 `lastPaymentDate`、`period` 和 `customDate` 计算。`PATCH` 时，只有这三个字段之一发生变化才会重新计算；修改其它字段不会改动已存的 `nextPaymentDate`。`PATCH` 只校验你实际传入的字段，因此更新某个字段不会因为其它历史数据不合规而失败。

## 生命周期：取消、暂停、恢复 vs 删除

订阅有一个 `status`：`active`（计费中）、`paused`（暂停）、`cancelled`（已取消、停止计费但保留历史）。修改状态是软操作——记录及其审计历史都会保留，可随时恢复为 `active`。而 `DELETE` 是永久删除，会一并移除该记录的审计历史。

当用户表示不再使用某服务但将来可能回来时，优先改状态而不是删除：

```bash theme={null}
# 取消（保留历史）
curl -X PATCH \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"cancelled"}' \
  https://your-site.example/api/v1/subscriptions/33333333-3333-4333-8333-333333333333

# 暂停，之后再恢复
curl -X PATCH ... -d '{"status":"paused"}' .../{id}
curl -X PATCH ... -d '{"status":"active"}'  .../{id}
```

在[分析](/zh-CN/api/analytics)端点中，只有 `active` 订阅计入活跃支出。只有当用户希望永久删除记录时才用 `DELETE`。

## 自然语言示例

### 新增订阅

用户意图：

```text theme={null}
帮我记录 ChatGPT Plus，每月 20 美元，上次付款是 2026-06-17。
```

API 调用：

```bash theme={null}
curl -X POST \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"ChatGPT Plus","category":"AI","amount":20,"currency":"USD","period":"monthly","lastPaymentDate":"2026-06-17","notificationEnabled":true}' \
  https://your-site.example/api/v1/subscriptions
```

### 修改计费周期

用户意图：

```text theme={null}
把 Netflix 改成年付。
```

Agent 流程：

1. 调用 `GET /api/v1/subscriptions` 找到 Netflix 记录。
2. 向用户确认具体记录和修改内容。
3. 调用 `PATCH /api/v1/subscriptions/{id}`。

```bash theme={null}
curl -X PATCH \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"period":"yearly"}' \
  https://your-site.example/api/v1/subscriptions/33333333-3333-4333-8333-333333333333
```

### 删除重复记录

用户意图：

```text theme={null}
删除重复的 Netflix 订阅。
```

Agent 流程：

1. 调用 `GET /api/v1/subscriptions` 找到重复候选。
2. 向用户确认订阅名称和 id。
3. 调用 `DELETE /api/v1/subscriptions/{id}`。

```bash theme={null}
curl -X DELETE \
  -H "Authorization: Bearer $SUBSCRIPTION_MANAGER_API_KEY" \
  https://your-site.example/api/v1/subscriptions/33333333-3333-4333-8333-333333333333
```

## 自定义计费周期

如果不是月付或年付，把 `period` 设为 `custom`，并把 `customDate` 设为表示天数的正整数字符串。

```json theme={null}
{
  "name": "Domain renewal",
  "category": "Infrastructure",
  "amount": 12,
  "currency": "USD",
  "period": "custom",
  "lastPaymentDate": "2026-06-01",
  "customDate": "45"
}
```

从 `custom` 改回 `monthly` 或 `yearly` 时，省略 `customDate`。服务端会清理旧的自定义计费数据。

## AI tool schema

使用 [ai-tools.json](/api/ai-tools.json) 获取 tool/function 定义、风险等级和确认提示。
