跳转到内容
FFormhook

Formhook 文档

将您的 API 密钥粘贴到表单的 action 中,在仪表盘中接收提交,每条新提交都收到系统推送通知。

快速开始

1. 注册 并验证您的邮箱。
2. 在仪表盘中创建表单。复制 API 密钥(形如 fh_…)。
3. 将其粘贴为 HTML 表单的 action。就这样。

HTML 表单(无 JavaScript)

最简单的集成。原生表单提交,可选成功后重定向。

<form action="https://formhook.app/f/YOUR_API_KEY" method="POST">
  <input name="email" type="email" required>
  <textarea name="message" required></textarea>

  <!-- honeypot: bots fill this, humans don't see it -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off"
         style="position:absolute;left:-9999px">

  <!-- where to send the user after success -->
  <input type="hidden" name="_redirect" value="https://example.com/thanks">

  <button type="submit">Send</button>
</form>

JavaScript fetch

await fetch("https://formhook.app/f/YOUR_API_KEY", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    email: "user@example.com",
    message: "Hello",
  }),
});

React 组件

"use client";
import { useState } from "react";

export function ContactForm() {
  const [status, setStatus] = useState<"idle" | "sending" | "ok" | "error">("idle");

  async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setStatus("sending");
    const data = Object.fromEntries(new FormData(e.currentTarget));
    const res = await fetch("https://formhook.app/f/YOUR_API_KEY", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(data),
    });
    setStatus(res.ok ? "ok" : "error");
  }

  if (status === "ok") return <p>Thanks - we'll be in touch.</p>;

  return (
    <form onSubmit={onSubmit}>
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button disabled={status === "sending"}>Send</button>
      {status === "error" && <p>Something went wrong. Try again.</p>}
    </form>
  );
}

WordPress

Formhook 是表单后端,因此任何 WordPress 表单都可以向它发送数据。请根据你的情况选择:

原生 HTML 表单

在页面或区块中放入一个普通表单,将其 action 指向你的端点。把你网站的来源添加到表单的“允许的来源”中,以便接受浏览器提交。

<form action="https://formhook.app/f/YOUR_API_KEY" method="POST">
  <input name="email" type="email" required>
  <textarea name="message" required></textarea>
  <input type="hidden" name="_redirect" value="https://your-site.com/thanks">
  <button type="submit">Send</button>
</form>

Contact Form 7

在主题的 functions.php 中将每个 Contact Form 7 提交转发出去。由于请求来自你的服务器(没有浏览器 Origin),需使用 X-Auth-Token 进行认证:

// functions.php - forward Contact Form 7 submissions to Formhook
add_action('wpcf7_before_send_mail', function ($contact_form) {
  $submission = WPCF7_Submission::get_instance();
  if (!$submission) return;
  $data = $submission->get_posted_data();

  wp_remote_post('https://formhook.app/f/YOUR_API_KEY', [
    'headers' => [
      'Content-Type' => 'application/json',
      'X-Auth-Token' => 'YOUR_AUTH_TOKEN',
    ],
    'body'    => wp_json_encode($data),
    'timeout' => 10,
  ]);
});

Gravity Forms

使用官方 Webhooks 插件向你的端点发送数据,或在 functions.php 中用相同的服务器端令牌转发:

// functions.php - forward Gravity Forms submissions to Formhook
add_action('gform_after_submission', function ($entry, $form) {
  $payload = [];
  foreach ($form['fields'] as $field) {
    $payload[$field->label] = rgar($entry, (string) $field->id);
  }

  wp_remote_post('https://formhook.app/f/YOUR_API_KEY', [
    'headers' => [
      'Content-Type' => 'application/json',
      'X-Auth-Token' => 'YOUR_AUTH_TOKEN',
    ],
    'body'    => wp_json_encode($payload),
    'timeout' => 10,
  ]);
}, 10, 2);

在表单的设置 → 服务器请求认证下生成令牌。有关浏览器请求与服务器请求的区别,请参阅来源与认证

cURL(测试)

服务器到服务器的请求(curl、后端代码)不发送 Origin 请求头,因此必须使用 X-Auth-Token 请求头进行认证。在表单的设置 → 服务器请求认证中生成令牌,并在每个请求中传递它--否则服务器请求将被拒绝。

curl -X POST https://formhook.app/f/YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: YOUR_AUTH_TOKEN" \
  -d '{"email":"test@example.com","message":"hi"}'

保留字段

这三个字段名会从存储的 payload 中剥离:

  • _gotcha - honeypot。这里任何非空值都返回 200 成功,但静默丢弃提交且不消耗您的配额。
  • _redirect - 成功时重定向的 URL(仅 form-encoded 提交)。必须在表单的允许列表中的 origin 上;否则被忽略。
  • cf-turnstile-response - Turnstile 令牌,服务端验证。

