开发者

Formalini API

读取文档及其提取的字段、从您的自有系统推送文档、更正数值,并在内容发生变化时接收签名 Webhook。以下每个示例均提供 curl、JavaScript、Python、PHP 和 C# 版本。

Base URL

所有端点均位于此 Base URL 下。以 JSON 格式发送和接收数据。

https://api.formalini.com/api/public/v1

短地址 api.formalini.com 已上线;即上方显示的地址。

身份验证

在应用的“集成”下创建 API 密钥,然后在每次请求中将其作为 Bearer Token 发送。密钥归属于单个工作区,且仅能访问该工作区的数据。

  • documents.read — 读取文档及其字段
  • documents.write — 创建文档并更正字段
  • workflows.run — 按需向您的 Webhook 发送事件

每个密钥都有每分钟请求数限制(默认 60 次)。响应头包含 X-RateLimit-Limit 和 X-RateLimit-Remaining;超出限制将返回附带 Retry-After 的 429 错误。

获取文档列表

GET /documents?limit=25&status=approved — documents.read

按最新优先排序。limit 接受 1–100(默认 25),status 可按 queued、processing、review、approved、exported 或 failed 过滤。

curl -X GET "https://api.formalini.com/api/public/v1/documents?limit=25&status=approved" \
  -H "Authorization: Bearer pf_live_your_key"
{
  "documents": [
    {
      "id": "b0f1c8d2-5a71-4c0e-9f2a-1d4b6e8c3a90",
      "name": "Job sheet 4821.jpg",
      "status": "approved",
      "average_confidence": 0.94,
      "source": "upload",
      "created_at": "2026-09-11T09:12:04.000Z",
      "fields": [
        { "name": "engineer", "value": "M. Byrne", "type": "text",
          "confidence": 0.97, "page": 1, "valid": true }
      ]
    }
  ]
}

更正后的值会替换原始识别结果,因此 value 始终是您应当使用的有效值。

获取单个文档

GET /documents/{id} — documents.read

返回包含类别、供应商、金额、日期以及每个字段的完整文档信息。

curl -X GET "https://api.formalini.com/api/public/v1/documents/b0f1c8d2-5a71-4c0e-9f2a-1d4b6e8c3a90" \
  -H "Authorization: Bearer pf_live_your_key"

上传文件并获取数据

POST /documents/upload — documents.write

发送单页图像,并在同一次响应中直接返回结构化数据。该文档会像普通扫描件一样出现在工作台收件箱中,每页消耗 1 点 AI 额度,并在处理完成后触发您的 Webhook。

  • file — 单页图像;对于多页文档,请为每页重复此字段(最多 25 页)
  • name — 可选文档名称;留空时将使用文件名
  • template_id — 可选模板 ID,以便字段以您的自定义名称返回
  • async — 设置为 true 可立即获得 202 响应,并通过轮询 GET /documents/{id} 获取结果
curl -X POST "https://api.formalini.com/api/public/v1/documents/upload" \
  -H "Authorization: Bearer pf_live_your_key" \
  -F "file=@invoice-page-1.jpg" \
  -F "file=@invoice-page-2.jpg" \
  -F "name=Invoice 2026-0041"
{
  "document": {
    "id": "8c2a1f76-4d33-4a1e-8f10-72b9e5c4a118",
    "name": "Invoice 2026-0041",
    "status": "review",
    "average_confidence": 0.93,
    "vendor": "Northbridge Supplies",
    "amount": 1249.5,
    "currency": "EUR",
    "document_date": "2026-09-14",
    "pages": 2,
    "credits_spent": 2,
    "fields": [
      { "name": "invoice_number", "value": "2026-0041", "type": "string",
        "confidence": 0.98, "page": 1, "valid": true }
    ]
  }
}

支持 JPEG、PNG、WebP 和 HEIC 格式,每页最高 100 MB。单页识别需要数秒,请设置充足的超时时间或使用 async=true。402 错误表示工作区的额度、存储空间或每日配额已耗尽,响应中会指明具体原因。

