API Reference

n8n Outbound Delivery

Recommended n8n integration pattern for reviewed Numora documents, delivery outbox polling, dispatch, and optional delivery callbacks.

Scope

This page describes the recommended public API pattern for connecting reviewed Numora documents to downstream systems with n8n.

The current provider shown in examples is dynamics365.

Numora supports two outbound delivery modes.

1. Numora-Managed Downstream Execution

This is the recommended model for multi-tenant SaaS integrations.

  • End users configure the downstream connection and workflow inside Numora.
  • n8n polls the delivery outbox and calls dispatch.
  • Numora reuses the saved connection and internal workflow to perform the downstream write.
  • n8n does not need to store per-user Dataverse credentials.

Recommended flow:

  1. GET /v1/delivery/outbox
  2. POST /v1/documents/{id}/dispatch
  3. Poll GET /v1/documents/{id} until the document leaves push_pending

In this mode, POST /v1/documents/{id}/delivery is not required.

2. External Executor With Delivery Callback

Use this mode only when the downstream write is performed outside Numora.

  • n8n or another worker fetches the document result.
  • The external worker writes to the destination system directly.
  • The external worker calls the delivery callback to record success or failure.
  • If you still want end users to manage Dynamics connections inside Numora, n8n can request a short-lived Dataverse token from Numora instead of storing the client secret itself.

Recommended flow:

  1. GET /v1/delivery/outbox
  2. GET /v1/documents/{id}/result
  3. POST /v1/dynamics/session
  4. Write to the destination system
  5. POST /v1/documents/{id}/delivery

In this mode, do not call POST /v1/documents/{id}/dispatch. dispatch still routes into Numora's internal workflow engine and does not replace the old workflow logic.

Poll the Delivery Outbox

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

Example request:

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"

Example response:

{
  "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
  }
}

Notes:

  • The outbox returns reviewed and finalized documents that are eligible for downstream delivery.
  • Documents already in push_pending or push_succeeded are excluded.
  • next_cursor is opaque and should be passed back unchanged when paginating.

Dispatch a Reviewed Document

POST /v1/documents/{id}/dispatch

Example request:

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"
  }'

Example response:

{
  "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"
  }
}

Notes:

  • dispatch is asynchronous. Treat 202 Accepted as “queued,” not “completed.”
  • If the document is not both reviewed and finalized, the API returns 409 conflict.
  • If no active downstream workflow is available, the API returns 409 conflict.

Poll Document Status After Dispatch

GET /v1/documents/{id}

Recommended status handling:

  • push_pending: downstream execution is still queued or running
  • push_succeeded: downstream execution completed successfully
  • push_failed: the latest downstream attempt failed and may require retry or operator action

For dispatch mode, this status resource is the primary completion signal for n8n.

Record an External Delivery Result

POST /v1/documents/{id}/delivery

Use this only when the downstream write is executed outside Numora.

Success example:

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"
  }'

Failure example:

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"
  }'

The callback is idempotent by execution_id.

  • Replaying the same execution_id with the same payload is accepted.
  • Replaying the same execution_id with different delivery data returns 409 conflict.

Request a Short-Lived Dataverse Session

POST /v1/dynamics/session

Use this only for the external executor mode, where n8n replaces the old workflow logic but still relies on a Dynamics connection that the end user configured inside Numora.

Example request:

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 '{}'

Example response:

{
  "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"
    }
  }
}

Notes:

  • The stored client secret is never returned.
  • The bearer token is intended for trusted workers such as your own n8n.
  • If you need a specific Dynamics connection, send connection_id in the JSON body.
  • If the executor intentionally finishes without creating CRM rows, for example because a supplier lookup misses, send status=skipped to POST /v1/documents/{id}/delivery. Numora will treat that as a completed no-write outcome and keep the document out of automatic outbox retries.

Invoice_v2 Executor Mapping

If you are replacing the current invoice workflow with n8n, use this shape:

  1. Poll GET /v1/delivery/outbox.
  2. Split each returned item.
  3. Fetch GET /v1/documents/{id}/result.
  4. Read fields from data.data.fields and line items from data.data.line_items.
  5. Request POST /v1/dynamics/session.
  6. Query Dataverse team by the fixed owner name.
  7. Query Dataverse otcm_supplier by vendor_name.
  8. If the supplier lookup misses, callback status=skipped.
  9. If the supplier lookup succeeds, create otcm_inboundinvoice.
  10. Loop through line_items and create otcm_inboundinvoicelineitem.
  11. Callback status=succeeded with the created invoice record id.

Useful field paths:

  • 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

Retrieve Delivery Attempts

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

Use this resource to inspect recorded delivery attempts from callback-based integrations.

Suggested n8n Polling Strategy

For the recommended Numora-managed dispatch mode:

  1. Schedule polling every 5 to 10 minutes.
  2. Call GET /v1/delivery/outbox.
  3. Split returned items.
  4. Call each item's dispatch_url.
  5. Store document_id and meta.run_id for observability.
  6. Poll GET /v1/documents/{id} or inspect workflow runs until the status leaves push_pending.

This model keeps destination credentials and workflow configuration inside Numora.