CORS

对于浏览器提交,请在设置中将来源(协议 + 主机,不含路径)添加到表单的允许来源。空列表将完全拒绝来自浏览器来源的请求。服务器到服务器的请求(无 Origin 请求头,例如 curl 或后端)必须发送在表单设置中生成的有效 X-Auth-Token 请求头;没有令牌将被拒绝。

Cloudflare Turnstile(可选)

在表单设置中启用 Turnstile,然后在 HTML 中嵌入小部件:

<script src="https://challenges.cloudflare.com/turnstile/v0/api.js"
        async defer></script>

<form action="https://formhook.app/f/YOUR_API_KEY" method="POST">
  <input name="email" type="email" required>
  <div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITEKEY"></div>
  <button type="submit">Send</button>
</form>

文件上传(Pro 和 Studio)

表单可以接受文件附件。使用 enctype="multipart/form-data" 提交;每个 <input type="file"> 字段都会上传到对象存储,并在仪表盘中与提交关联。文件通过短 TTL 的签名 URL 从仪表盘下载;没有任何内容公开可寻址。

<!-- enctype is the important bit -->
<form action="https://formhook.app/f/YOUR_API_KEY" method="POST" enctype="multipart/form-data">
  <input name="email" type="email" required>
  <textarea name="message" required></textarea>

  <!-- one or more file inputs; same field name = multiple files -->
  <input name="attachment" type="file">

  <button type="submit">Send</button>
</form>

单文件上限:10 MB。每账户存储:Pro 1 GB,Studio 10 GB。软失败响应:{ok: true, warnings: ["file_too_large"]} 当单个文件超过单文件上限,["storage_full"] 当账户达到配额,["files_not_configured"] 若运营方未配置对象存储。任何情况下文本提交仍会保存。

Studio:配置并转交客户表单

Studio 账户可以为客户构建表单并交付。在拥有期间由 Studio 配置与测试;转交后客户拥有表单及其提交,Studio 仅可见元数据。

  1. 在新建表单中填写可选的客户邮箱字段。表单被标记为客户表单(赞助关系已记录),但您仍是所有者。
  2. 正常配置和测试表单 - 测试期间提交进入您的仪表盘。
  3. 准备好后,打开表单设置 → 客户转交 部分并发送邀请。表单保持活跃,等待期间继续接受提交。
  4. 客户通过邮件收到链接,注册(邮箱通过令牌持有自动验证)并点击认领。从此提交进入他们的仪表盘。

认领后,您可在 客户控制台 看到该表单及状态 + 月度计数。您不再看到提交内容或附件 - 这是 GDPR 边界,让您远离客户的数据链。提交仍计入您的 Studio 配额(账单上滚);客户本人可以保持免费而不影响功能。

导入提交

通过 JSON 文件将提交批量导入到表单中--在仪表板中打开该表单,点击导入,然后上传如下结构的文件:

{
  "schemaVersion": 1,
  "submissions": [
    {
      "payload": { "email": "ada@example.com", "message": "Hello" },
      "createdAt": "2025-01-02T15:04:05Z"
    }
  ]
}

顶层可以是条目数组,也可以是带有 submissions 数组的对象。每个条目需要一个 payload 对象;createdAt(ISO-8601)为可选项,若存在则会保留。导入的提交会被标记为已读,不会触发任何通知或 webhook,每个文件上限为 5000 条(最大 5 MB)。

速率限制和配额

  • 跨所有表单按 IP:每分钟 10 次请求 → 429 加 Retry-After
  • 按表单:每分钟 60 次请求 → 429。
  • 请求体大小上限:64 KB → 413。
  • 免费层每月配额:滚动 30 天窗口内 250 次提交。超过配额仍返回 200,body 中带 warnings: ["over_quota"] - 提交永不被静默丢弃。

错误响应

HTTP代码含义
200ok成功
302-重定向到 _redirect URL
400invalid_body格式错误的 JSON / 不支持的 content-type
403origin_not_allowedCORS 允许列表不匹配
403turnstile_failedTurnstile 令牌缺失/无效
403account_suspended表单所有者被暂停
404form_not_found未知 api_key
413body_too_large请求体 > 64 KB
429rate_limited包含 Retry-After 头
500internal_error请重试或上报

推送通知(给您,表单所有者)

打开您的仪表盘,点击启用通知 - 浏览器会请求权限,然后每条新提交都会触发系统通知,即使标签页已关闭。在 iOS 上请先将 Formhook 安装到主屏幕(Safari 16.4+)。