﻿# 双翼邮件群发软件 HTTP API 使用指南

本文介绍第三方程序如何通过 API 提交邮件，并查询请求的发送状态。

## 1. 工作方式

第三方程序通过 HTTP 提交收件人邮箱和邮件变量。双翼软件将请求放入队列，由正在运行的 API 任务读取并发送。

API 请求只提供本次收件人和变量；发件账号、邮件主题与正文、发送策略、计划任务以及换 IP 等设置均由 API 任务提供。不同发送配置应分别建立 API 任务。

## 2. 使用前准备

### 2.1 创建并启动 API 任务

1. 在 双翼软件**开始**菜单 中新建 **API 任务**。
2. 像配置普通发送任务一样，设置发件账号、邮件内容、发送参数及其他所需选项。收件人不需要选择。
3. 在主题或正文中使用地址簿字段宏，例如 `[%FirstName]`。调用方提交的变量键必须是对应字段的宏名，例如 `FirstName`。
4. 使用任务编辑窗口中左上角的 **保存并启动**启动它。API 任务启动后会保持运行并等待请求。
5. 复制任务 ID：重新打开创建的API任务，点**任务标识**后面的**复制**按钮，可以将任务ID复制到系统粘贴板中。

请求只会由**正在运行**的 API 任务消费。任务停止期间不会处理队列；重新启动任务后，仍在队列中的请求可以继续处理。任务 ID 指定错误或任务未处于 双翼软件 的“正在发送”目录时，接口会返回任务不存在。

### 2.2 确认服务和端口

双翼软件 服务默认监听本机地址 `127.0.0.1:8061`。如需更改端口，可在 双翼软件 主配置文件的 `[API]` 配置段设置 `NativePort`，然后重启双翼软件。若配置中 `NativeEnabled=False`，将服务设为 `True` 后重启。

例如：

```ini
[API]
NativeEnabled=True
NativePort=8061
```

服务只绑定 `127.0.0.1`，因此调用程序需要运行在 双翼软件 所在的同一台计算机上。该接口没有内置认证，不应通过端口转发或其他方式暴露到不可信网络。

## 3. 提交邮件

### 3.1 请求地址

指定 API 任务：

```text
POST http://127.0.0.1:8061/api/v1/tasks/{TaskID}/requests
```

也可省略任务 ID：

```text
POST http://127.0.0.1:8061/api/v1/requests
```

省略任务 ID 时，双翼软件 会选择任务列表中第一个正在运行的 API 任务。若同时运行多个 API 任务，建议始终在 URL 中明确指定任务 ID，避免请求被其他配置的任务处理。

请求头：

```http
Content-Type: application/json
```

### 3.2 请求体

```json
{
  "recipientEmail": "customer@example.com",
  "variables": {
    "FirstName": "Alex",
    "Company": "双翼软件",
    "Tel": "8888888"
  }
}
```

字段说明：

| 字段 | 必需 | 说明 |
|---|---|---|
| `recipientEmail` | 是 | 收件人邮箱，必须是非空字符串。 |
| `variables` | 否 | JSON 对象；每个键是地址簿字段宏名，每个值必须是字符串、数字或布尔值。省略时按空变量处理。 |

邮件模板中可使用 `[%FirstName]`、`[%Company]` 等宏。请求中不提供的字段在邮件中为空；任务没有对应地址簿字段的变量会被忽略。变量名称使用字段宏名，而不是字段显示名称，具体名称以 双翼软件 地址簿字段配置为准。

请求体最大为 1 MiB。JSON 格式错误、`recipientEmail` 缺失或类型错误、`variables` 不是对象，或变量值为 `null`、数组、对象时，请求会被拒绝。

### 3.3 成功响应

请求被接受后返回 HTTP `202 Accepted`：

```json
{
  "requestId": "2132DDC6-4A00-423B-8ABA-B1B62BD90396",
  "status": "queued",
  "statusUrl": "/api/v1/tasks/{TaskID}/requests/2132DDC6-4A00-423B-8ABA-B1B62BD90396"
}
```

