API 参考

n8n 出站交付

使用 n8n 对已复核 Numora 文档做出站集成的推荐模式,包括 outbox、dispatch 与可选的 delivery callback。

范围

本页描述如何通过 n8n 把已复核的 Numora 文档接到下游系统。

示例中的当前 provider 为 dynamics365

推荐模式

Numora 当前支持两种出站交付模式。

1. Numora 托管下游执行

这是面向多租户 SaaS 的推荐模式。

  • 终端用户在 Numora 内配置下游 connection 和 workflow。
  • n8n 只负责轮询 delivery outbox 并调用 dispatch
  • 真正的下游写入由 Numora 复用已保存的 connection 与内部 workflow 来完成。
  • n8n 不需要保存每个用户各自的 Dataverse 凭证。

推荐流程:

  1. GET /v1/delivery/outbox
  2. POST /v1/documents/{id}/dispatch
  3. 轮询 GET /v1/documents/{id},直到文档离开 push_pending

在这个模式下,不需要再调用 POST /v1/documents/{id}/delivery

2. 外部执行器 + delivery callback

只有当下游写入动作发生在 Numora 外部时,才使用这个模式。

  • n8n 或其它 worker 先读取文档结果。
  • 外部执行器直接写入目标系统。
  • 外部执行器再调用 delivery callback,把成功或失败回写给 Numora。
  • 如果你仍希望终端用户在 Numora 内维护 Dynamics connection,则 n8n 可以向 Numora 申请短期 Dataverse token,而不是自己保存 client secret。

推荐流程:

  1. GET /v1/delivery/outbox
  2. GET /v1/documents/{id}/result
  3. POST /v1/dynamics/session
  4. 写入目标系统
  5. POST /v1/documents/{id}/delivery

在这个模式下,不要调用 POST /v1/documents/{id}/dispatchdispatch 仍然会回到 Numora 内部 workflow engine,并不能替代旧 workflow 逻辑。

轮询 Delivery Outbox

GET /v1/delivery/outbox?provider=dynamics365&limit=100

请求示例:

curl "https://api.numora.example/v1/delivery/outbox?provider=dynamics365&limit=100" \
  -H "Authorization: Bearer $NUMORA_API_KEY" \
  -H "x-request-id: n8n-outbox-001"

响应示例:

{
  "data": [
    {
      "document_id": "4be38121-3791-4856-80d5-c57dbec2cf78",
      "schema": "invoice_v2",
      "status": "reviewed",
      "external_id": null,
      "metadata": {},
      "result_url": "/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78/result",
      "dispatch_url": "/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78/dispatch",
      "updated_at": "2026-03-24T02:36:37.684Z"
    }
  ],
  "meta": {
    "provider": "dynamics365",
    "next_cursor": null
  }
}

说明:

  • outbox 返回的是当前可投递的、已复核且已 finalized 的文档。
  • 已经处于 push_pendingpush_succeeded 的文档会被排除。
  • next_cursor 是不透明游标,分页时应原样传回。

Dispatch 已复核文档

POST /v1/documents/{id}/dispatch

请求示例:

curl -X POST "https://api.numora.example/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78/dispatch" \
  -H "Authorization: Bearer $NUMORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-request-id: n8n-dispatch-001" \
  -d '{
    "provider": "dynamics365"
  }'

响应示例:

{
  "data": {
    "id": "4be38121-3791-4856-80d5-c57dbec2cf78",
    "schema": "invoice_v2",
    "status": "push_pending",
    "external_id": null,
    "metadata": {},
    "review_state": {
      "version": 1,
      "confirmed_general_field_names": ["invoice_number"],
      "confirmed_line_item_ids": []
    },
    "duplicate_of": null,
    "created_at": "2026-03-20T20:25:04.644Z",
    "updated_at": "2026-03-24T02:36:37.684Z",
    "status_url": "/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78",
    "result_url": "/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78/result",
    "dispatch_url": "/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78/dispatch"
  },
  "meta": {
    "run_id": "18e1224c-fb6c-429e-89a3-9d3ee96ff7a9"
  }
}

说明:

  • dispatch 是异步动作。202 Accepted 表示已入队,不代表已完成。
  • 文档如果还没达到 reviewed + finalized,会返回 409 conflict
  • 如果当前没有可用的下游 workflow,也会返回 409 conflict

Dispatch 后轮询文档状态

GET /v1/documents/{id}

推荐这样解释状态:

  • push_pending:下游执行仍在排队或运行中
  • push_succeeded:下游执行成功完成
  • push_failed:最近一次下游执行失败,需要重试或人工处理

对于推荐的 dispatch 模式,这个文档状态资源就是 n8n 判断完成与否的主信号。

记录外部交付结果

POST /v1/documents/{id}/delivery

