开发者指南 · 订阅表单
打造自己的订阅体验。
保留自己的 HTML、样式和网站框架。通过公开订阅接口,将自定义表单连接到已发布的 smartest.email 表单。
本指南涵盖
公开表单配置,以及来自任意网站源的订阅
同意、验证、响应和速率限制
使用表单提供的网址。
在“订阅表单”中创建表单,添加必填邮箱字段和必选同意复选框,然后发布。“安装”选项卡会显示该表单的准确网址。
基础网址
https://api.smartest.email/api/v1/public/forms/{FORM_ID}使用表单“安装”选项卡中显示的网址。在下方示例中,将 FORM_ID 替换为已发布表单的 ID。
创建
设置字段和明确的同意声明。
发布
新表单初始为草稿;发布之前,公开路由返回 404。
集成
使用嵌入代码,或调用下方的两个公开接口。
使用自己的 HTML 表单。
支持直接提交。在 smartest.email 中创建并发布表单以定义字段和同意声明,然后在现有网站表单中使用该表单 ID。您可以选择嵌入式表单类型,而不安装组件。
您的网站可以通过 JavaScript 提交 fetch(),也可以由您自己的服务器将订阅转发为 JSON。公开接口无需 API 密钥、访问令牌或 smartest.email 嵌入脚本。
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/submit将 FORM_ID 替换为已发布表单的 UUID。发送下方请求正文时,请使用 Content-Type: application/json。
{
"data": {
"email": "visitor@example.com"
},
"consent": true
}请设置 consent 为访客明确选择的值,并显示表单的同意声明和隐私说明。请将 consent 保留在顶层;将配置的联系人字段放在 data中。请包含已发布表单中的每个必填字段。
普通 HTML 表单的 action 需要适配器
此接口仅接受 JSON。普通的 <form method="post" action="…"> 会发送 URL 编码或 multipart 数据,并收到 415 JSON body required。请使用下方的 浏览器 JavaScript 示例,或先提交到您自己的服务器,再由服务器向 smartest.email 发送 JSON。
获取 公开配置 可让字段标签和验证规则与已发布表单保持一致,但并非每次提交的必要前提。您的集成需自行处理成功提示或跳转;此接口始终返回 JSON。
渲染您发布的字段。
此公开接口无需访问令牌。显示表单时获取配置,然后在自定义界面中使用返回的字段名、同意声明和隐私网址。
https://api.smartest.email/api/v1/public/forms/{FORM_ID}返回 200 及可安全渲染的启用表单数据。每次成功获取都会增加浏览次数,请避免轮询。
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/embed.js现成的内嵌式或弹窗组件脚本。如无需自定义界面,请使用“安装”中的准确脚本网址。
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/landing启用表单的托管订阅页面。
{
"id": "FORM_ID",
"name": "Newsletter",
"organizationName": "Example Studio",
"type": "embedded",
"fields": [
{ "name": "email", "type": "email", "label": "Email address", "required": true },
{ "name": "consent", "type": "checkbox", "label": "I agree to receive email updates.", "required": true }
],
"styling": {},
"successAction": {},
"privacyUrl": "https://example.com/privacy"
}响应可能包含其他已配置的字段、选项及验证规则。 successAction 描述现成组件的行为;直接提交仍返回下方通用 JSON 响应。对于草稿、未发布、无效或未知表单,脚本和托管页面返回 404。
提交数据时一并发送同意标记。
访客明确选择接收营销邮件后再提交 JSON。同意标记应放在顶层,不要将 consent 放入 data。
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/submit无需访问令牌。发送 Content-Type: application/json 和 {data:{email:...},consent:true}。对于新地址和已有地址,均返回相同的通用成功响应。
<form id="signup-form">
<fieldset id="signup-fields" disabled>
<legend>Subscribe to email updates</legend>
<label id="email-label" for="signup-email">Email address</label>
<input id="signup-email" type="email" autocomplete="email" required>
<label for="signup-consent">
<input id="signup-consent" type="checkbox" required>
<span id="consent-label"></span>
</label>
<a id="privacy-link" href="/privacy">Privacy notice</a>
<button id="signup-submit" type="submit">Subscribe</button>
</fieldset>
<p id="signup-status" role="status" aria-live="polite"></p>
</form>
<script type="module">
const endpoint = "https://api.smartest.email/api/v1/public/forms/FORM_ID";
const form = document.querySelector("#signup-form");
const fields = document.querySelector("#signup-fields");
const button = document.querySelector("#signup-submit");
const status = document.querySelector("#signup-status");
let ready = false;
let submitting = false;
form.addEventListener("submit", async event => {
event.preventDefault();
if (!ready || submitting || !form.reportValidity()) return;
submitting = true;
button.disabled = true;
try {
const response = await fetch(endpoint + "/submit", {
method: "POST",
credentials: "omit",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
data: { email: document.querySelector("#signup-email").value.trim() },
consent: document.querySelector("#signup-consent").checked
})
});
const result = await response.json();
status.textContent = result.message || "Please try again.";
if (response.ok) form.reset();
} catch {
status.textContent = "Please try again.";
} finally {
submitting = false;
button.disabled = false;
}
});
try {
const response = await fetch(endpoint, { credentials: "omit" });
if (!response.ok) throw new Error("Signup form unavailable");
const config = await response.json();
document.querySelector("#email-label").textContent =
config.fields.find(field => field.name === "email").label;
document.querySelector("#consent-label").textContent =
config.fields.find(field => field.name === "consent").label;
if (config.privacyUrl) {
document.querySelector("#privacy-link").href = config.privacyUrl;
}
ready = true;
fields.disabled = false;
} catch (error) {
status.textContent = error.message;
}
</script>替换 FORM_ID 为您的表单 UUID,并将备用 /privacy 链接设为您自己的隐私声明(如果表单没有配置 privacyUrl)。本示例处理两个必填字段;请根据已发布配置渲染并发送其他必填字段。
接受响应
{"success":true,"message":"Thank you for subscribing."}响应有意不透露地址是否已存在。已有订阅和退订联系人的状态保持不变;只有新接受的地址才会增加订阅者和同意记录。
仅发送已发布的 fields 数组中存在的字段名。必须提供必填字段。其他字段中,复选框值应为布尔值,数值应为数字字符串,日期格式为 YYYY-MM-DD,单选或下拉选项值必须与 options中列出的完全一致。API 会在服务器上再次验证数据。
可选的顶层 website 属性是防垃圾提交的蜜罐字段。正常提交时请省略或留空。非空值会返回通用成功响应,但不会添加联系人;它不是用于填写访客网站网址的字段。
处理失败和流量限制。
公开表单接口允许外部网站发起带 Content-Type 和 Accept 标头的跨源 GET、POST 和 OPTIONS 请求。浏览器请求请使用 credentials: omit,无需账户 Cookie、Authorization 标头或 API 密钥。
| 状态 | 含义 |
|---|---|
| 200 | 配置获取成功或提交已接受;重复地址获得相同的提交响应。 |
| 400 / 415 | JSON 格式错误/请求格式不是 application/json. |
| 404 | 表单未知、为草稿、未发布或无效。 |
| 409 | 账户已达到联系人上限。 |
| 413 / 422 | 正文超过 16 KiB/同意标记或字段数据无效。 |
| 429 | 已达到速率限制;浏览器代码可使用 JSON 正文中的 retry_after 。服务器集成也可读取 Retry-After. |
错误采用 {"error":true,"message":"..."};速率限制错误还包括 retry_after ,单位为秒。表单每个 IP 每分钟最多允许 30 次请求,其中提交次数最多为 10 次。您网站的内容安全策略必须允许访问 https://api.smartest.email 出现在 connect-src中;现成嵌入组件还需要将其允许在 script-src.
当您自己的服务器转发提交时,这些提交会共用该服务器的出口 IP 来计算请求速率限制。
同意与投递
新接受的订阅会创建已订阅联系人,并保存表单同意声明、时间及请求提供的来源。访客提交前,自定义界面必须显示表单同意文字和隐私说明。记录的请求来源不能证明身份。此接口采用单次订阅确认;不会发送确认邮件,也不会验证邮箱所有权。

