DATA CONTRIBUTION API · V1

把新文章投递给号外

面向已获授权的数据贡献方。一次请求可投递 1–100 篇文章,通过签名和字段校验后会立即进入公开索引与 RSS。

接口地址

POSThttps://haowai.thesignalwise.com/api/v1/webhooks/articles

请求体必须是 UTF-8 JSON,最大 256 KiB。完整机器可读定义见 OpenAPI 文档。

Key ID 和 Secret 由服务管理员线下提供。公开文档永远不会展示真实凭据。

请求签名

每次请求发送四个认证头:

Content-Type: application/json
X-Webhook-Key-Id: YOUR_KEY_ID
X-Webhook-Timestamp: UNIX_SECONDS
X-Webhook-Nonce: A_NEW_UUID
X-Webhook-Signature: v1=LOWERCASE_HEX_HMAC

签名原文是以下 UTF-8 字节,换行符固定为 :

v1
{timestamp}
{nonce}
{raw request body}

使用共享 Secret 计算 HMAC-SHA256,并编码为小写十六进制。时间戳默认只能与服务器时间相差五分钟。

Node.js 示例

import { createHmac, randomUUID } from "node:crypto";

const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = randomUUID();
const body = JSON.stringify(event);
const signed = `v1\n${timestamp}\n${nonce}\n${body}`;
const signature = createHmac("sha256", process.env.WEBHOOK_SECRET)
  .update(signed, "utf8")
  .digest("hex");

await fetch("https://haowai.thesignalwise.com/api/v1/webhooks/articles", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-webhook-key-id": process.env.WEBHOOK_KEY_ID,
    "x-webhook-timestamp": timestamp,
    "x-webhook-nonce": nonce,
    "x-webhook-signature": `v1=${signature}`
  },
  body
});

文章数据

{
  "schema_version": "1.0",
  "event_id": "018f5d53-f856-7d26-9b6d-a87d5e2c10f3",
  "source": "wechat-monitor",
  "sent_at": "2026-09-28T07:03:20+08:00",
  "articles": [{
    "external_id": "optional-stable-id",
    "title": "文章标题",
    "url": "https://mp.weixin.qq.com/s/example",
    "description": "文章描述",
    "author": { "id": "author-id", "name": "作者" },
    "account": { "id": "account-id", "name": "公众号名称" },
    "published_at": "2026-09-28T07:00:00+08:00",
    "discovered_at": "2026-09-28T07:03:12+08:00",
    "extra": { "monitor_rule": "news" }
  }]
}

title、url、account.id、account.name 和 discovered_at 是文章必填字段。作者、真实发布时间、描述、封面、语言和 extra 均可按实际发现能力补充。

所有时间使用带时区的 RFC 3339 格式。扩展数据必须放在 extra 对象中;未知的顶层字段会被拒绝,避免拼写错误静默丢失。

响应与去重

{
  "event_id": "018f5d53-f856-7d26-9b6d-a87d5e2c10f3",
  "status": "accepted",
  "counts": { "received": 1, "created": 1, "updated": 0, "duplicate": 0 },
  "request_id": "01..."
}

同一逻辑事件重试时保持 event_id 不变。每一次 HTTP 尝试都生成新的 nonce、时间戳和签名。号外会同时根据外部文章 ID 和规范化 URL 去重。

可靠重试

  • 只自动重试 429、500、502、503 和 504。
  • 使用带随机抖动的指数退避,并遵循 Retry-After。
  • 监控程序本地保留 outbox,直到收到成功响应。
  • 认证或校验错误应先修正配置/数据,不要盲目重试。