Skip to content

V2 API 文档

V2 API 提供基于消息模板的发送接口,支持占位符替换和多实例发送。

⭐ 推荐使用

V2 API(模板)是我们推荐的消息发送方式,相比 V1 API 具有以下优势:

  • 内容复用 - 一次定义,多处使用,大幅提高开发效率
  • 灵活性 - 通过占位符实现动态内容,兼顾固定格式和动态数据
  • 易维护 - 修改消息内容无需改代码,运营人员可直接操作
  • 版本控制 - 支持模板启用/禁用,便于灰度发布和回滚
  • 团队协作 - 开发和运营分工明确,提高协作效率

适用于 90% 的消息发送场景,特别是有固定格式的通知类消息。

接口概述

V2 API 与 V1 API 的主要区别:

特性V1 APIV2 API
发送方式基于任务基于模板
内容定义API 调用时传递模板预定义
动态内容不支持支持占位符
多格式单一格式Text/HTML/Markdown
安全性Token 加密Token 加密

接口地址

POST /api/v2/message/send

请求参数

参数类型必填说明
tokenstring加密的模板 Token
titlestring消息标题
placeholdersobject占位符键值对
recipientsarray条件必填动态接收者列表(群发模式)🆕

参数说明

token

  • 模板的加密 Token,在管理后台的"消息模板"页面获取
  • 注意:V2 API 只支持加密 Token,不支持明文模板 ID
  • Token 使用对称加密算法生成,确保安全性

title

  • 消息标题,会传递给所有支持标题的渠道(如邮件)
  • 必填参数,不能为空

placeholders

  • 占位符的键值对,用于替换模板中的
  • 格式为 JSON 对象:{"key": "value"}
  • 如果模板中定义了占位符但未传递,将使用默认值
  • 如果既未传递也无默认值,占位符将保持原样

recipients 🆕

  • 动态接收者列表,用于群发场景
  • 格式为字符串数组:["user1@example.com", "user2@example.com"]
  • 条件必填:如果模板配置了动态接收实例,此参数为必填
  • 支持的渠道
    • ✅ 邮件 - 多个收件人邮箱地址
    • ✅ 微信公众号 - 多个用户 OpenID
  • 使用限制
    • 一个模板只能配置一个动态接收实例
    • 动态接收实例不能与固定接收实例混合使用
    • 建议控制接收者数量,避免触发渠道限流

请求示例

基本示例

bash
curl -X POST http://your-domain/api/v2/message/send \
  -H "Content-Type: application/json" \
  -d '{
    "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
    "title": "系统通知",
    "placeholders": {
      "username": "张三",
      "action": "登录",
      "time": "2024-12-06 12:00:00"
    }
  }'

Python 示例

python
import requests
import json

url = "http://your-domain/api/v2/message/send"
headers = {"Content-Type": "application/json"}

data = {
    "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
    "title": "系统通知",
    "placeholders": {
        "username": "张三",
        "action": "登录",
        "time": "2024-12-06 12:00:00"
    }
}

response = requests.post(url, headers=headers, data=json.dumps(data))
print(response.json())

Go 示例

go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    url := "http://your-domain/api/v2/message/send"
    
    data := map[string]interface{}{
        "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
        "title": "系统通知",
        "placeholders": map[string]string{
            "username": "张三",
            "action":   "登录",
            "time":     "2024-12-06 12:00:00",
        },
    }
    
    jsonData, _ := json.Marshal(data)
    resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer resp.Body.Close()
    
    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    fmt.Println(result)
}

JavaScript/Node.js 示例

javascript
const axios = require('axios');

const url = 'http://your-domain/api/v2/message/send';

const data = {
  token: 'a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b',
  title: '系统通知',
  placeholders: {
    username: '张三',
    action: '登录',
    time: '2024-12-06 12:00:00'
  }
};

axios.post(url, data)
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error('Error:', error);
  });

动态接收者示例(群发)🆕

javascript
const axios = require('axios');

