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

# 添加收件人

> 为门店添加一个接收商户通知邮件的地址

为门店添加一个商户通知收件人。每个门店最多 **5 个收件人**。新增收件人的 9 个事件开关**继承门店当前的 `notify*` 设置**（门店未设过的键按订阅处理），`isPrimary` 恒为 `false`。已经关掉的事件不会因为添加收件人而重新开始投递。

继承只在**创建时**发生一次：9 个键在那一刻一次性写入，此后门店的 `notify*` 变化不再影响已存在的收件人，要改请用 `update-recipient`。同理，第 2 个收件人继承的是门店当前值而非第 1 个收件人的选择；移除后重新添加也是一次全新继承，不恢复历史偏好。

<Warning>
  门店没有显式收件人时，列表里会有一条合成的兜底条目（`isPrimary: true`），地址是门店 owner 的账号邮箱，通知也发到那里。**添加第一个显式收件人后该兜底条目即消失** —— 此后只有配置过的地址会收到通知。
</Warning>

```
POST /v1/actions/store-notification-recipient/add-recipient
```

**认证方式：** API Key（需 owner 或 admin 角色）

## 请求体

| 字段        | 类型     | 必填 | 说明                                                    |
| --------- | ------ | -- | ----------------------------------------------------- |
| `storeId` | string | 是  | 门店 ID（Short ID 格式 `STO_xxx`）                          |
| `email`   | string | 是  | 收件地址。入库前经 `lower(trim())` 归一化，仅大小写或前后空格不同的地址视为同一个收件人。 |

## 请求示例

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST https://api.waffo.com/v1/actions/store-notification-recipient/add-recipient \
    -H "Content-Type: application/json" \
    -H "X-Merchant-Id: $WAFFO_MERCHANT_ID" \
    -H "X-Signature: ..." \
    -d '{
      "storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw",
      "email": "ops@acme.com"
    }'
  ```

  ```javascript JavaScript theme={"system"}
  const response = await fetch(
    "https://api.waffo.com/v1/actions/store-notification-recipient/add-recipient",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Merchant-Id": process.env.WAFFO_MERCHANT_ID,
        "X-Signature": signature,
      },
      body: JSON.stringify({
        storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw",
        email: "ops@acme.com",
      }),
    },
  );

  const { data } = await response.json();
  ```
</CodeGroup>

## 成功响应 (200)

```json theme={"system"}
{
  "data": {
    "recipient": {
      "id": "11111111-2222-3333-4444-555555555555",
      "storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw",
      "email": "ops@acme.com",
      "isPrimary": false,
      "preferences": {
        "notifyNewOrders": true,
        "notifyNewSubscriptions": true,
        "notifySubscriptionCanceled": true,
        "notifySubscriptionRenewed": true,
        "notifySubscriptionEnded": true,
        "notifySubscriptionPastDue": true,
        "notifySubscriptionUncanceled": true,
        "notifySubscriptionPlanChanged": true,
        "notifyChargeback": true
      },
      "createdAt": "2026-08-28T00:00:00.000Z"
    }
  }
}
```

<Note>
  返回的 `recipient.id` 是 UUID，不是 Short ID。请把它当作不透明字符串，原样作为 `recipientId` 回传给 `update-recipient` 与 `remove-recipient`。
</Note>

## 响应字段

| 字段            | 类型      | 说明                                       |
| ------------- | ------- | ---------------------------------------- |
| `id`          | string  | 收件人 UUID。兜底条目改用形如 `OWNER_<storeId>` 的哨兵值 |
| `storeId`     | string  | 所属门店 Short ID                            |
| `email`       | string  | 归一化后的收件地址                                |
| `isPrimary`   | boolean | 通过本端点添加的收件人恒为 `false`；只有合成的兜底条目为 `true`  |
| `preferences` | object  | 9 个事件开关，新建时继承门店当前的 `notify*`             |
| `createdAt`   | string  | 创建时间（ISO 8601）                           |

## 错误

> **重试策略：** 4xx 一律不要重试 —— 修正请求后重新提交。5xx 用指数退避重试（起始 5 秒，最多 3 次）。

| 状态码 | `errors[0].message`                                               | 含义                         | 建议处理                   |
| --- | ----------------------------------------------------------------- | -------------------------- | ---------------------- |
| 400 | `Missing required field: storeId`                                 | 未提供 `storeId`              | 修正请求体后重新提交             |
| 400 | `Expected format: STO_xxx, got "..."`                             | `storeId` Short ID 无法解码    | 修正 `storeId` 格式后重新提交   |
| 400 | `Invalid email format`                                            | `email` 不是合法地址             | 修正地址后重新提交              |
| 400 | `Notification recipient limit reached (max 5 per store)`          | 门店已有 5 个收件人                | 先移除一个已有收件人             |
| 401 | `Missing merchantId in request context`                           | API Key 认证未解析出商户           | 检查 API Key 请求头与签名      |
| 403 | `Not authorized to manage notification recipients for this store` | 商户不是该门店的 `owner` 或 `admin` | 确认商户在该门店的角色            |
| 409 | `Recipient with this email already exists for this store`         | 归一化后的地址已是收件人，或并发写入抢先       | 直接更新已有收件人，不要重复添加       |
| 500 | `Internal server error`                                           | 服务端未预期故障                   | 用指数退避重试（起始 5 秒，最多 3 次） |