只有在下游写入动作由 Numora 外部执行时才需要调用。

成功示例:

curl -X POST "https://api.numora.example/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78/delivery" \
  -H "Authorization: Bearer $NUMORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-request-id: n8n-delivery-success-001" \
  -d '{
    "provider": "dynamics365",
    "status": "succeeded",
    "external_record_id": "crm-row-001",
    "execution_id": "n8n-4be38121-3791-4856-80d5-c57dbec2cf78",
    "attempted_at": "2026-03-24T03:00:00.000Z"
  }'

失败示例:

curl -X POST "https://api.numora.example/v1/documents/4be38121-3791-4856-80d5-c57dbec2cf78/delivery" \
  -H "Authorization: Bearer $NUMORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-request-id: n8n-delivery-failed-001" \
  -d '{
    "provider": "dynamics365",
    "status": "failed",
    "execution_id": "n8n-4be38121-3791-4856-80d5-c57dbec2cf78",
    "error_code": "crm_429",
    "error_message": "Dataverse rate limited",
    "attempted_at": "2026-03-24T03:00:00.000Z"
  }'

这个 callback 会按 execution_id 做幂等处理。

  • 相同 execution_id + 相同 payload 可以安全重放。
  • 相同 execution_id + 不同 delivery 数据会返回 409 conflict

申请短期 Dataverse Session

POST /v1/dynamics/session

只有在外部执行器模式下才需要这个接口。它适用于这种场景:你希望由 n8n 替代旧 workflow 的 Dataverse 逻辑,但仍然让终端用户在 Numora 内配置 Dynamics connection。

请求示例:

curl -X POST "https://api.numora.example/v1/dynamics/session" \
  -H "Authorization: Bearer $NUMORA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-request-id: n8n-dynamics-session-001" \
  -d '{}'

响应示例:

{
  "data": {
    "provider": "dynamics365",
    "token_type": "Bearer",
    "access_token": "<short-lived-dataverse-token>",
    "organization_url": "https://org.crm.dynamics.com",
    "organization_origin": "https://org.crm.dynamics.com",
    "api_base_url": "https://org.crm.dynamics.com/api/data/v9.2",
    "connection": {
      "id": "conn_123",
      "status": "active",
      "organization_url": "https://org.crm.dynamics.com",
      "tenant_id": "tenant-id",
      "client_id": "client-id"
    }
  }
}

说明:

  • 不会返回已保存的 client secret。
  • 返回的 bearer token 只适合可信 worker,例如你自管的 n8n
  • 如果要指定某条 Dynamics connection,可以在 JSON body 里传 connection_id
  • 如果执行器有意结束但不创建 CRM 记录,例如 supplier lookup 未命中,请向 POST /v1/documents/{id}/delivery 回写 status=skipped。Numora 会把它视为“完成但未写入 CRM”,并避免文档被自动 outbox 无限重试。

Invoice_v2 执行器映射

如果你要用 n8n 替代当前 invoice workflow,可以按下面的结构搭建:

  1. 轮询 GET /v1/delivery/outbox
  2. 展开返回的每条 item。
  3. GET /v1/documents/{id}/result
  4. data.data.fields 读取字段,从 data.data.line_items 读取 line items。
  5. POST /v1/dynamics/session
  6. 在 Dataverse 中按固定 owner 名查询 team
  7. vendor_name 查询 otcm_supplier
  8. 如果 supplier lookup 未命中,则 callback status=skipped
  9. 如果 supplier lookup 命中,则创建 otcm_inboundinvoice
  10. 遍历 line_items 并创建 otcm_inboundinvoicelineitem
  11. 以创建出的 invoice record id 回写 status=succeeded

常用字段路径:

  • fields.invoice_number.value
  • fields.invoice_date.value
  • fields.due_date.value
  • fields.vendor_name.value
  • fields.subtotal.value
  • fields.tax_amount.value
  • fields.invoice_total.value
  • line_items[*].description
  • line_items[*].quantity
  • line_items[*].unit_price
  • line_items[*].amount
  • line_items[*].tax_code

读取 Delivery Attempts

GET /v1/documents/{id}/delivery?provider=dynamics365&limit=20

这个资源主要用于 callback 模式下查看历史交付记录。

推荐的 n8n 轮询策略

对于推荐的 Numora 托管 dispatch 模式:

  1. 每 5 到 10 分钟轮询一次。
  2. GET /v1/delivery/outbox
  3. 展开返回 items。
  4. 对每条 item 调用 dispatch_url
  5. 保存 document_idmeta.run_id 以便观测。
  6. 轮询 GET /v1/documents/{id} 或查看 workflow runs,直到状态离开 push_pending

这种模式可以把目标系统连接和 workflow 配置都保留在 Numora 内部。

相关页面