跳转到内容

开发者指南 · 订阅表单

打造自己的订阅体验。

保留自己的 HTML、样式和网站框架。通过公开订阅接口,将自定义表单连接到已发布的 smartest.email 表单。

本指南涵盖

公开表单配置,以及来自任意网站源的订阅

同意、验证、响应和速率限制

使用表单提供的网址。

在“订阅表单”中创建表单,添加必填邮箱字段和必选同意复选框,然后发布。“安装”选项卡会显示该表单的准确网址。

基础网址

https://api.smartest.email/api/v1/public/forms/{FORM_ID}

使用表单“安装”选项卡中显示的网址。在下方示例中,将 FORM_ID 替换为已发布表单的 ID。

1

创建

设置字段和明确的同意声明。

2

发布

新表单初始为草稿;发布之前,公开路由返回 404。

3

集成

使用嵌入代码,或调用下方的两个公开接口。

使用自己的 HTML 表单。

支持直接提交。在 smartest.email 中创建并发布表单以定义字段和同意声明,然后在现有网站表单中使用该表单 ID。您可以选择嵌入式表单类型,而不安装组件。

您的网站可以通过 JavaScript 提交 fetch(),也可以由您自己的服务器将订阅转发为 JSON。公开接口无需 API 密钥、访问令牌或 smartest.email 嵌入脚本。

POST
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/submit

将 FORM_ID 替换为已发布表单的 UUID。发送下方请求正文时,请使用 Content-Type: application/json。

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。

渲染您发布的字段。

此公开接口无需访问令牌。显示表单时获取配置,然后在自定义界面中使用返回的字段名、同意声明和隐私网址。

GET
https://api.smartest.email/api/v1/public/forms/{FORM_ID}

返回 200 及可安全渲染的启用表单数据。每次成功获取都会增加浏览次数,请避免轮询。

GET
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/embed.js

现成的内嵌式或弹窗组件脚本。如无需自定义界面,请使用“安装”中的准确脚本网址。

GET
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/landing

启用表单的托管订阅页面。

GET /api/v1/public/forms/{FORM_ID} · 响应示例
{
  "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。

POST
https://api.smartest.email/api/v1/public/forms/{FORM_ID}/submit

无需访问令牌。发送 Content-Type: application/json 和 {data:{email:...},consent:true}。对于新地址和已有地址,均返回相同的通用成功响应。

HTML + 浏览器 JavaScript · 邮箱和同意表单
<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)。本示例处理两个必填字段;请根据已发布配置渲染并发送其他必填字段。

接受响应

JSON
{"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 / 415JSON 格式错误/请求格式不是 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 来计算请求速率限制。

同意与投递

新接受的订阅会创建已订阅联系人,并保存表单同意声明、时间及请求提供的来源。访客提交前,自定义界面必须显示表单同意文字和隐私说明。记录的请求来源不能证明身份。此接口采用单次订阅确认;不会发送确认邮件,也不会验证邮箱所有权。