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 仅可见元数据。
- 在新建表单中填写可选的客户邮箱字段。表单被标记为客户表单(赞助关系已记录),但您仍是所有者。
- 正常配置和测试表单 - 测试期间提交进入您的仪表盘。
- 准备好后,打开表单设置 → 客户转交 部分并发送邀请。表单保持活跃,等待期间继续接受提交。
- 客户通过邮件收到链接,注册(邮箱通过令牌持有自动验证)并点击认领。从此提交进入他们的仪表盘。
认领后,您可在 客户控制台 看到该表单及状态 + 月度计数。您不再看到提交内容或附件 - 这是 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 | 代码 | 含义 |
|---|---|---|
| 200 | ok | 成功 |
| 302 | - | 重定向到 _redirect URL |
| 400 | invalid_body | 格式错误的 JSON / 不支持的 content-type |
| 403 | origin_not_allowed | CORS 允许列表不匹配 |
| 403 | turnstile_failed | Turnstile 令牌缺失/无效 |
| 403 | account_suspended | 表单所有者被暂停 |
| 404 | form_not_found | 未知 api_key |
| 413 | body_too_large | 请求体 > 64 KB |
| 429 | rate_limited | 包含 Retry-After 头 |
| 500 | internal_error | 请重试或上报 |
推送通知(给您,表单所有者)
打开您的仪表盘,点击启用通知 - 浏览器会请求权限,然后每条新提交都会触发系统通知,即使标签页已关闭。在 iOS 上请先将 Formhook 安装到主屏幕(Safari 16.4+)。