← 文档 / 指南
API 规范
请求结构、权限范围、列表查询、金额、错误处理、幂等性与频率限制。
请求结构
每个操作均采用 RPC 风格的 POST 请求:
POST /v1/{module}/{resource}/{action}
模块包括: reference、partners、catalog、sales、purchases、ledger、bank、declarations、files、webhooks、audit。操作动词包括: create、get、update、delete、list,以及领域特定的操作,例如 issue、register、match、apply-advance、generate。
认证与权限范围 (Scopes)
Authorization: Bearer <api key>。API 密钥带有在每个操作中进行检查的权限范围 (Scopes):
{module}:read- 获取或列出操作{module}:write- 变更操作{module}:*或*- 通配符
缺少权限范围将返回 403 forbidden。密钥缺失或无效将返回 401 unauthorized。
列表查询
所有 list 操作均遵循统一的契约:
{
"page": 1,
"pageSize": 50,
"sort": [{ "field": "createdAt", "dir": "desc" }],
"filter": [{ "field": "paymentStatus", "op": "eq", "value": "unpaid" }]
}
过滤运算符: eq (等于)、ne (不等于)、contains (不区分大小写的文本匹配,仅限文本字段)、gte (大于或等于)、lte (小于或等于)、in (最多 500 个值的列表中的任意一个)。可排序和可过滤的字段在每个端点中均有白名单限制 (参见 OpenAPI 规范);未知字段将返回 400 错误。在请求中添加 totals: ["field", …] 可对所有匹配行的数值字段求和;当记录带有币种时,totalsByCurrency 会给出按币种代码拆分的相同总和。响应结构为: { rows, total, page, pageSize, totals?, totalsByCurrency? }.
金额与日期
货币金额为最多保留 4 位小数的十进制字符串 ("121.0000"),绝不使用浮点数。增值税 (VAT) 按明细行计算,采用四舍五入 (half-up) 方式。日期格式为 YYYY-MM-DD;时间戳格式为 ISO 8601 UTC。
错误处理
全局采用统一的错误信封结构:
{
"error": {
"code": "validation",
"message": "Request validation failed",
"requestId": "k1x…",
"key": "request_invalid",
"fieldErrors": { "lines.0.unitPriceExclVat": ["Invalid value"] }
}
}
错误代码包括: validation (400/422,当上传的文件或请求正文过大时为 413)、unauthorized (401)、payment_required (402,试用期或预付额度已到期;错误信息会说明如何继续)、forbidden (403)、not_found (404)、conflict (409)、idempotency_key_reuse (422)、idempotency_in_progress (409)、rate_limited (429)、internal (5xx)。当错误信息属于固定提示信息之一时,key 字段会指明其键名,以便客户端显示其自身的翻译,而 params 字段则包含填充到其中的值 (例如,对于锁定的会计期间,其值为 { "period": "2026-09" })。在报告问题时,请务必提供 requestId。
幂等性
在任何变更性调用中添加 Idempotency-Key: <any string ≤255 chars> 即可实现幂等性:
- 使用相同的密钥和有效负载重试 -> 将按字节原样重播存储的响应,并带有
x-idempotent-replay: true标头。 - 密钥相同但有效负载不同 -> 返回 422
idempotency_key_reuse。 - 在首次请求仍在处理时发生并发重复请求 -> 返回 409
idempotency_in_progress。 - 密钥归属于发送它们的 API 密钥或用户,并且在调用所针对的公司范围内有效,因此同一公司的两个集成系统可以使用相同的密钥。在公司成立之前进行的调用以及
account/*调用,其密钥是按用户隔离的,因此重复调用account/companies/create只会创建一个公司。 - 只有在调用方再次通过相同的权限检查 (权限范围、角色、公司和账单状态) 后,才会返回存储的响应;否则,调用将获取当前的错误信息。
- 密钥在 24 小时后过期。错误响应 (4xx) 也会被重播;5xx 响应不会被存储,因此重试会重新执行。
频率限制
每个 API 密钥每分钟限制 300 个请求 (基于所提供的凭据;未认证的请求按 IP 地址限制)。收到 429 响应时,请遵循 retry-after 标头。x-ratelimit-* 标头报告当前的额度预算。
Webhooks
事件将被写入事务性发件箱 (transactional outbox),并与变更处于同一数据库事务中。这意味着回滚的文档绝不会发出事件,也不会丢失任何事件。投递机制:
- 向您的 URL 发送包含事件 JSON 的
POST请求 x-nordlet-timestamp: <unix seconds>,即投递签名的时间x-nordlet-signature-v2: t=<timestamp>,v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>">,使用您的订阅密钥进行签名x-nordlet-signature: sha256=<hex HMAC-SHA256 of the raw body>,为兼容现有接收方而保持不变x-nordlet-delivery包含投递 ID,同一投递的所有重试均保持该 ID 不变- 如果返回非 2xx 响应或重定向,系统将分别在 30 秒、2 分钟、10 分钟、30 分钟、1 小时、3 小时、6 小时和 12 小时后进行重试 (总共约 24 小时),之后该投递将被标记为失败
- 每个投递由单个服务器一次发送一个,因此运行多个 API 实例不会导致重复发送
在信任有效负载之前,请使用恒定时间比较 (constant-time comparison) 验证 x-nordlet-signature-v2,并拒绝时间戳与您的系统时钟相差超过 5 分钟的投递。由于时间戳是签名文本的一部分,因此捕获的投递无法在稍后使用新的时间戳进行重播。请存储您已处理的 x-nordlet-delivery ID,以便在该时间窗口内忽略重复投递。失败的投递可以通过 webhooks/deliveries/redeliver 重新发送。
审计
每一项变更都会被记录 (actor、action、entity、diff),并且可以通过 audit/list 进行查询。
会计保证
- 日记账分录保持借贷平衡,这由提交时的延迟数据库触发器 (deferred database trigger) 强制执行,而不仅依靠应用程序代码。
- 按系列或年度划分的凭证编号连续无间隙,编号分配具备并发安全性。
- 锁定的会计期间会拒绝任何记账日期落入其中的凭证 (返回 409)。
- 软删除的文档会保留其审计线索;已发布或已登记的文档无法删除。