const url = 'http://your-domain/api/v2/message/send';

const data = {
  token: 'a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b',
  title: '活动通知',
  placeholders: {
    activity_name: '双十二大促',
    start_time: '2024-12-12 00:00:00',
    discount: '全场8折'
  },
  recipients: [
    'user1@example.com',
    'user2@example.com',
    'user3@example.com'
  ]
};

axios.post(url, data)
  .then(response => {
    console.log(response.data);
    console.log(`成功发送给 ${response.data.data.count} 个接收者`);
  })
  .catch(error => {
    console.error('Error:', error);
  });

Java 示例

java
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class MessageSender {
    public static void main(String[] args) throws Exception {
        String url = "http://your-domain/api/v2/message/send";
        
        String jsonData = """
            {
                "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
                "title": "系统通知",
                "placeholders": {
                    "username": "张三",
                    "action": "登录",
                    "time": "2024-12-06 12:00:00"
                }
            }
            """;
        
        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setDoOutput(true);
        
        try (OutputStream os = conn.getOutputStream()) {
            byte[] input = jsonData.getBytes(StandardCharsets.UTF_8);
            os.write(input, 0, input.length);
        }
        
        int responseCode = conn.getResponseCode();
        System.out.println("Response Code: " + responseCode);
    }
}

响应格式

成功响应

json
{
    "code": 200,
    "msg": "success",
    "data": {
        "token": "a3541c2f0d3e1b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
        "count": 3
    }
}

响应字段说明:

  • code - 状态码,200 表示成功
  • msg - 响应消息
  • data.token - 使用的模板 Token
  • data.count - 成功发送的实例数量

失败响应

json
{
    "code": 400,
    "msg": "token解析失败:invalid token format",
    "data": null
}

工作流程

  1. Token 解析 - 解密 Token 获取模板 ID
  2. 模板查询 - 根据模板 ID 查询模板信息
  3. 状态检查 - 检查模板是否启用
  4. 占位符替换 - 使用传入的 placeholders 替换模板中的占位符
  5. 实例遍历 - 获取模板关联的所有启用实例
  6. 格式匹配 - 根据实例的 ContentType 选择对应格式的内容
  7. 消息发送 - 向每个实例发送消息
  8. 返回结果 - 返回发送成功的实例数量

占位符替换规则

基本规则

模板中使用 定义占位符,API 调用时通过 placeholders 参数传递替换值。

模板内容:

text
您好,{{username}}!
您的订单 {{order_id}} 已经 {{status}}。

API 调用:

json
{
  "placeholders": {
    "username": "张三",
    "order_id": "20241206001",
    "status": "发货"
  }
}

替换结果:

text
您好,张三!
您的订单 20241206001 已经发货。

默认值处理

如果占位符定义了默认值,未传递时使用默认值:

占位符定义:

json
[
  {
    "key": "username",
    "label": "用户名",
    "default": "Guest"
  }
]

API 调用(未传递 username):

json
{
  "placeholders": {}
}

替换结果:

text
您好,Guest!

未定义占位符

如果占位符既未传递也无默认值,将保持原样:

text
您好,{{username}}!  // username 未传递且无默认值

内容格式选择

V2 API 支持三种内容格式,系统会根据实例配置自动选择:

格式适用渠道说明
Text所有渠道纯文本格式,兼容性最好
HTML邮件富文本格式,支持样式
Markdown钉钉、企业微信Markdown 格式,支持格式化

示例:

假设模板定义了三种格式的内容:

  • Text: 您好,!
  • HTML: <h2>您好,!</h2>
  • Markdown: ## 您好,!

当发送到不同实例时:

  • 邮件实例(ContentType=html) → 使用 HTML 格式
  • 钉钉实例(ContentType=markdown) → 使用 Markdown 格式
  • 其他实例(ContentType=text) → 使用 Text 格式

@提醒功能

如果模板配置了@提醒,会自动应用到支持的渠道(钉钉、企业微信)。

