在本教程中,您将设置一个常见的预订工作流程:
- 在用户完成预订后发送预订确认邮件。
- 如果用户开始预订但未及时完成,则发送恢复邮件。
完成后,您将拥有:
- 两个自定义事件(
booking_started、booking_complete)
- 一个根据完成与放弃情况分支的 Journey
- 用于确认详情的预订数据源
- 用于恢复激励的可选优惠券数据源
本指南重点介绍 OneSignal 的配置。您的预订系统和后端可以用任何语言或框架实现。
设置流程
- 您的应用跟踪
booking_started 自定义事件。
- 这会将用户加入 Journey。
- Journey 等待
booking_complete 事件,如果未及时收到,则发送后续提醒。
- 如果预订完成,OneSignal 在发送时调用预订数据源,并发送包含最新预订详情的确认邮件。
- 如果预订未在等待窗口内完成,Journey 会走过期路径并发送恢复邮件。
先决条件
开始之前,请确保您具备:
- 启用了 邮件 渠道的 OneSignal 应用
- 可以以 JSON 格式返回预订和/或优惠券数据的后端端点
- 在您的应用、后端和 OneSignal 外部 ID 之间共享的稳定用户标识符
- 访问 自定义事件
1. 跟踪预订事件
跟踪以下自定义事件。这些可以来自您的应用(使用我们的 SDK)或后端(使用我们的 REST API)。
事件名称:
booking_started — 当用户开始预订流程时
booking_complete — 当预订成功完成时
在我们的 移动 SDK 和/或 网页 SDK 上使用 trackEvent() 方法直接从您的应用/网站发送自定义事件。OneSignal.User.trackEvent("booking_started");
OneSignal.User.trackEvent("booking_complete");
如果您从后端跟踪事件,请使用 创建自定义事件 API 将事件发送到 OneSignal。curl --request POST \
--url https://api.onesignal.com/apps/{app_id}/custom_events \
--header 'Authorization: Key YOUR_APP_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"events": [
{
"name": "booking_started",
"external_id": "user123",
"properties": {}
}
]
}'
跟踪事件和从后端返回数据时使用相同的用户身份。不匹配的 ID 是缺少个性化的最常见原因。
2. 创建数据源别名
在 OneSignal 中,转到 设置 > 数据源 并创建以下别名。
预订数据源:
使用此源在发送时提取最新预订详情。
- 别名:
booking_data
- 方法: GET
- URL:
https://your-domain.com/datafeed/booking?user_id={{subscription.external_id}}
示例响应:
{
"first_name": "Sam",
"last_booking": {
"service_type": "Consultation",
"booking_date": "January 22, 2026",
"booking_time": "2:00 PM",
"price": 45
}
}
优惠券数据源(可选):
如果您想在恢复邮件中包含优惠券代码,请使用此可选源。
https://your-domain.com/datafeed/coupon?user_id={{subscription.external_id}}
示例响应:
{
"first_name": "Sam",
"code": "PROMO8F3K2",
"discount_text": "10%",
"expires_in_hours": 2,
"deep_link": "https://your-domain.com/checkout?coupon=PROMO8F3K2"
}
保护您的数据源端点。在生产环境中,在请求标头中发送 API 密钥(例如 x-api-key),并在 设置 > 数据源 中配置该标头,而不是在 URL 中嵌入密钥。
3. 创建邮件模板
预订确认邮件:
主题:
正文:
您好 {{ data_feed.booking_data.first_name | default: "客户" }},
感谢您的预订!以下是您的预约详情:
服务:{{ data_feed.booking_data.last_booking.service_type }}
日期:{{ data_feed.booking_data.last_booking.booking_date }}
时间:{{ data_feed.booking_data.last_booking.booking_time }}
价格:${{ data_feed.booking_data.last_booking.price }}
我们期待与您见面!
预订恢复邮件
主题:
正文:
您好 {{ data_feed.coupon.first_name | default: "客户" }},
在接下来的 {{ data_feed.coupon.expires_in_hours }} 小时内完成您的预订,
使用此代码即可享受 {{ data_feed.coupon.discount_text }} 优惠:
{{ data_feed.coupon.code }}
请在这里使用:
{{ data_feed.coupon.deep_link }}
您好,
您尚未完成预订!
立即完成预订,为下次预约节省费用。
使用此链接完成您的预订:
[在此插入深度链接]
在 Liquid 中始终包含 default 过滤器,以防止数据源字段缺失时出现空白内容。
4. 构建 Journey
-
在 OneSignal 中,转到 消息 > Journeys > 创建 Journey
-
将 入口触发器 设置为:
-
添加 等待直到 步骤:
- 条件: 发生自定义事件
- 事件名称:
booking_complete
- 最大等待时间: 10 分钟
- 启用过期路径
-
配置分支:
- 已完成: 发送预订确认邮件
- 已过期: 发送恢复邮件
过期分支允许您处理放弃情况,无需在应用中添加额外逻辑。请参见:
5. 测试和验证
验证事件
从您的应用或后端触发自定义事件并确认。
在 OneSignal 中,转到 分析 > 自定义事件 并确认您看到:
- 为您的外部 ID 显示
booking_started 事件
- 为您的外部 ID 显示
booking_complete 事件
验证数据源
使用已知用户 ID 手动调用您的数据源端点并确认:
验证邮件
从 Journey 编辑器发送测试消息并确认:
- 预订邮件包含真实预订详情
- 恢复邮件包含有效优惠券
- 没有 Liquid 变量显示为空
如果个性化缺失,请确认数据源请求中的用户 ID 与触发 Journey 的用户匹配。
示例:数据源实现
此示例显示了预订确认和恢复数据源的最小 Express 实现。您的后端语言、框架和数据源可以不同,只要 JSON 响应结构与您的邮件模板匹配即可。预订数据源示例
import express from "express";
const app = express();
function dataFeedAuth(req, res, next) {
if (req.headers["x-api-key"] !== process.env.DATAFEED_API_KEY) {
return res.status(401).json({ error: "Unauthorized" });
}
next();
}
app.get("/datafeed/booking", dataFeedAuth, async (req, res) => {
const { user_id } = req.query;
if (!user_id) {
return res.status(400).json({ error: "Missing user_id" });
}
const booking = await getLatestBookingForUser(user_id);
if (!booking) {
return res.status(404).json({ error: "No booking found" });
}
res.json({
first_name: booking.first_name,
last_booking: {
service_type: booking.service_type,
booking_date: booking.booking_date,
booking_time: booking.booking_time,
price: booking.price
}
});
});
优惠券数据源示例
app.get("/datafeed/coupon", dataFeedAuth, async (req, res) => {
const { user_id } = req.query;
if (!user_id) {
return res.status(400).json({ error: "Missing user_id" });
}
const coupon = await generateCouponForUser(user_id);
res.json({
first_name: coupon.first_name,
code: coupon.code,
discount_text: coupon.discount_text,
expires_in_hours: coupon.expires_in_hours,
deep_link: coupon.deep_link
});
});
实现指南
- 保持响应快速(数据源在发送时被调用)
- 始终返回可预测的 JSON 结构
- 当没有数据存在时使用 404
- 通过请求标头发送的 API 密钥保护端点
常见问题
邮件显示空值
- 数据源返回 404
- JSON 响应中的字段名称已更改
- 用户身份不匹配
Journey 不分支
- 未跟踪
booking_complete 事件
- 事件名称不匹配(区分大小写)
- 事件发生在等待窗口之外
数据源返回 401 或 403
下一步
- 添加事件属性(服务类型、价格)以实现更高级的 Journey 条件
- 添加额外的恢复步骤,如推送或短信提醒
- 使用 Journey 出口规则防止重复恢复消息