此处暂不支持直接上传 PDF:请将每页渲染为 JPEG 或 PNG,并通过多个重复的 file 字段发送。应用内仍支持上传 PDF,因为页面会在您的浏览器中完成渲染。

创建文档

POST /documents — documents.write

推送您已有数据的文档。name 为必填项;fields 为选填项,最多包含 400 个条目。包含 fields 的文档将进入 review 状态,未包含的将进入 queue 状态。

curl -X POST "https://api.formalini.com/api/public/v1/documents" \
  -H "Authorization: Bearer pf_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Invoice 2026-0041.pdf",
    "category": "invoice",
    "vendor": "Northbridge Supplies",
    "amount": 1249.5,
    "document_date": "2026-09-14",
    "fields": [
      {
        "name": "invoice_number",
        "value": "2026-0041",
        "type": "text",
        "confidence": 0.99,
        "page": 1
      },
      {
        "name": "total",
        "value": "1249.50",
        "type": "number",
        "confidence": 0.96,
        "page": 1
      }
    ]
  }'
{
  "document": {
    "id": "8c2a…",
    "name": "Invoice 2026-0041.pdf",
    "status": "review",
    "created_at": "2026-09-18T10:02:11.000Z",
    "fields": 2
  }
}

更新文档

PATCH /documents/{id} — documents.write

更改状态或按名称更正字段值。将状态设为 approved、exported 或 review 还会触发相应的 Webhook。

curl -X PATCH "https://api.formalini.com/api/public/v1/documents/b0f1c8d2-5a71-4c0e-9f2a-1d4b6e8c3a90" \
  -H "Authorization: Bearer pf_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "approved",
    "fields": [
      {
        "name": "total",
        "value": "1249.50"
      }
    ]
  }'
{ "ok": true, "updated_fields": 1 }

触发 Webhook

POST /workflows/dispatch — workflows.run

向订阅了该事件的所有活跃 Webhook 发送事件,以便按需启动下游系统。支持的事件:document.processed、document.needs_review、document.approved、document.exported。

curl -X POST "https://api.formalini.com/api/public/v1/workflows/dispatch" \
  -H "Authorization: Bearer pf_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "document.approved",
    "document_id": "b0f1c8d2-5a71-4c0e-9f2a-1d4b6e8c3a90",
    "payload": {
      "target": "erp"
    }
  }'
{ "ok": true, "event": "document.approved", "document_id": "b0f1…" }

Webhooks

在应用中添加 Webhook URL 并选择所需的事件。每次投递均为包含 JSON 请求体和签名标头的 POST 请求,使用您的 Webhook 密钥进行签名。

POST https://your-app.example.com/hooks/formalini
Content-Type: application/json
X-Paperflow-Signature: sha256=<hex hmac of the raw body>

{ "event": "document.processed", "document_id": "b0f1…", "payload": { … } }

投递失败最多会自动重试五次(延迟递增),您也可以在应用中手动重放任何投递记录。

在信任投递内容之前,请对照原始请求体验证签名:

// Node.js
import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const got = (header || "").replace(/^sha256=/, "");
  return got.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
}

# Python
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, (header or "").removeprefix("sha256="))

// PHP
$expected = hash_hmac('sha256', $rawBody, $secret);
$got = str_replace('sha256=', '', $_SERVER['HTTP_X_PAPERFLOW_SIGNATURE'] ?? '');
$ok = hash_equals($expected, $got);

// C#
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var expected = Convert.ToHexString(hmac.ComputeHash(rawBodyBytes)).ToLowerInvariant();
var ok = CryptographicOperations.FixedTimeEquals(
    Encoding.UTF8.GetBytes(expected),
    Encoding.UTF8.GetBytes(header.Replace("sha256=", "")));

错误码

  • 400 — 请求体不是合法的 JSON,或者缺少必填值/包含无效值
  • 401 — 缺少密钥,或密钥已被撤销/已过期
  • 403 — 密钥缺少此端点所需的权限
  • 404 — 此工作区中不存在具有该 ID 的文档
  • 429 — 已达每分钟请求上限——请稍后重试
  • 500 — 服务端发生错误;响应中会提供具体信息