# Notify Hub 接入 API

只看两个入口就够了：

- 推送消息：进入通知中心，并尝试推送到设备
- 创建 Todo：直接进入 Todo 工作台，不走通知推送

这两个接口当前都不需要 `Authorization` token。

## 1. 推送消息

- 方法：`POST /api/notifications/webhook`
- 生产地址：`https://notify.zbcode.cn/api/notifications/webhook`
- 用途：告警、提醒、审批提示、需要进入消息中心的内容

### 请求头

```http
Content-Type: application/json
```

### 最小请求体

```json
{
  "title": "新的重点提醒",
  "body": "这是一条来自外部服务的通知。",
  "sourceName": "Studio"
}
```

### 完整请求体

```json
{
  "title": "票务监测发现新的结果",
  "body": "目标票档已锁定，请立即完成后续处理。",
  "sourceName": "Ticket Worker",
  "level": "critical",
  "targetUsernames": ["zhubin"]
}
```

### 参数说明

- `title`：必填，消息标题
- `body`：必填，消息正文
- `sourceName`：必填，来源名称
- `level`：可选，消息级别，支持 `critical`、`important`、`normal`，默认 `important`
- `targetUsernames`：可选，用户名数组；不传时默认发给管理员账号

### 成功响应示例

```json
{
  "requestId": "6f5a95d6-7b76-4a92-b4ef-f9d02073d991",
  "success": true,
  "status": "success",
  "createdAt": "2026-03-20T14:10:11.000Z",
  "sourceName": "Ticket Worker",
  "targetUsernames": ["zhubin"],
  "requestedCount": 1,
  "createdCount": 1,
  "deliveredCount": 1,
  "failedCount": 0,
  "skippedCount": 0,
  "invalidTokens": [],
  "notificationIds": ["94f41756-8bd5-43e3-9f34-467a15815a56"],
  "deliveryDetails": [
    {
      "notificationId": "94f41756-8bd5-43e3-9f34-467a15815a56",
      "userId": "admin-user-id",
      "username": "zhubin",
      "tokenCount": 1,
      "deliveredCount": 1,
      "failedCount": 0,
      "skipped": false,
      "reason": "delivered",
      "invalidTokens": [],
      "tokenResults": []
    }
  ]
}
```

`deliveryDetails.reason` 会说明本次为什么投递成功、跳过或失败，重点看：

- `no_target_user`
- `no_android_device`
- `no_push_token`
- `firebase_not_configured`
- `partial_failure`
- `failed`

### curl 示例

```bash
curl -X POST https://notify.zbcode.cn/api/notifications/webhook \
  -H "Content-Type: application/json" \
  -d '{
    "title": "新的重点提醒",
    "body": "这是一条来自外部服务的通知。",
    "sourceName": "Studio",
    "level": "important",
    "targetUsernames": ["zhubin"]
  }'
```

## 2. 创建 Todo

- 方法：`POST /api/webhooks/tasks`
- 生产地址：`https://notify.zbcode.cn/api/webhooks/tasks`
- 用途：任务、待办、流程项、排期事项

### 请求头

```http
Content-Type: application/json
```

### 最小请求体

```json
{
  "title": "补齐 Android Todo 文档",
  "content": "需要补充字段说明、curl 示例和返回结构。",
  "source": "Task Loader"
}
```

### 完整请求体

```json
{
  "title": "补齐 Android Todo 文档",
  "content": "需要补充字段说明、curl 示例和返回结构。",
  "source": "Task Loader",
  "priority": "high",
  "dueAt": "2026-03-23T09:30:00+08:00",
  "targetUsername": "zhubin",
  "projectName": "产品研发",
  "collectionName": "Android Todo 接入"
}
```

### 参数说明

- `title`：必填，任务标题
- `content`：可选，任务正文或补充说明
- `source`：必填，任务来源名称
- `priority`：可选，支持 `critical`、`high`、`medium`、`low`
- `dueAt`：可选，截止时间，建议使用 ISO 8601，例如 `2026-03-23T09:30:00+08:00`
- `targetUsername`：可选，目标用户名；不传时默认写入管理员账号
- `projectName`：可选，目标项目名；不存在时自动创建
- `collectionName`：可选，目标合集名；不存在时自动创建

### 路由规则

- 不传 `projectName` 时，进入默认项目
- 不传 `collectionName` 时，进入项目下默认 `待分拣` 合集
- `projectName` 不存在时，系统会自动创建项目
- `collectionName` 不存在时，系统会自动创建合集

### 成功响应示例

```json
{
  "success": true,
  "task": {
    "id": "ed1f1f06-5720-4672-bef4-e2b94a37e750",
    "userId": "admin-user-id",
    "projectId": "1f0f0e37-6325-4f4f-9f56-48b1b7bbd88c",
    "collectionId": "120984a4-6d13-4187-96d6-f54b8e192b40",
    "title": "补齐 Android Todo 文档",
    "content": "需要补充字段说明、curl 示例和返回结构。",
    "sourceType": "webhook",
    "sourceName": "Task Loader",
    "priority": "high",
    "dueAt": "2026-03-23T01:30:00.000Z",
    "status": "todo",
    "createdAt": "2026-03-21T10:31:22.000Z",
    "updatedAt": "2026-03-21T10:31:22.000Z",
    "completedAt": null,
    "sourceNotificationId": null
  }
}
```

### curl 示例

```bash
curl -X POST https://notify.zbcode.cn/api/webhooks/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "title": "补齐 webhook 接入文档",
    "content": "需要增加字段说明和最小示例。",
    "source": "Task Loader",
    "priority": "high",
    "dueAt": "2026-03-23T09:30:00+08:00",
    "targetUsername": "zhubin",
    "projectName": "产品研发",
    "collectionName": "Android Todo 接入"
  }'
```

## 说明

- `/todo-webhook.md` 不是公开接口地址，直接访问会返回 `404`
- 对外接入请使用页面文档或直接调用上面的两个 API 地址