模板配置:

  • @手机号:13800138000,13900139000
  • @用户ID:user001,user002
  • @所有人:是

发送效果:

  • 钉钉/企业微信会@指定的手机号或用户
  • 如果启用@所有人,会@群内所有成员

错误码说明

错误码说明解决方案
200成功-
400请求参数错误检查请求参数格式
400token解析失败检查 Token 是否正确
400模板不存在检查模板 ID 是否有效
400模板已禁用在管理后台启用模板
400模板没有配置发送实例为模板添加发送实例
400模板没有启用的发送实例启用至少一个实例
500服务器内部错误联系管理员

获取模板 Token

方式一:管理后台查看

  1. 登录 Message Nest 管理后台
  2. 进入"消息模板"页面
  3. 点击模板的"接口"按钮
  4. 查看并复制加密的 Token

方式二:API 代码示例

管理后台提供多种语言的 API 调用示例,包含真实的加密 Token。

安全性说明

Token 加密

  • V2 API 使用对称加密算法保护模板 ID
  • Token 是确定性加密,相同的模板 ID 生成相同的 Token
  • 加密密钥存储在服务器端,客户端无需关心加密细节

最佳实践

  1. Token 保护

    • 不要在公开代码中硬编码 Token
    • 使用环境变量或配置文件存储 Token
    • 定期检查 Token 的使用情况
  2. 权限控制

    • 合理设置模板的启用/禁用状态
    • 及时禁用不再使用的模板
    • 定期审查模板配置
  3. 内容安全

    • 注意模板内容的合规性
    • 避免在模板中包含敏感信息
    • 对用户输入进行验证和过滤

使用场景

1. 用户通知

json
{
  "token": "...",
  "title": "账号安全提醒",
  "placeholders": {
    "username": "张三",
    "action": "登录",
    "ip": "192.168.1.100",
    "time": "2024-12-06 12:00:00"
  }
}

2. 订单通知

json
{
  "token": "...",
  "title": "订单状态更新",
  "placeholders": {
    "order_id": "20241206001",
    "status": "已发货",
    "tracking_number": "SF1234567890",
    "estimated_delivery": "2024-12-08"
  }
}

3. 系统告警

json
{
  "token": "...",
  "title": "系统告警",
  "placeholders": {
    "service": "API Server",
    "level": "严重",
    "message": "CPU使用率超过90%",
    "time": "2024-12-06 12:00:00"
  }
}

4. 营销推广

json
{
  "token": "...",
  "title": "优惠活动通知",
  "placeholders": {
    "username": "张三",
    "product": "VIP会员",
    "discount": "8折",
    "expire_date": "2024-12-31"
  }
}

性能优化

异步发送

V2 API 采用异步发送机制,API 调用立即返回,实际发送在后台进行。

优点:

  • 快速响应,不阻塞调用方
  • 支持批量发送多个实例
  • 自动重试失败的发送

批量发送

一次 API 调用可以发送到多个实例(渠道),系统自动遍历所有启用的实例。

示例:

模板配置了 3 个实例:

  • 邮件实例(启用)
  • 钉钉实例(启用)
  • 企业微信实例(禁用)

调用 API 后,消息会发送到邮件和钉钉,企业微信实例被跳过。

常见问题

Q: V1 和 V2 API 可以同时使用吗?

A: 可以。V1 和 V2 API 是独立的,可以根据需求选择使用。

Q: 如何从 V1 迁移到 V2?

A:

  1. 创建消息模板,定义占位符
  2. 为模板配置发送实例
  3. 获取模板 Token
  4. 修改 API 调用代码,使用 V2 接口

Q: 占位符可以嵌套吗?

A: 不支持。占位符只支持一级替换,不支持嵌套或递归。

Q: 可以动态添加占位符吗?

A: 不可以。占位符必须在模板中预先定义,API 调用时只能传递已定义的占位符。

Q: 如何调试模板?

A: 使用管理后台的"预览"功能,可以填写测试数据查看替换效果。

下一步

Released under the MIT License.