Skip to main content
A WorkflowRun tracks one submitted business operation. A WorkflowRunItem tracks one admitted unit of work and retains its result or failure. A single invoice import creates one run with one item. An accounting sync discovers and adds many invoice items to one run. Discovery may temporarily have no items, finish with no relevant work, or fail before admitting any. Create work through domain endpoints such as POST /v1/invoices/imports. There is no generic task submission endpoint. Import admission/retry returns import_id, workflow_run_id, and workflow_run_item_id; the import’s summary/detail also identify its current run and item. Known primary-file duplicates detected before admission create neither a run nor an item. The preview exposes invoice_import, invoice_import_batch, invoice_agreement_matching, agreement_invoice_matching, invoice_compliance_check, agreement_compliance_check, alert_topic_reconciliation, alert_topic_proposal, agreement_context_suggestions, document_extraction, agreement_document_classification, agreement_detail_suggestions and integration_sync workflows. An invoice check has one item per agreement and an agreement check one item per invoice; see compliance checks. After a check, an alert_topic_reconciliation run with one item organises the agreement’s alerts into topics; at most one is active per agreement. Each extraction, classification or detail-suggestion request has one item. Classification and detail-suggestion items identify the agreement and contain the proposed role or details; extraction items identify the Document. See agreement suggestions. Matching items always identify invoices; an invoice refresh has one item, while an agreement refresh discovers invoice items in bounded pages. See Explicit matching for commands and relationship results. Workflow responses contain business progress and results; source evidence is available through the import resource.

Endpoints and permissions

Send a bearer credential and X-Organization-Id. Workflow reads require Read; cancellation requires Write. OpenAPI records each operation’s level in x-watchdog-permission. Lists include only the shipped public workflow inventory in the target organization. Unknown, foreign, or internal run IDs return 404; an item must belong to the requested run and organization. Insufficient access returns 403.

Background progress

Use GET /v1/workflow-runs/summary for background observation. It returns one entry per public workflow type, including idle types. run_counts covers all retained runs by status; progress covers items belonging to currently active runs only. discovering_run_count reports open discovery, in which case progress.total is null. A zero total is a valid empty operation. Each entry has an opaque change_token derived from run counts and active progress. Terminal counts ensure the token changes when work finishes, even if the active run count is unchanged. This is a snapshot token, not a durable event cursor or an exactly-once notification mechanism. Use token changes to invalidate related application data; establish a baseline on first load. Poll this summary once every five seconds while active, stop polling when idle, and pause in hidden tabs, and honor Retry-After. Load individual runs and item results through the paginated endpoints only when needed. Do not drain the full run inventory on every polling interval.

Run representation

Lists and individual run reads use the same fixed shape:
resource identifies the business operation owning the run. Progress counts admitted items, not invoices ultimately created or provider records filtered out during discovery. total is null while discovery can add items. Once the collection closes, total equals the sum of the six counts. A total of zero is a legitimate empty operation. Counts and parent status describe the same point in time; progress changes update the parent’s updated_at. All timestamps are UTC. One item failure does not terminate its siblings. Completion requires both a closed collection and no queued/running items. A fatal discovery failure remains a parent failure even if admitted items succeed. A failed run has failure: { code, message }; item-specific details remain on the items. Reading a failed run or item returns HTTP 200. finished_at is set for terminal states.

Items and results

Items use queued | running | completed | skipped | failed | cancelled. Pending items have null result and failure. Failed items have a stable failure code/message and null result; cancelled items have neither. Successful/skipped items retain a typed business result, available immediately regardless of sibling or parent status. Historical items whose result was never retained have null result. For invoice imports, imported and duplicate results include an invoice UUID; not_invoice has invoice_id: null. All three are completed processing outcomes. Financial inconsistency does not fail the item: read the resulting invoice’s financially_valid and financial_errors. Results describe the exact item execution, remain unchanged after invoice edits or explicit retry, and contain resource references rather than copies of business documents. Publication, item result, applicable usage, and possible parent completion commit atomically. There is no run-level /result or separate /results collection: use /items, optionally filtered to terminal statuses.

Filtering and pagination

Run lists support status, type, created_from (inclusive UTC), created_before (exclusive UTC), resource_type, resource_id (requires resource_type), and ids (at most 100 UUIDs). status, type, and ids accept comma-separated values. Different filters use AND; values within one filter use OR. Omit status to include terminal runs. Run order is created_at DESC, id DESC. When both creation bounds are supplied, created_before must be later than created_from. Item lists support comma-separated status. Order is created_at ASC, id ASC. Both lists return { "data": [...], "next_cursor": null }, default to 50 items and allow at most 100. Cursors bind to the organization, filters, ordering, and (for items) parent run. Continue with the same filters. Pagination reads current state rather than a frozen snapshot. A null cursor means there are no more matching rows at that moment, not that discovery or processing has finished. Newly settled items can enter a status-filtered collection before an existing cursor: restart listing when refreshing it. This is not an incremental event feed.

Polling in an application

  1. Share one summary query across application modules. Refresh it after successful admission and when returning to the app; use its change tokens to invalidate affected data.
  2. Open run lists on demand, fetching one page at a time. Do not expand the background summary into a poll for every active run or every admission ID.
  3. Poll the selected active run without a status filter so its terminal state remains visible. Fetch item/result pages only when the user opens them.
  4. Stop polling unavailable IDs. Resolve 401/403; respect Retry-After across summary, focus, and detail requests on 429. Back off for transport/5xx failures and pause while hidden.
Polling reads stored product state without contacting the execution or extraction provider. Responses use Cache-Control: private, no-store. The generated client owns no timers; your application controls polling.

Cancellation, retry, and recovery

Cancellation takes no body or idempotency key. It fences publication, closes discovery, and cancels queued/running items before requesting provider cancellation. Completed/skipped/failed items retain their results and failures. Repeated cancellation succeeds, including after a newer retry exists. Other terminal runs return 409 conflict. A provider cancellation failure cannot reopen product work. Retry remains a domain command: POST /v1/invoices/imports/{id}/retry validates sources and capacity again and optionally accepts Idempotency-Key to safely replay that retry request. Explicit retries create new run and item IDs with retry_of_run_id and retry_of_item_id; replays return the same new identities. Earlier runs/items never reopen. Future bulk retry commands select unsuccessful items rather than redoing successes. Automatic execution retries keep the current item identity and cannot overwrite terminal results. If an execution stops outside the API, background recovery reconciles confirmed failure or cancellation into the run and item. An execution that completes without publishing its business result fails with execution_incomplete. Temporary provider unavailability does not change the product state or cause an automatic resubmission. Retrieve the import’s failure and evidence before choosing an explicit retry.