`requestId` 是 GUID 字符串（36 个字符，包含连字符）。`202` 表示请求已进入 双翼软件 队列，不代表邮件已经发送成功。请保存 `requestId` 或使用响应中的 `statusUrl` 查询最终状态。

### 3.4 curl 示例

将 `{TaskID}` 替换为 API 任务 ID：

```bash
curl -X POST "http://127.0.0.1:8061/api/v1/tasks/{TaskID}/requests" \
  -H "Content-Type: application/json" \
  -d "{\"recipientEmail\":\"customer@example.com\",\"variables\":{\"FirstName\":\"Alex\",\"Company\":\"双翼软件\"}}"
```

不指定任务 ID 的调用示例：

```bash
curl -X POST "http://127.0.0.1:8061/api/v1/requests" \
  -H "Content-Type: application/json" \
  -d "{\"recipientEmail\":\"customer@example.com\",\"variables\":{\"FirstName\":\"Alex\"}}"
```

## 4. 查询请求状态

使用提交时返回的任务 ID 和 请求 ID：

```bash
curl.exe -X GET "http://127.0.0.1:8061/api/v1/tasks/46322.310393588/requests/2132DDC6-4A00-423B-8ABA-B1B62BD90396"
```

将示例中的任务 ID 和请求 ID 替换为提交请求时实际返回的值。

成功响应示例：

```json
{
  "status": "succeeded",
  "errorMessage": ""
}
```

状态说明：

| 状态 | 含义 |
|---|---|
| `queued` | 已排队，等待 API 任务处理。 |
| `processing` | API 任务已领取请求，正在处理或发送。 |
| `succeeded` | 邮件发送成功。 |
| `failed` | 请求数据处理或邮件发送失败；可查看 `errorMessage`。 |
| `uncertain` | 双翼软件 在处理期间停止或中断，无法确认邮件是否已送达。为避免重复发送，此状态不会自动重试。 |

查询时任务 ID 和请求 ID 必须同时匹配。任务不存在、不是 API 任务或请求 ID 不属于该任务时返回 `404`。

## 5. HTTP 状态码

| HTTP 状态码 | 说明 |
|---|---|
| `200` | 状态查询成功。 |
| `202` | 请求已接受并排队。 |
| `400` | 请求格式、字段类型、Content-Type 或请求体大小不符合要求。 |
| `404` | 路径、任务或请求不存在；提交时也可能表示没有正在运行的 API 任务。 |
| `405` | 请求方法与接口路径不匹配。 |
| `500` | 双翼软件 处理请求时发生内部错误。 |

## 6. 日志与邮件内容

API 请求会进入请求队列，发送处理结果会记录在任务发送日志中。API 任务的发送日志会保存实际发送的主题和正文，可在 双翼软件 的任务日志中选中对应记录查看。保存的正文是宏替换后的实际发送内容。

## 7. 常见问题

### 请求已返回 queued，但没有发送

- 确认对应 API 任务仍处于运行状态。
- 若请求 URL 指定了任务 ID，确认该 ID 对应的是已启动的 API 任务。
- 确认 API 任务配置了可用的发件账号，且邮件内容和发送策略可正常使用。
- 确认 双翼软件 原生 HTTP 服务已启用，并且端口没有被其他程序占用。

### 返回 `404`，提示没有正在运行的 API 任务

省略任务 ID 的请求需要至少有一个正在运行的 API 任务。启动任务，或改用带任务 ID 的提交地址。

### 查询结果为 `uncertain`

该状态表示处理过程中发生中断，双翼软件 无法判断邮件是否已经送达。请先检查发送日志或收件情况，再决定是否重新提交，以免重复发送。

### 变量没有出现在邮件中

检查变量键是否使用地址簿字段宏名，并确认邮件模板中引用的宏与变量键一致。例如宏 `[%FirstName]` 对应变量 `"FirstName": "Alex"`。
