# Create AccessToken Source: https://developer.kodexa.ai/api-reference/accesstokens/create-accesstoken /api-reference/api-docs.yaml post /api/access-tokens Creates a new AccessToken. Access tokens provide programmatic API authentication. # Delete AccessToken Source: https://developer.kodexa.ai/api-reference/accesstokens/delete-accesstoken /api-reference/api-docs.yaml delete /api/access-tokens/{id} Permanently deletes the specified AccessToken. Access tokens provide programmatic API authentication. # Get AccessToken by ID Source: https://developer.kodexa.ai/api-reference/accesstokens/get-accesstoken-by-id /api-reference/api-docs.yaml get /api/access-tokens/{id} Retrieves a single AccessToken by its unique identifier. Access tokens provide programmatic API authentication. # Get AccessToken change sequence Source: https://developer.kodexa.ai/api-reference/accesstokens/get-accesstoken-change-sequence /api-reference/api-docs.yaml get /api/access-tokens/{id}/sequence Returns the current change sequence number for the specified AccessToken. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List AccessTokens Source: https://developer.kodexa.ai/api-reference/accesstokens/list-accesstokens /api-reference/api-docs.yaml get /api/access-tokens Returns a paginated list of AccessTokens. Access tokens provide programmatic API authentication. # Update AccessToken Source: https://developer.kodexa.ai/api-reference/accesstokens/update-accesstoken /api-reference/api-docs.yaml put /api/access-tokens/{id} Updates an existing AccessToken. Access tokens provide programmatic API authentication. # Accept platform terms Source: https://developer.kodexa.ai/api-reference/account/accept-platform-terms /api-reference/api-docs.yaml put /api/account/acceptTerms # Auth0 profile sync callback Source: https://developer.kodexa.ai/api-reference/account/auth0-profile-sync-callback /api-reference/api-docs.yaml post /api/account/callback Receives an Auth0 callback to synchronise the authenticated user's profile data on the platform. # Check if user is admin Source: https://developer.kodexa.ai/api-reference/account/check-if-user-is-admin /api-reference/api-docs.yaml get /api/account/admin Returns whether the currently authenticated user has platform admin privileges. # Create CLI access token Source: https://developer.kodexa.ai/api-reference/account/create-cli-access-token /api-reference/api-docs.yaml post /api/account/cli/access-token Creates a new access token for CLI authentication. The returned token can be used for subsequent API calls. # Check if user is admin Source: https://developer.kodexa.ai/api-reference/account/get-account-admin get /api/account/admin Returns whether the currently authenticated user has platform admin privileges. # Get current user Source: https://developer.kodexa.ai/api-reference/account/get-account-me get /api/account/me Returns the profile and settings of the currently authenticated user. # Get account memberships (legacy alias) Source: https://developer.kodexa.ai/api-reference/account/get-account-memberships-legacy-alias /api-reference/api-docs.yaml get /api/account/memberships Legacy alias of GET /account/teams retained for cached UI bundles. # Get account organization access Source: https://developer.kodexa.ai/api-reference/account/get-account-organization-access /api-reference/api-docs.yaml get /api/account/teams Returns all team-based organization access for the currently authenticated user. # Get account organization access Source: https://developer.kodexa.ai/api-reference/account/get-account-teams get /api/account/teams Returns all team-based organization access for the currently authenticated user. # Get current user Source: https://developer.kodexa.ai/api-reference/account/get-current-user /api-reference/api-docs.yaml get /api/account/me Returns the profile and settings of the currently authenticated user. # List FGAC permissions for the current user Source: https://developer.kodexa.ai/api-reference/account/list-fgac-permissions-for-the-current-user /api-reference/api-docs.yaml get /api/account/permissions # Create CLI access token Source: https://developer.kodexa.ai/api-reference/account/post-account-cli-access-token post /api/account/cli/access-token Creates a new access token for CLI authentication. The returned token can be used for subsequent API calls. # Verify CLI device code Source: https://developer.kodexa.ai/api-reference/account/post-account-cli-device-verify post /api/account/cli/device/verify Verifies a CLI device code to complete the device authorization flow. Used by the Kodexa CLI to authenticate users via browser-based login. # Update current user Source: https://developer.kodexa.ai/api-reference/account/put-account-me put /api/account/me Updates the profile and settings of the currently authenticated user. # Update current user Source: https://developer.kodexa.ai/api-reference/account/update-current-user /api-reference/api-docs.yaml put /api/account/me Updates the profile and settings of the currently authenticated user. # Verify CLI device code Source: https://developer.kodexa.ai/api-reference/account/verify-cli-device-code /api-reference/api-docs.yaml post /api/account/cli/device/verify Verifies a CLI device code to complete the device authorization flow. Used by the Kodexa CLI to authenticate users via browser-based login. # Approve a paused activity step Source: https://developer.kodexa.ai/api-reference/activities/approve-a-paused-activity-step /api-reference/api-docs.yaml post /api/activities/{id}/steps/{stepId}/approve Approves a step that paused for human review and lets the activity continue. # Cancel a running activity Source: https://developer.kodexa.ai/api-reference/activities/cancel-a-running-activity /api-reference/api-docs.yaml post /api/activities/{id}/cancel # Delete Activity Source: https://developer.kodexa.ai/api-reference/activities/delete-activities-id delete /api/activities/{id} Permanently deletes the specified Activity. Activities are runtime instances of an ActivityPlan, materialized into a graph of Steps. # Delete Activity Source: https://developer.kodexa.ai/api-reference/activities/delete-activity /api-reference/api-docs.yaml delete /api/activities/{id} Permanently deletes the specified Activity. Activities are runtime instances of an ActivityPlan, materialized into a graph of Steps. # List Activities Source: https://developer.kodexa.ai/api-reference/activities/get-activities get /api/activities Returns a paginated list of Activities. Activities are runtime instances of an ActivityPlan, materialized into a graph of Steps. # Get Activity by ID Source: https://developer.kodexa.ai/api-reference/activities/get-activities-id get /api/activities/{id} Retrieves a single Activity by its unique identifier. The response embeds the activity's steps (ordered by sequence ascending) so a single round-trip suffices — collapses the legacy `GET /api/plans/{id}` + `GET /api/plans/{id}/items` pair. # Get Activity change sequence Source: https://developer.kodexa.ai/api-reference/activities/get-activities-id-sequence get /api/activities/{id}/sequence Returns the current change sequence number for the specified Activity. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get Activity by ID Source: https://developer.kodexa.ai/api-reference/activities/get-activity-by-id /api-reference/api-docs.yaml get /api/activities/{id} Retrieves a single Activity by its unique identifier. The response embeds the activity's steps (ordered by sequence ascending) so a single round-trip suffices — collapses the legacy `GET /api/plans/{id}` + `GET /api/plans/{id}/items` pair. # Get Activity change sequence Source: https://developer.kodexa.ai/api-reference/activities/get-activity-change-sequence /api-reference/api-docs.yaml get /api/activities/{id}/sequence Returns the current change sequence number for the specified Activity. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get activity logs Source: https://developer.kodexa.ai/api-reference/activities/get-activity-logs /api-reference/api-docs.yaml get /api/activities/{id}/logs Returns aggregated log output from every step of the activity. # List Activities Source: https://developer.kodexa.ai/api-reference/activities/list-activities /api-reference/api-docs.yaml get /api/activities Returns a paginated list of Activities. Activities are runtime instances of an ActivityPlan, materialized into a graph of Steps. # List document family executions for an activity step Source: https://developer.kodexa.ai/api-reference/activities/list-document-family-executions-for-an-activity-step /api-reference/api-docs.yaml get /api/activities/{id}/steps/{stepId}/document-families Returns one row per document family processed by the specified step, including the input/output content object IDs and any error details. Used by the per-step debug panel. # Replan an activity Source: https://developer.kodexa.ai/api-reference/activities/replan-an-activity /api-reference/api-docs.yaml post /api/activities/{id}/replan Creates a new instance of the activity using the same plan, inputs, and trigger metadata; marks the source activity as REPLANNED. # Retry a failed activity Source: https://developer.kodexa.ai/api-reference/activities/retry-a-failed-activity /api-reference/api-docs.yaml post /api/activities/{id}/retry # Start an activity Source: https://developer.kodexa.ai/api-reference/activities/start-an-activity /api-reference/api-docs.yaml post /api/activities Creates and starts a new Activity from an ActivityPlan. Returns the materialised Activity with its initial Step graph. # Update an activity step Source: https://developer.kodexa.ai/api-reference/activities/update-an-activity-step /api-reference/api-docs.yaml patch /api/activities/{id}/steps/{stepId} Patches a single step on an activity. Whitelist: only `scriptBody` is writable, and only on SCRIPT-kind steps — non-SCRIPT kinds return 400. Unknown fields are silently ignored for legacy parity. # Validate activity plan steps Source: https://developer.kodexa.ai/api-reference/activity-plans/validate-activity-plan-steps /api-reference/api-docs.yaml post /api/activity-plans/validate Runs the compile-time plan-shape checks (the same ones enforced when an activity is started) against a steps array, statelessly, and returns every finding instead of rejecting on the first. Used by the plan editor for design-time feedback. Always 200 for a well-formed request — shape problems are reported in the payload, not as HTTP errors. Note: CREATE_TASK action edges are not checked here (their valid action set lives on the referenced task template's metadata); the editor validates those client-side. # Create ActivityDocumentFamily Source: https://developer.kodexa.ai/api-reference/activitydocumentfamilies/create-activitydocumentfamily /api-reference/api-docs.yaml post /api/activity-document-families Creates a new ActivityDocumentFamily. Links between activities and document families. # Get ActivityDocumentFamily change sequence Source: https://developer.kodexa.ai/api-reference/activitydocumentfamilies/get-activitydocumentfamily-change-sequence /api-reference/api-docs.yaml get /api/activity-document-families/{id}/sequence Returns the current change sequence number for the specified ActivityDocumentFamily. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List ActivityDocumentFamilies Source: https://developer.kodexa.ai/api-reference/activitydocumentfamilies/list-activitydocumentfamilies /api-reference/api-docs.yaml get /api/activity-document-families Returns a paginated list of ActivityDocumentFamilies. Links between activities and document families. # Create ActivityPlan Source: https://developer.kodexa.ai/api-reference/activityplans/create-activityplan /api-reference/api-docs.yaml post /api/activity-plans Creates a new ActivityPlan. ActivityPlans are reusable graph-of-steps blueprints that runtime Activities are instantiated from. # Delete ActivityPlan Source: https://developer.kodexa.ai/api-reference/activityplans/delete-activityplan /api-reference/api-docs.yaml delete /api/activity-plans/{id} Permanently deletes the specified ActivityPlan. ActivityPlans are reusable graph-of-steps blueprints that runtime Activities are instantiated from. # Get ActivityPlan by ID Source: https://developer.kodexa.ai/api-reference/activityplans/get-activityplan-by-id /api-reference/api-docs.yaml get /api/activity-plans/{id} Retrieves a single ActivityPlan by its unique identifier. ActivityPlans are reusable graph-of-steps blueprints that runtime Activities are instantiated from. # Get ActivityPlan change sequence Source: https://developer.kodexa.ai/api-reference/activityplans/get-activityplan-change-sequence /api-reference/api-docs.yaml get /api/activity-plans/{id}/sequence Returns the current change sequence number for the specified ActivityPlan. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List ActivityPlan audit history Source: https://developer.kodexa.ai/api-reference/activityplans/list-activityplan-audit-history /api-reference/api-docs.yaml get /api/activity-plans/{id}/audit Returns the paginated audit history for a single ActivityPlan. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # List ActivityPlans Source: https://developer.kodexa.ai/api-reference/activityplans/list-activityplans /api-reference/api-docs.yaml get /api/activity-plans Returns a paginated list of ActivityPlans. ActivityPlans are reusable graph-of-steps blueprints that runtime Activities are instantiated from. # Update ActivityPlan Source: https://developer.kodexa.ai/api-reference/activityplans/update-activityplan /api-reference/api-docs.yaml put /api/activity-plans/{id} Updates an existing ActivityPlan. ActivityPlans are reusable graph-of-steps blueprints that runtime Activities are instantiated from. # Create OrchestratorAgentRuntime Source: https://developer.kodexa.ai/api-reference/agent-runtimes/create-orchestratoragentruntime /api-reference/api-docs.yaml post /api/agent-runtimes Creates a new OrchestratorAgentRuntime. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Delete OrchestratorAgentRuntime Source: https://developer.kodexa.ai/api-reference/agent-runtimes/delete-agent-runtimes-id delete /api/agent-runtimes/{id} Permanently deletes the specified OrchestratorAgentRuntime. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Delete OrchestratorAgentRuntime Source: https://developer.kodexa.ai/api-reference/agent-runtimes/delete-orchestratoragentruntime /api-reference/api-docs.yaml delete /api/agent-runtimes/{id} Permanently deletes the specified OrchestratorAgentRuntime. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # List Agent Runtimes Source: https://developer.kodexa.ai/api-reference/agent-runtimes/get-agent-runtimes get /api/agent-runtimes Returns a paginated list of Agent Runtimes. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Get OrchestratorAgentRuntime by ID Source: https://developer.kodexa.ai/api-reference/agent-runtimes/get-agent-runtimes-id get /api/agent-runtimes/{id} Retrieves a single OrchestratorAgentRuntime by its unique identifier. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Get OrchestratorAgentRuntime change sequence Source: https://developer.kodexa.ai/api-reference/agent-runtimes/get-agent-runtimes-id-sequence get /api/agent-runtimes/{id}/sequence Returns the current change sequence number for the specified OrchestratorAgentRuntime. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get OrchestratorAgentRuntime by ID Source: https://developer.kodexa.ai/api-reference/agent-runtimes/get-orchestratoragentruntime-by-id /api-reference/api-docs.yaml get /api/agent-runtimes/{id} Retrieves a single OrchestratorAgentRuntime by its unique identifier. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Get OrchestratorAgentRuntime change sequence Source: https://developer.kodexa.ai/api-reference/agent-runtimes/get-orchestratoragentruntime-change-sequence /api-reference/api-docs.yaml get /api/agent-runtimes/{id}/sequence Returns the current change sequence number for the specified OrchestratorAgentRuntime. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Agent Runtimes Source: https://developer.kodexa.ai/api-reference/agent-runtimes/list-agent-runtimes /api-reference/api-docs.yaml get /api/agent-runtimes Returns a paginated list of Agent Runtimes. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Create OrchestratorAgentRuntime Source: https://developer.kodexa.ai/api-reference/agent-runtimes/post-agent-runtimes post /api/agent-runtimes Creates a new OrchestratorAgentRuntime. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Update OrchestratorAgentRuntime Source: https://developer.kodexa.ai/api-reference/agent-runtimes/put-agent-runtimes-id put /api/agent-runtimes/{id} Updates an existing OrchestratorAgentRuntime. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Update OrchestratorAgentRuntime Source: https://developer.kodexa.ai/api-reference/agent-runtimes/update-orchestratoragentruntime /api-reference/api-docs.yaml put /api/agent-runtimes/{id} Updates an existing OrchestratorAgentRuntime. Agent runtimes define agent processing environments backed by AWS Bedrock AgentCore. # Create OrchestratorAgentSession Source: https://developer.kodexa.ai/api-reference/agent-sessions/create-orchestratoragentsession /api-reference/api-docs.yaml post /api/agent-sessions Creates a new OrchestratorAgentSession. Agent sessions track stateful conversation sessions within agent runtimes. # Delete OrchestratorAgentSession Source: https://developer.kodexa.ai/api-reference/agent-sessions/delete-agent-sessions-id delete /api/agent-sessions/{id} Permanently deletes the specified OrchestratorAgentSession. Agent sessions track stateful conversation sessions within agent runtimes. # Delete OrchestratorAgentSession Source: https://developer.kodexa.ai/api-reference/agent-sessions/delete-orchestratoragentsession /api-reference/api-docs.yaml delete /api/agent-sessions/{id} Permanently deletes the specified OrchestratorAgentSession. Agent sessions track stateful conversation sessions within agent runtimes. # List Agent Sessions Source: https://developer.kodexa.ai/api-reference/agent-sessions/get-agent-sessions get /api/agent-sessions Returns a paginated list of Agent Sessions. Agent sessions track stateful conversation sessions within agent runtimes. # Get OrchestratorAgentSession by ID Source: https://developer.kodexa.ai/api-reference/agent-sessions/get-agent-sessions-id get /api/agent-sessions/{id} Retrieves a single OrchestratorAgentSession by its unique identifier. Agent sessions track stateful conversation sessions within agent runtimes. # Get OrchestratorAgentSession change sequence Source: https://developer.kodexa.ai/api-reference/agent-sessions/get-agent-sessions-id-sequence get /api/agent-sessions/{id}/sequence Returns the current change sequence number for the specified OrchestratorAgentSession. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get OrchestratorAgentSession by ID Source: https://developer.kodexa.ai/api-reference/agent-sessions/get-orchestratoragentsession-by-id /api-reference/api-docs.yaml get /api/agent-sessions/{id} Retrieves a single OrchestratorAgentSession by its unique identifier. Agent sessions track stateful conversation sessions within agent runtimes. # Get OrchestratorAgentSession change sequence Source: https://developer.kodexa.ai/api-reference/agent-sessions/get-orchestratoragentsession-change-sequence /api-reference/api-docs.yaml get /api/agent-sessions/{id}/sequence Returns the current change sequence number for the specified OrchestratorAgentSession. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Agent Sessions Source: https://developer.kodexa.ai/api-reference/agent-sessions/list-agent-sessions /api-reference/api-docs.yaml get /api/agent-sessions Returns a paginated list of Agent Sessions. Agent sessions track stateful conversation sessions within agent runtimes. # Create OrchestratorAgentSession Source: https://developer.kodexa.ai/api-reference/agent-sessions/post-agent-sessions post /api/agent-sessions Creates a new OrchestratorAgentSession. Agent sessions track stateful conversation sessions within agent runtimes. # Update OrchestratorAgentSession Source: https://developer.kodexa.ai/api-reference/agent-sessions/put-agent-sessions-id put /api/agent-sessions/{id} Updates an existing OrchestratorAgentSession. Agent sessions track stateful conversation sessions within agent runtimes. # Update OrchestratorAgentSession Source: https://developer.kodexa.ai/api-reference/agent-sessions/update-orchestratoragentsession /api-reference/api-docs.yaml put /api/agent-sessions/{id} Updates an existing OrchestratorAgentSession. Agent sessions track stateful conversation sessions within agent runtimes. # Create OrchestratorAgent Source: https://developer.kodexa.ai/api-reference/agents/create-orchestratoragent /api-reference/api-docs.yaml post /api/agents Creates a new OrchestratorAgent. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Delete OrchestratorAgent Source: https://developer.kodexa.ai/api-reference/agents/delete-agents-id delete /api/agents/{id} Permanently deletes the specified OrchestratorAgent. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Delete OrchestratorAgent Source: https://developer.kodexa.ai/api-reference/agents/delete-orchestratoragent /api-reference/api-docs.yaml delete /api/agents/{id} Permanently deletes the specified OrchestratorAgent. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # List Agents Source: https://developer.kodexa.ai/api-reference/agents/get-agents get /api/agents Returns a paginated list of Agents. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Get OrchestratorAgent by ID Source: https://developer.kodexa.ai/api-reference/agents/get-agents-id get /api/agents/{id} Retrieves a single OrchestratorAgent by its unique identifier. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Get OrchestratorAgent change sequence Source: https://developer.kodexa.ai/api-reference/agents/get-agents-id-sequence get /api/agents/{id}/sequence Returns the current change sequence number for the specified OrchestratorAgent. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get OrchestratorAgent by ID Source: https://developer.kodexa.ai/api-reference/agents/get-orchestratoragent-by-id /api-reference/api-docs.yaml get /api/agents/{id} Retrieves a single OrchestratorAgent by its unique identifier. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Get OrchestratorAgent change sequence Source: https://developer.kodexa.ai/api-reference/agents/get-orchestratoragent-change-sequence /api-reference/api-docs.yaml get /api/agents/{id}/sequence Returns the current change sequence number for the specified OrchestratorAgent. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Agents Source: https://developer.kodexa.ai/api-reference/agents/list-agents /api-reference/api-docs.yaml get /api/agents Returns a paginated list of Agents. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Create OrchestratorAgent Source: https://developer.kodexa.ai/api-reference/agents/post-agents post /api/agents Creates a new OrchestratorAgent. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Update OrchestratorAgent Source: https://developer.kodexa.ai/api-reference/agents/put-agents-id put /api/agents/{id} Updates an existing OrchestratorAgent. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Update OrchestratorAgent Source: https://developer.kodexa.ai/api-reference/agents/update-orchestratoragent /api-reference/api-docs.yaml put /api/agents/{id} Updates an existing OrchestratorAgent. Agents are instantiated agent runtimes within an org+project, tracking lifecycle and configuration. # Create chat completion Source: https://developer.kodexa.ai/api-reference/ai-gateway/create-chat-completion /api-reference/api-docs.yaml post /api/organizations/{orgId}/ai/chat/completions Creates a chat completion using the specified model via the AI Gateway. Supports both synchronous and streaming (SSE) responses. Set `stream: true` in the request body to receive server-sent events. The request is proxied to the organization's configured AI provider (Bedrock, Azure, Gemini, etc.). # List available AI models Source: https://developer.kodexa.ai/api-reference/ai-gateway/list-available-ai-models /api-reference/api-docs.yaml get /api/organizations/{orgId}/ai/models Returns the list of models available through the AI Gateway for the specified organization. # Data Lake Source: https://developer.kodexa.ai/api-reference/analytics/data-lake The Kodexa data lake mirrors each document's post-processing state into S3-backed content-object envelopes for analytics — including, from 2026.6, a per-revision audit trail under after.audit. Kodexa streams a document's post-processing state into an S3-backed **data lake** for analytics. Each document's content is written as a **content-object envelope** under the `content-objects/` prefix. An envelope's `After` body holds the projected state: * **`after.dataObjects`** — the extracted data objects projected from the document's KDDB (keyed by taxonomy reference), i.e. the post-Apply view of the data a reviewer sees. * **`after.audit`** — *(2026.6+)* the document's full per-revision audit trail (see below). The analytics [datasets](/api-reference/analytics/get-analytics-datasets) and the [query API](/api-reference/analytics/post-analytics-query) read from these envelopes. ## Document audit trail (`after.audit`) *(2026.6, lake schema `1.0` → `1.1`)* Each content-object envelope now carries a co-located **`after.audit`** section inside its `After` body that projects the document's full per-revision KDDB audit trail. There is no separate audit envelope or prefix — `after.audit` lives inside the existing envelope under `content-objects/`, and is omitted when a document has no trail (schema-stable, the same way `after.dataObjects` is). ### What it contains * **`after.audit.revisions[]`** — every audit revision, each with `auditRevisionId`, `revisionTimestamp` (UTC), and actor attribution: `actorEmail`, `actorUserId`, `batchId`, `taskId`, `taskTemplateRef`, and `userWorkSessionStartedAt`. * **`after.audit.dataAttributeAudits[]`** — the per-attribute add / edit / delete changes. * **`after.audit.dataExceptionAudits[]`** — the data-exception lifecycle (raise / modify / resolve). * **`dataObjectAudits`**, **`tagAudits`**, and **`metadataAudits`** are also projected for completeness. ### Before and after values Every attribute-change row in `dataAttributeAudits[]` carries a `transactionType` of `add`, `edit`, or `delete`, and presents its values uniformly: the current value in `value` / `stringValue` / `decimalValue` / etc., and the prior value in the matching `previousValue` / `previousStringValue` / `previousOwnerUri` fields. Deletes are normalized so the removed value always appears in the `previous*` fields (never as a current value) — so you can read before/after the same way for any change type. Revision attribution (actor, task, session) is denormalized onto each row, so no join back to `revisions[]` is needed. ### Leaf grain Rows are unique at `(document_family_id, auditRevisionId, id)`. The audit trail is a property of the document **family**: the platform mints new content-object ids over a document's life but copies the trail forward with stable revision ids, so every content-object envelope for a family carries the cumulative trail as of its version. Dedup on `(document_family_id, auditRevisionId, id)` to assemble the complete trail across a family's content objects without double-counting. ### Schema bump and backfill This is a purely **additive** `1.0` → `1.1` schema change — existing envelopes and queries are unaffected, and consumers that don't read `after.audit` see no change. Envelopes written before 2026.6 do not have `after.audit` and are not rewritten in place; they can be retro-fitted with the **`lake-backfill-audit`** utility, which re-projects each document's trail from KDDB and splices it into the existing envelope. Backfill is idempotent — a re-run skips envelopes that already carry `after.audit` (unless forced) — and leaves the rest of the envelope semantically unchanged (row state, `dataObjects`, and knowledge items are all preserved; only `after.audit` is spliced in). ## Task lock feed (`task-locks/`) *(2026.8)* The lake now also mirrors **task-lock events**, not only document content-objects. On every genuine lock transition — when a task's `locked` flag flips true, whether from the submit/batch path or the bulk lock action — a **`task_lock` envelope** (`_meta.kind` = `task_lock`) is written under the `task-locks/` prefix. This is a **dedicated feed**, kept separate from the work-session **`batches/`** feed: a bulk lock is not a work session, so lock events are held out of the effort- and timing-telemetry that `batches/` carries. ### What it contains The envelope's `After` body holds the task's lock state at the moment it was locked: * **`taskId`** — the task that was locked. * **`lockedAt`** — the lock watermark (UTC). * **`lockedById`** / **`lockedByEmail`** — who locked it. * **`taskStatus`** — the task's status at lock time. * **`projectId`** — the owning project. * **`documentFamilyIds`** — the document families the task covers; this is the **join key back to the document (content-object) feed**. * **`properties`** — the task's properties, projected verbatim. `_meta.actor` also carries who locked the task; for system / API-key callers (where no user can be resolved) its identity fields (`userId` / `userEmail`) are left empty. The write is **best-effort**: a failed mirror is logged and reconciled rather than propagated, so a missing envelope never blocks the lock itself. # Execute analytics query Source: https://developer.kodexa.ai/api-reference/analytics/execute-analytics-query /api-reference/api-docs.yaml post /api/analytics/query Runs a server-side dataset query with grouping, metrics, filters, and sorting. The API validates all fields against a fixed dataset catalog and applies FGAC scope checks. # List analytics datasets Source: https://developer.kodexa.ai/api-reference/analytics/get-analytics-datasets get /api/analytics/datasets Returns the analytics dataset catalog used to drive dashboard query builders (dimensions, metrics, and supported time fields). # List analytics datasets Source: https://developer.kodexa.ai/api-reference/analytics/list-analytics-datasets /api-reference/api-docs.yaml get /api/analytics/datasets Returns the analytics dataset catalog used to drive dashboard query builders (dimensions, metrics, and supported time fields). # Mint an analytics embed token Source: https://developer.kodexa.ai/api-reference/analytics/mint-an-analytics-embed-token /api-reference/api-docs.yaml post /api/analytics/embed-token Exchanges the caller's kodexa credential for a short-lived, per-tenant-scoped RS256 JWT for the standalone analytics-server query API. The org scope is derived server-side from the caller's team/org assignments. # Mint an analytics embed token Source: https://developer.kodexa.ai/api-reference/analytics/post-analytics-embed-token post /api/analytics/embed-token Exchanges the caller's kodexa credential for a short-lived, per-tenant-scoped RS256 JWT for the standalone analytics-server query API. The org scope is derived server-side from the caller's team/org assignments. Exchange the caller's own Kodexa credential (typically a Profile → API token) for a short-lived, tenant-scoped RS256 JWT that the standalone analytics-server accepts on its query API. Use this to embed dashboards or run analytics queries on a user's behalf without ever handing out a long-lived analytics credential. A successful response returns: ```json theme={null} { "token": "eyJhbGciOiJSUzI1Ni...", "expiresAt": "2026-07-24T14:15:00Z", "models": ["invoices", "documents"], "orgs": ["acme-corp"] } ``` * **`token`** — the signed embed JWT. Present it to analytics-server as a bearer credential. * **`expiresAt`** — RFC 3339 expiry. Token lifetime is `tokenTtlSeconds`, defaulting to and **hard-capped at 15 minutes** (900s) — the analytics-server ceiling. Mint a fresh token when it expires; do not cache it. * **`models`** — the closed model allowlist baked into the token. * **`orgs`** — the organization slug(s) the token is scoped to. ## Organization scope is server-derived The caller can **never** request an organization scope. Scope is derived server-side from the authenticated user's team/org assignments, and every granted dataset is row-filtered to exactly those orgs. The minter attaches a per-object row filter — `{ field: "org_slug", op: "in", value: [] }` — to every object in the token's allowlist, so an object granted without a filter cannot leak rows. A caller who belongs to no organization is rejected. The `models` and `objects` allowlists are **closed**: analytics-server denies anything not explicitly listed, so an object added to the warehouse but omitted from the config fails closed (denied), never exposed. ## Enabling the minter The endpoint is opt-in through the `analyticsEmbed` block in the kodexa-api config. Everything in this block is non-secret: ```yaml theme={null} analyticsEmbed: enabled: true issuer: kodexa-api # JWT `iss`; must match an analytics-server auth.embed.issuers entry audience: analytics-server # JWT `aud` keyId: embed-2026-07 # JWT `kid` header, for verifier key rotation tokenTtlSeconds: 900 # token lifetime; defaults to and capped at 900 (15m) models: # closed model allowlist baked into every token - invoices - documents objects: # closed object allowlist; each gets a row filter - document_facts - invoice_facts rowFilterField: org_slug # tenant column on every object; defaults to org_slug ``` The RS256 **signing private key is not part of this config** — it is supplied at runtime via the `ANALYTICS_EMBED_SIGNING_KEY_PEM` environment variable. The `keyId` above is emitted as the token's `kid` header so verifiers can rotate keys. The endpoint returns **503 Service Unavailable** when the minter is disabled (`enabled: false`) or when it is enabled but `ANALYTICS_EMBED_SIGNING_KEY_PEM` is empty — in that case the signer is never wired up and the minter stays disabled. Set both to bring the endpoint online. # Execute analytics query Source: https://developer.kodexa.ai/api-reference/analytics/post-analytics-query post /api/analytics/query Runs a server-side dataset query with grouping, metrics, filters, and sorting. The API validates all fields against a fixed dataset catalog and applies FGAC scope checks. # Create AppliedKnowledgeSet Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/create-appliedknowledgeset /api-reference/api-docs.yaml post /api/applied-knowledge-sets Creates a new AppliedKnowledgeSet. Applied knowledge set configurations determine which knowledge is available during processing. # Delete AppliedKnowledgeSet Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/delete-applied-knowledge-sets-id delete /api/applied-knowledge-sets/{id} Permanently deletes the specified AppliedKnowledgeSet. Applied knowledge set configurations determine which knowledge is available during processing. # Delete AppliedKnowledgeSet Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/delete-appliedknowledgeset /api-reference/api-docs.yaml delete /api/applied-knowledge-sets/{id} Permanently deletes the specified AppliedKnowledgeSet. Applied knowledge set configurations determine which knowledge is available during processing. # Get applied knowledge set snapshot Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-set-snapshot /api-reference/api-docs.yaml get /api/applied-knowledge-sets/{applicationId}/snapshot Returns the knowledge set snapshot captured at the time this knowledge set was applied. # Get applied knowledge set statistics Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-set-statistics /api-reference/api-docs.yaml get /api/applied-knowledge-sets/knowledge-sets/{knowledgeSetId}/stats Returns application statistics for the specified knowledge set, including counts by status. # List Applied Knowledge Sets Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-sets get /api/applied-knowledge-sets Returns a paginated list of Applied Knowledge Sets. Applied knowledge set configurations determine which knowledge is available during processing. # Get applied knowledge set snapshot Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-sets-applicationId-snapshot get /api/applied-knowledge-sets/{applicationId}/snapshot Returns the knowledge set snapshot captured at the time this knowledge set was applied. # Get AppliedKnowledgeSet by ID Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-sets-id get /api/applied-knowledge-sets/{id} Retrieves a single AppliedKnowledgeSet by its unique identifier. Applied knowledge set configurations determine which knowledge is available during processing. # Get AppliedKnowledgeSet change sequence Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-sets-id-sequence get /api/applied-knowledge-sets/{id}/sequence Returns the current change sequence number for the specified AppliedKnowledgeSet. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get outdated applied knowledge sets Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-sets-knowledge-sets-knowledgeSetId-outdated get /api/applied-knowledge-sets/knowledge-sets/{knowledgeSetId}/outdated Returns applied knowledge sets that reference outdated snapshots (snapshot doesn't match the current knowledge set snapshot). # Get applied knowledge set statistics Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-applied-knowledge-sets-knowledge-sets-knowledgeSetId-stats get /api/applied-knowledge-sets/knowledge-sets/{knowledgeSetId}/stats Returns application statistics for the specified knowledge set, including counts by status. # Get AppliedKnowledgeSet by ID Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-appliedknowledgeset-by-id /api-reference/api-docs.yaml get /api/applied-knowledge-sets/{id} Retrieves a single AppliedKnowledgeSet by its unique identifier. Applied knowledge set configurations determine which knowledge is available during processing. # Get AppliedKnowledgeSet change sequence Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-appliedknowledgeset-change-sequence /api-reference/api-docs.yaml get /api/applied-knowledge-sets/{id}/sequence Returns the current change sequence number for the specified AppliedKnowledgeSet. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get outdated applied knowledge sets Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/get-outdated-applied-knowledge-sets /api-reference/api-docs.yaml get /api/applied-knowledge-sets/knowledge-sets/{knowledgeSetId}/outdated Returns applied knowledge sets that reference outdated snapshots (snapshot doesn't match the current knowledge set snapshot). # List Applied Knowledge Sets Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/list-applied-knowledge-sets /api-reference/api-docs.yaml get /api/applied-knowledge-sets Returns a paginated list of Applied Knowledge Sets. Applied knowledge set configurations determine which knowledge is available during processing. # Create AppliedKnowledgeSet Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/post-applied-knowledge-sets post /api/applied-knowledge-sets Creates a new AppliedKnowledgeSet. Applied knowledge set configurations determine which knowledge is available during processing. # Update AppliedKnowledgeSet Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/put-applied-knowledge-sets-id put /api/applied-knowledge-sets/{id} Updates an existing AppliedKnowledgeSet. Applied knowledge set configurations determine which knowledge is available during processing. # Update AppliedKnowledgeSet Source: https://developer.kodexa.ai/api-reference/applied-knowledge-sets/update-appliedknowledgeset /api-reference/api-docs.yaml put /api/applied-knowledge-sets/{id} Updates an existing AppliedKnowledgeSet. Applied knowledge set configurations determine which knowledge is available during processing. # Create AppliedKnowledge Source: https://developer.kodexa.ai/api-reference/applied-knowledge/create-appliedknowledge /api-reference/api-docs.yaml post /api/applied-knowledges Creates a new AppliedKnowledge. Applied knowledge represents knowledge used during document processing. # Delete AppliedKnowledge Source: https://developer.kodexa.ai/api-reference/applied-knowledge/delete-appliedknowledge /api-reference/api-docs.yaml delete /api/applied-knowledges/{id} Permanently deletes the specified AppliedKnowledge. Applied knowledge represents knowledge used during document processing. # Get AppliedKnowledge by ID Source: https://developer.kodexa.ai/api-reference/applied-knowledge/get-appliedknowledge-by-id /api-reference/api-docs.yaml get /api/applied-knowledges/{id} Retrieves a single AppliedKnowledge by its unique identifier. Applied knowledge represents knowledge used during document processing. # Get AppliedKnowledge change sequence Source: https://developer.kodexa.ai/api-reference/applied-knowledge/get-appliedknowledge-change-sequence /api-reference/api-docs.yaml get /api/applied-knowledges/{id}/sequence Returns the current change sequence number for the specified AppliedKnowledge. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Applied Knowledge Source: https://developer.kodexa.ai/api-reference/applied-knowledge/list-applied-knowledge /api-reference/api-docs.yaml get /api/applied-knowledges Returns a paginated list of Applied Knowledge. Applied knowledge represents knowledge used during document processing. # Update AppliedKnowledge Source: https://developer.kodexa.ai/api-reference/applied-knowledge/update-appliedknowledge /api-reference/api-docs.yaml put /api/applied-knowledges/{id} Updates an existing AppliedKnowledge. Applied knowledge represents knowledge used during document processing. # Create Assistant Source: https://developer.kodexa.ai/api-reference/assistants/create-assistant /api-reference/api-docs.yaml post /api/assistants Creates a new Assistant. Assistants are AI-powered processors that analyze and extract data from documents. # Delete Assistant Source: https://developer.kodexa.ai/api-reference/assistants/delete-assistant /api-reference/api-docs.yaml delete /api/assistants/{id} Permanently deletes the specified Assistant. Assistants are AI-powered processors that analyze and extract data from documents. # Delete Assistant Source: https://developer.kodexa.ai/api-reference/assistants/delete-assistants-id delete /api/assistants/{id} Permanently deletes the specified Assistant. Assistants are AI-powered processors that analyze and extract data from documents. # Get Assistant by ID Source: https://developer.kodexa.ai/api-reference/assistants/get-assistant-by-id /api-reference/api-docs.yaml get /api/assistants/{id} Retrieves a single Assistant by its unique identifier. Assistants are AI-powered processors that analyze and extract data from documents. # Get Assistant change sequence Source: https://developer.kodexa.ai/api-reference/assistants/get-assistant-change-sequence /api-reference/api-docs.yaml get /api/assistants/{id}/sequence Returns the current change sequence number for the specified Assistant. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Assistants Source: https://developer.kodexa.ai/api-reference/assistants/get-assistants get /api/assistants Returns a paginated list of Assistants. Assistants are AI-powered processors that analyze and extract data from documents. # Get Assistant by ID Source: https://developer.kodexa.ai/api-reference/assistants/get-assistants-id get /api/assistants/{id} Retrieves a single Assistant by its unique identifier. Assistants are AI-powered processors that analyze and extract data from documents. # Get Assistant change sequence Source: https://developer.kodexa.ai/api-reference/assistants/get-assistants-id-sequence get /api/assistants/{id}/sequence Returns the current change sequence number for the specified Assistant. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Assistants Source: https://developer.kodexa.ai/api-reference/assistants/list-assistants /api-reference/api-docs.yaml get /api/assistants Returns a paginated list of Assistants. Assistants are AI-powered processors that analyze and extract data from documents. # Create Assistant Source: https://developer.kodexa.ai/api-reference/assistants/post-assistants post /api/assistants Creates a new Assistant. Assistants are AI-powered processors that analyze and extract data from documents. # Update Assistant Source: https://developer.kodexa.ai/api-reference/assistants/put-assistants-id put /api/assistants/{id} Updates an existing Assistant. Assistants are AI-powered processors that analyze and extract data from documents. # Update Assistant Source: https://developer.kodexa.ai/api-reference/assistants/update-assistant /api-reference/api-docs.yaml put /api/assistants/{id} Updates an existing Assistant. Assistants are AI-powered processors that analyze and extract data from documents. # Process batch document update Source: https://developer.kodexa.ai/api-reference/batch-updates/post-batch-update post /api/batch-update Processes a batch update for multiple document families in a single request. Supports uploading binary files, applying labels, updating notes, and deleting documents. Use multipart/form-data for file uploads or application/json for metadata-only updates. ## Task status in the response Send the task's status as `statusSlug` on the `task` object. The task echoed back in the response carries it under the same name, matching the Task model everywhere else in the API, so a client can apply the returned task directly to its local copy. ```json theme={null} { "task": { "id": "550e8400-e29b-41d4-a716-446655440000", "statusSlug": "reviewed", "changeSequence": 42 } } ``` The legacy `statusId` key is no longer emitted in the batch response. Integrations that read the task status from this endpoint must read `statusSlug`. ## Completing a task with a discarded save When the batch moves the task to a status of type `DONE`, the platform first checks the task's documents. If the most recent saved changes for one of them were discarded server-side and never redone, the request is refused with `409 Conflict` and no part of the batch is applied — the status change, the queued document uploads, the label changes and the deletes are all rolled back together. The message names the affected documents by path (or by ID when a document has no path): ```json theme={null} { "code": "CONFLICT", "message": "Cannot complete this task: the most recent saved changes for invoices/2026-08/inv-1042.pdf were not applied to the stored document (the save failed server-side). Reopen the document, redo the changes, and save again before completing." } ``` To clear the block, reopen each document the message names, redo the changes, and save again. A successful re-save — or any full-document save — releases it and the batch completes normally. Uploads sent in the same batch as the completion are queued before the check runs, so saving and completing in one request is not blocked by that request's own save. Only the latest save per document counts, so a failure that has already been redone and saved does not block completion. Batches that leave the task's status unchanged, or move it to a status of any other type, are unaffected. This endpoint also returns `409 Conflict` for a stale `changeSequence` on the task; the two cases share the `CONFLICT` code and are distinguished by the message. # Process batch document update Source: https://developer.kodexa.ai/api-reference/batch-updates/process-batch-document-update /api-reference/api-docs.yaml post /api/batch-update Processes a batch update for multiple document families in a single request. Supports uploading binary files, applying labels, updating notes, and deleting documents. Use multipart/form-data for file uploads or application/json for metadata-only updates. # Create ChannelType Source: https://developer.kodexa.ai/api-reference/channel-types/create-channeltype /api-reference/api-docs.yaml post /api/channel-types Creates a new ChannelType. Channel types are per-database registry rows that tell the agent runtime what to load for chats of a given type. # Delete ChannelType Source: https://developer.kodexa.ai/api-reference/channel-types/delete-channeltype /api-reference/api-docs.yaml delete /api/channel-types/{id} Permanently deletes the specified ChannelType. Channel types are per-database registry rows that tell the agent runtime what to load for chats of a given type. # Get ChannelType by ID Source: https://developer.kodexa.ai/api-reference/channel-types/get-channeltype-by-id /api-reference/api-docs.yaml get /api/channel-types/{id} Retrieves a single ChannelType by its unique identifier. Channel types are per-database registry rows that tell the agent runtime what to load for chats of a given type. # Get ChannelType change sequence Source: https://developer.kodexa.ai/api-reference/channel-types/get-channeltype-change-sequence /api-reference/api-docs.yaml get /api/channel-types/{id}/sequence Returns the current change sequence number for the specified ChannelType. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Channel Types Source: https://developer.kodexa.ai/api-reference/channel-types/list-channel-types /api-reference/api-docs.yaml get /api/channel-types Returns a paginated list of Channel Types. Channel types are per-database registry rows that tell the agent runtime what to load for chats of a given type. # Update ChannelType Source: https://developer.kodexa.ai/api-reference/channel-types/update-channeltype /api-reference/api-docs.yaml put /api/channel-types/{id} Updates an existing ChannelType. Channel types are per-database registry rows that tell the agent runtime what to load for chats of a given type. # Create Channel Source: https://developer.kodexa.ai/api-reference/channel/create-channel /api-reference/api-docs.yaml post /api/channels Creates a new Channel. Channels provide real-time messaging within the platform. # Delete all messages in a channel Source: https://developer.kodexa.ai/api-reference/channel/delete-all-messages-in-a-channel /api-reference/api-docs.yaml delete /api/channels/{channelId}/messages Permanently removes all messages from the specified channel. This action cannot be undone. # Delete Channel Source: https://developer.kodexa.ai/api-reference/channel/delete-channel /api-reference/api-docs.yaml delete /api/channels/{id} Permanently deletes the specified Channel. Channels provide real-time messaging within the platform. # Get Channel by ID Source: https://developer.kodexa.ai/api-reference/channel/get-channel-by-id /api-reference/api-docs.yaml get /api/channels/{id} Retrieves a single Channel by its unique identifier. Channels provide real-time messaging within the platform. # Get Channel change sequence Source: https://developer.kodexa.ai/api-reference/channel/get-channel-change-sequence /api-reference/api-docs.yaml get /api/channels/{id}/sequence Returns the current change sequence number for the specified Channel. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Channel Source: https://developer.kodexa.ai/api-reference/channel/list-channel /api-reference/api-docs.yaml get /api/channels Returns a paginated list of Channel. Channels provide real-time messaging within the platform. # Stream messages from a channel via SSE Source: https://developer.kodexa.ai/api-reference/channel/stream-messages-from-a-channel-via-sse /api-reference/api-docs.yaml get /api/channels/{channelId}/stream # Update Channel Source: https://developer.kodexa.ai/api-reference/channel/update-channel /api-reference/api-docs.yaml put /api/channels/{id} Updates an existing Channel. Channels provide real-time messaging within the platform. # Delete all messages in a channel Source: https://developer.kodexa.ai/api-reference/channels/delete-channels-channelId-messages delete /api/channels/{channelId}/messages Permanently removes all messages from the specified channel. This action cannot be undone. # Create ContentException Source: https://developer.kodexa.ai/api-reference/content-exception/create-contentexception /api-reference/api-docs.yaml post /api/content-exceptions Creates a new ContentException. Content exceptions record errors during document content processing. # Delete ContentException Source: https://developer.kodexa.ai/api-reference/content-exception/delete-contentexception /api-reference/api-docs.yaml delete /api/content-exceptions/{id} Permanently deletes the specified ContentException. Content exceptions record errors during document content processing. # Get ContentException by ID Source: https://developer.kodexa.ai/api-reference/content-exception/get-contentexception-by-id /api-reference/api-docs.yaml get /api/content-exceptions/{id} Retrieves a single ContentException by its unique identifier. Content exceptions record errors during document content processing. # Get ContentException change sequence Source: https://developer.kodexa.ai/api-reference/content-exception/get-contentexception-change-sequence /api-reference/api-docs.yaml get /api/content-exceptions/{id}/sequence Returns the current change sequence number for the specified ContentException. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Content Exception Source: https://developer.kodexa.ai/api-reference/content-exception/list-content-exception /api-reference/api-docs.yaml get /api/content-exceptions Returns a paginated list of Content Exception. Content exceptions record errors during document content processing. # Update ContentException Source: https://developer.kodexa.ai/api-reference/content-exception/update-contentexception /api-reference/api-docs.yaml put /api/content-exceptions/{id} Updates an existing ContentException. Content exceptions record errors during document content processing. # Create ContentObject Source: https://developer.kodexa.ai/api-reference/content-object/create-contentobject /api-reference/api-docs.yaml post /api/content-objects Creates a new ContentObject. Content objects represent individual files or processing results within a document family. # Delete ContentObject Source: https://developer.kodexa.ai/api-reference/content-object/delete-contentobject /api-reference/api-docs.yaml delete /api/content-objects/{id} Permanently deletes the specified ContentObject. Content objects represent individual files or processing results within a document family. # Get content object binary content Source: https://developer.kodexa.ai/api-reference/content-object/get-content-object-binary-content /api-reference/api-docs.yaml get /api/content-objects/{id}/content Downloads the binary content (file) of the content object. Returns the raw file bytes with the appropriate content type header. # Get content object metadata preview Source: https://developer.kodexa.ai/api-reference/content-object/get-content-object-metadata-preview /api-reference/api-docs.yaml get /api/content-objects/{id}/preview Returns a preview of the content object's metadata, including extracted data and processing results. # Get content object page image Source: https://developer.kodexa.ai/api-reference/content-object/get-content-object-page-image /api-reference/api-docs.yaml get /api/content-objects/{id}/image Returns a rendered page image (PNG) of the content object. Use the page parameter to specify which page to render. # Get ContentObject by ID Source: https://developer.kodexa.ai/api-reference/content-object/get-contentobject-by-id /api-reference/api-docs.yaml get /api/content-objects/{id} Retrieves a single ContentObject by its unique identifier. Content objects represent individual files or processing results within a document family. # Get ContentObject change sequence Source: https://developer.kodexa.ai/api-reference/content-object/get-contentobject-change-sequence /api-reference/api-docs.yaml get /api/content-objects/{id}/sequence Returns the current change sequence number for the specified ContentObject. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Content Object Source: https://developer.kodexa.ai/api-reference/content-object/list-content-object /api-reference/api-docs.yaml get /api/content-objects Returns a paginated list of Content Object. Content objects represent individual files or processing results within a document family. # Replace content object binary Source: https://developer.kodexa.ai/api-reference/content-object/replace-content-object-binary /api-reference/api-docs.yaml put /api/content-objects/{id}/content # Update ContentObject Source: https://developer.kodexa.ai/api-reference/content-object/update-contentobject /api-reference/api-docs.yaml put /api/content-objects/{id} Updates an existing ContentObject. Content objects represent individual files or processing results within a document family. # Create CreditTransaction Source: https://developer.kodexa.ai/api-reference/credit-transactions/create-credittransaction /api-reference/api-docs.yaml post /api/credit-transactions Creates a new CreditTransaction. Credit transactions record billing credit usage or additions. # Delete CreditTransaction Source: https://developer.kodexa.ai/api-reference/credit-transactions/delete-credit-transactions-id delete /api/credit-transactions/{id} Permanently deletes the specified CreditTransaction. Credit transactions record billing credit usage or additions. # Delete CreditTransaction Source: https://developer.kodexa.ai/api-reference/credit-transactions/delete-credittransaction /api-reference/api-docs.yaml delete /api/credit-transactions/{id} Permanently deletes the specified CreditTransaction. Credit transactions record billing credit usage or additions. # List Credit Transactions Source: https://developer.kodexa.ai/api-reference/credit-transactions/get-credit-transactions get /api/credit-transactions Returns a paginated list of Credit Transactions. Credit transactions record billing credit usage or additions. # Get CreditTransaction by ID Source: https://developer.kodexa.ai/api-reference/credit-transactions/get-credit-transactions-id get /api/credit-transactions/{id} Retrieves a single CreditTransaction by its unique identifier. Credit transactions record billing credit usage or additions. # Get CreditTransaction change sequence Source: https://developer.kodexa.ai/api-reference/credit-transactions/get-credit-transactions-id-sequence get /api/credit-transactions/{id}/sequence Returns the current change sequence number for the specified CreditTransaction. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get CreditTransaction by ID Source: https://developer.kodexa.ai/api-reference/credit-transactions/get-credittransaction-by-id /api-reference/api-docs.yaml get /api/credit-transactions/{id} Retrieves a single CreditTransaction by its unique identifier. Credit transactions record billing credit usage or additions. # Get CreditTransaction change sequence Source: https://developer.kodexa.ai/api-reference/credit-transactions/get-credittransaction-change-sequence /api-reference/api-docs.yaml get /api/credit-transactions/{id}/sequence Returns the current change sequence number for the specified CreditTransaction. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Credit Transactions Source: https://developer.kodexa.ai/api-reference/credit-transactions/list-credit-transactions /api-reference/api-docs.yaml get /api/credit-transactions Returns a paginated list of Credit Transactions. Credit transactions record billing credit usage or additions. # Create CreditTransaction Source: https://developer.kodexa.ai/api-reference/credit-transactions/post-credit-transactions post /api/credit-transactions Creates a new CreditTransaction. Credit transactions record billing credit usage or additions. # Update CreditTransaction Source: https://developer.kodexa.ai/api-reference/credit-transactions/put-credit-transactions-id put /api/credit-transactions/{id} Updates an existing CreditTransaction. Credit transactions record billing credit usage or additions. # Update CreditTransaction Source: https://developer.kodexa.ai/api-reference/credit-transactions/update-credittransaction /api-reference/api-docs.yaml put /api/credit-transactions/{id} Updates an existing CreditTransaction. Credit transactions record billing credit usage or additions. # Create Taxonomies Source: https://developer.kodexa.ai/api-reference/data-definitions/create-taxonomies /api-reference/api-docs.yaml post /api/data-definitions Creates a new Taxonomies. Data definitions (taxonomies) define document structure and extraction rules. # Delete Taxonomies Source: https://developer.kodexa.ai/api-reference/data-definitions/delete-data-definitions-id delete /api/data-definitions/{id} Permanently deletes the specified Taxonomies. Data definitions (taxonomies) define document structure and extraction rules. # Delete Taxonomies Source: https://developer.kodexa.ai/api-reference/data-definitions/delete-taxonomies /api-reference/api-docs.yaml delete /api/data-definitions/{id} Permanently deletes the specified Taxonomies. Data definitions (taxonomies) define document structure and extraction rules. # List Data Definitions Source: https://developer.kodexa.ai/api-reference/data-definitions/get-data-definitions get /api/data-definitions Returns a paginated list of Data Definitions. Data definitions (taxonomies) define document structure and extraction rules. # Get Taxonomies by ID Source: https://developer.kodexa.ai/api-reference/data-definitions/get-data-definitions-id get /api/data-definitions/{id} Retrieves a single Taxonomies by its unique identifier. Data definitions (taxonomies) define document structure and extraction rules. # Get Taxonomies change sequence Source: https://developer.kodexa.ai/api-reference/data-definitions/get-data-definitions-id-sequence get /api/data-definitions/{id}/sequence Returns the current change sequence number for the specified Taxonomies. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get Taxonomies by ID Source: https://developer.kodexa.ai/api-reference/data-definitions/get-taxonomies-by-id /api-reference/api-docs.yaml get /api/data-definitions/{id} Retrieves a single Taxonomies by its unique identifier. Data definitions (taxonomies) define document structure and extraction rules. # Get Taxonomies change sequence Source: https://developer.kodexa.ai/api-reference/data-definitions/get-taxonomies-change-sequence /api-reference/api-docs.yaml get /api/data-definitions/{id}/sequence Returns the current change sequence number for the specified Taxonomies. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Data Definitions Source: https://developer.kodexa.ai/api-reference/data-definitions/list-data-definitions /api-reference/api-docs.yaml get /api/data-definitions Returns a paginated list of Data Definitions. Data definitions (taxonomies) define document structure and extraction rules. # List Taxonomies audit history Source: https://developer.kodexa.ai/api-reference/data-definitions/list-taxonomies-audit-history /api-reference/api-docs.yaml get /api/data-definitions/{id}/audit Returns the paginated audit history for a single Taxonomies. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create Taxonomies Source: https://developer.kodexa.ai/api-reference/data-definitions/post-data-definitions post /api/data-definitions Creates a new Taxonomies. Data definitions (taxonomies) define document structure and extraction rules. # Update Taxonomies Source: https://developer.kodexa.ai/api-reference/data-definitions/put-data-definitions-id put /api/data-definitions/{id} Updates an existing Taxonomies. Data definitions (taxonomies) define document structure and extraction rules. # Update Taxonomies Source: https://developer.kodexa.ai/api-reference/data-definitions/update-taxonomies /api-reference/api-docs.yaml put /api/data-definitions/{id} Updates an existing Taxonomies. Data definitions (taxonomies) define document structure and extraction rules. # Create DataForms Source: https://developer.kodexa.ai/api-reference/data-forms/create-dataforms /api-reference/api-docs.yaml post /api/data-forms Creates a new DataForms. Data forms define structured data capture forms for documents. # Delete DataForms Source: https://developer.kodexa.ai/api-reference/data-forms/delete-data-forms-id delete /api/data-forms/{id} Permanently deletes the specified DataForms. Data forms define structured data capture forms for documents. # Delete DataForms Source: https://developer.kodexa.ai/api-reference/data-forms/delete-dataforms /api-reference/api-docs.yaml delete /api/data-forms/{id} Permanently deletes the specified DataForms. Data forms define structured data capture forms for documents. # List Data Forms Source: https://developer.kodexa.ai/api-reference/data-forms/get-data-forms get /api/data-forms Returns a paginated list of Data Forms. Data forms define structured data capture forms for documents. # Get DataForms by ID Source: https://developer.kodexa.ai/api-reference/data-forms/get-data-forms-id get /api/data-forms/{id} Retrieves a single DataForms by its unique identifier. Data forms define structured data capture forms for documents. # Get DataForms change sequence Source: https://developer.kodexa.ai/api-reference/data-forms/get-data-forms-id-sequence get /api/data-forms/{id}/sequence Returns the current change sequence number for the specified DataForms. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get DataForms by ID Source: https://developer.kodexa.ai/api-reference/data-forms/get-dataforms-by-id /api-reference/api-docs.yaml get /api/data-forms/{id} Retrieves a single DataForms by its unique identifier. Data forms define structured data capture forms for documents. # Get DataForms change sequence Source: https://developer.kodexa.ai/api-reference/data-forms/get-dataforms-change-sequence /api-reference/api-docs.yaml get /api/data-forms/{id}/sequence Returns the current change sequence number for the specified DataForms. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Data Forms Source: https://developer.kodexa.ai/api-reference/data-forms/list-data-forms /api-reference/api-docs.yaml get /api/data-forms Returns a paginated list of Data Forms. Data forms define structured data capture forms for documents. # List DataForms audit history Source: https://developer.kodexa.ai/api-reference/data-forms/list-dataforms-audit-history /api-reference/api-docs.yaml get /api/data-forms/{id}/audit Returns the paginated audit history for a single DataForms. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create DataForms Source: https://developer.kodexa.ai/api-reference/data-forms/post-data-forms post /api/data-forms Creates a new DataForms. Data forms define structured data capture forms for documents. # Update DataForms Source: https://developer.kodexa.ai/api-reference/data-forms/put-data-forms-id put /api/data-forms/{id} Updates an existing DataForms. Data forms define structured data capture forms for documents. # Update DataForms Source: https://developer.kodexa.ai/api-reference/data-forms/update-dataforms /api-reference/api-docs.yaml put /api/data-forms/{id} Updates an existing DataForms. Data forms define structured data capture forms for documents. # Create DataStoreMetadata Source: https://developer.kodexa.ai/api-reference/data-stores/create-datastoremetadata /api-reference/api-docs.yaml post /api/data-stores Creates a new DataStoreMetadata. Data stores provide structured data storage. # Delete DataStoreMetadata Source: https://developer.kodexa.ai/api-reference/data-stores/delete-data-stores-id delete /api/data-stores/{id} Permanently deletes the specified DataStoreMetadata. Data stores provide structured data storage. # Delete DataStoreMetadata Source: https://developer.kodexa.ai/api-reference/data-stores/delete-datastoremetadata /api-reference/api-docs.yaml delete /api/data-stores/{id} Permanently deletes the specified DataStoreMetadata. Data stores provide structured data storage. # List Data Stores Source: https://developer.kodexa.ai/api-reference/data-stores/get-data-stores get /api/data-stores Returns a paginated list of Data Stores. Data stores provide structured data storage. # Get DataStoreMetadata by ID Source: https://developer.kodexa.ai/api-reference/data-stores/get-data-stores-id get /api/data-stores/{id} Retrieves a single DataStoreMetadata by its unique identifier. Data stores provide structured data storage. # Get DataStoreMetadata change sequence Source: https://developer.kodexa.ai/api-reference/data-stores/get-data-stores-id-sequence get /api/data-stores/{id}/sequence Returns the current change sequence number for the specified DataStoreMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get DataStoreMetadata by ID Source: https://developer.kodexa.ai/api-reference/data-stores/get-datastoremetadata-by-id /api-reference/api-docs.yaml get /api/data-stores/{id} Retrieves a single DataStoreMetadata by its unique identifier. Data stores provide structured data storage. # Get DataStoreMetadata change sequence Source: https://developer.kodexa.ai/api-reference/data-stores/get-datastoremetadata-change-sequence /api-reference/api-docs.yaml get /api/data-stores/{id}/sequence Returns the current change sequence number for the specified DataStoreMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Data Stores Source: https://developer.kodexa.ai/api-reference/data-stores/list-data-stores /api-reference/api-docs.yaml get /api/data-stores Returns a paginated list of Data Stores. Data stores provide structured data storage. # List DataStoreMetadata audit history Source: https://developer.kodexa.ai/api-reference/data-stores/list-datastoremetadata-audit-history /api-reference/api-docs.yaml get /api/data-stores/{id}/audit Returns the paginated audit history for a single DataStoreMetadata. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create DataStoreMetadata Source: https://developer.kodexa.ai/api-reference/data-stores/post-data-stores post /api/data-stores Creates a new DataStoreMetadata. Data stores provide structured data storage. # Update DataStoreMetadata Source: https://developer.kodexa.ai/api-reference/data-stores/put-data-stores-id put /api/data-stores/{id} Updates an existing DataStoreMetadata. Data stores provide structured data storage. # Update DataStoreMetadata Source: https://developer.kodexa.ai/api-reference/data-stores/update-datastoremetadata /api-reference/api-docs.yaml put /api/data-stores/{id} Updates an existing DataStoreMetadata. Data stores provide structured data storage. # Create DocumentAssignment Source: https://developer.kodexa.ai/api-reference/document-assignments/create-documentassignment /api-reference/api-docs.yaml post /api/document-assignments Creates a new DocumentAssignment. Document assignments link document families to users for review. # Delete DocumentAssignment Source: https://developer.kodexa.ai/api-reference/document-assignments/delete-document-assignments-id delete /api/document-assignments/{id} Permanently deletes the specified DocumentAssignment. Document assignments link document families to users for review. # Delete DocumentAssignment Source: https://developer.kodexa.ai/api-reference/document-assignments/delete-documentassignment /api-reference/api-docs.yaml delete /api/document-assignments/{id} Permanently deletes the specified DocumentAssignment. Document assignments link document families to users for review. # List Document Assignments Source: https://developer.kodexa.ai/api-reference/document-assignments/get-document-assignments get /api/document-assignments Returns a paginated list of Document Assignments. Document assignments link document families to users for review. # Get DocumentAssignment by ID Source: https://developer.kodexa.ai/api-reference/document-assignments/get-document-assignments-id get /api/document-assignments/{id} Retrieves a single DocumentAssignment by its unique identifier. Document assignments link document families to users for review. # Get DocumentAssignment change sequence Source: https://developer.kodexa.ai/api-reference/document-assignments/get-document-assignments-id-sequence get /api/document-assignments/{id}/sequence Returns the current change sequence number for the specified DocumentAssignment. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get DocumentAssignment by ID Source: https://developer.kodexa.ai/api-reference/document-assignments/get-documentassignment-by-id /api-reference/api-docs.yaml get /api/document-assignments/{id} Retrieves a single DocumentAssignment by its unique identifier. Document assignments link document families to users for review. # Get DocumentAssignment change sequence Source: https://developer.kodexa.ai/api-reference/document-assignments/get-documentassignment-change-sequence /api-reference/api-docs.yaml get /api/document-assignments/{id}/sequence Returns the current change sequence number for the specified DocumentAssignment. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Document Assignments Source: https://developer.kodexa.ai/api-reference/document-assignments/list-document-assignments /api-reference/api-docs.yaml get /api/document-assignments Returns a paginated list of Document Assignments. Document assignments link document families to users for review. # Create DocumentAssignment Source: https://developer.kodexa.ai/api-reference/document-assignments/post-document-assignments post /api/document-assignments Creates a new DocumentAssignment. Document assignments link document families to users for review. # Update DocumentAssignment Source: https://developer.kodexa.ai/api-reference/document-assignments/put-document-assignments-id put /api/document-assignments/{id} Updates an existing DocumentAssignment. Document assignments link document families to users for review. # Update DocumentAssignment Source: https://developer.kodexa.ai/api-reference/document-assignments/update-documentassignment /api-reference/api-docs.yaml put /api/document-assignments/{id} Updates an existing DocumentAssignment. Document assignments link document families to users for review. # Remove a label from a document family Source: https://developer.kodexa.ai/api-reference/document-families/delete-document-families-id-labels-labelId delete /api/document-families/{id}/labels/{labelId} Removes a label from the document family. # Create DocumentFamilyStatistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/create-documentfamilystatistics /api-reference/api-docs.yaml post /api/document-family-statistics Creates a new DocumentFamilyStatistics. Document family statistics contain processing metrics for document families. # Delete DocumentFamilyStatistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/delete-document-family-statistics-id delete /api/document-family-statistics/{id} Permanently deletes the specified DocumentFamilyStatistics. Document family statistics contain processing metrics for document families. # Delete DocumentFamilyStatistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/delete-documentfamilystatistics /api-reference/api-docs.yaml delete /api/document-family-statistics/{id} Permanently deletes the specified DocumentFamilyStatistics. Document family statistics contain processing metrics for document families. # List Document Family Statistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/get-document-family-statistics get /api/document-family-statistics Returns a paginated list of Document Family Statistics. Document family statistics contain processing metrics for document families. # Get DocumentFamilyStatistics by ID Source: https://developer.kodexa.ai/api-reference/document-family-statistics/get-document-family-statistics-id get /api/document-family-statistics/{id} Retrieves a single DocumentFamilyStatistics by its unique identifier. Document family statistics contain processing metrics for document families. # Get DocumentFamilyStatistics change sequence Source: https://developer.kodexa.ai/api-reference/document-family-statistics/get-document-family-statistics-id-sequence get /api/document-family-statistics/{id}/sequence Returns the current change sequence number for the specified DocumentFamilyStatistics. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get DocumentFamilyStatistics by ID Source: https://developer.kodexa.ai/api-reference/document-family-statistics/get-documentfamilystatistics-by-id /api-reference/api-docs.yaml get /api/document-family-statistics/{id} Retrieves a single DocumentFamilyStatistics by its unique identifier. Document family statistics contain processing metrics for document families. # Get DocumentFamilyStatistics change sequence Source: https://developer.kodexa.ai/api-reference/document-family-statistics/get-documentfamilystatistics-change-sequence /api-reference/api-docs.yaml get /api/document-family-statistics/{id}/sequence Returns the current change sequence number for the specified DocumentFamilyStatistics. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Document Family Statistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/list-document-family-statistics /api-reference/api-docs.yaml get /api/document-family-statistics Returns a paginated list of Document Family Statistics. Document family statistics contain processing metrics for document families. # Create DocumentFamilyStatistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/post-document-family-statistics post /api/document-family-statistics Creates a new DocumentFamilyStatistics. Document family statistics contain processing metrics for document families. # Update DocumentFamilyStatistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/put-document-family-statistics-id put /api/document-family-statistics/{id} Updates an existing DocumentFamilyStatistics. Document family statistics contain processing metrics for document families. # Update DocumentFamilyStatistics Source: https://developer.kodexa.ai/api-reference/document-family-statistics/update-documentfamilystatistics /api-reference/api-docs.yaml put /api/document-family-statistics/{id} Updates an existing DocumentFamilyStatistics. Document family statistics contain processing metrics for document families. # Create DocumentStatus Source: https://developer.kodexa.ai/api-reference/document-statuses/create-documentstatus /api-reference/api-docs.yaml post /api/document-statuses Creates a new DocumentStatus. Document statuses define named states for tracking document processing. # Delete DocumentStatus Source: https://developer.kodexa.ai/api-reference/document-statuses/delete-document-statuses-id delete /api/document-statuses/{id} Permanently deletes the specified DocumentStatus. Document statuses define named states for tracking document processing. # Delete DocumentStatus Source: https://developer.kodexa.ai/api-reference/document-statuses/delete-documentstatus /api-reference/api-docs.yaml delete /api/document-statuses/{id} Permanently deletes the specified DocumentStatus. Document statuses define named states for tracking document processing. # List Document Statuses Source: https://developer.kodexa.ai/api-reference/document-statuses/get-document-statuses get /api/document-statuses Returns a paginated list of Document Statuses. Document statuses define named states for tracking document processing. # Get DocumentStatus by ID Source: https://developer.kodexa.ai/api-reference/document-statuses/get-document-statuses-id get /api/document-statuses/{id} Retrieves a single DocumentStatus by its unique identifier. Document statuses define named states for tracking document processing. # Get DocumentStatus change sequence Source: https://developer.kodexa.ai/api-reference/document-statuses/get-document-statuses-id-sequence get /api/document-statuses/{id}/sequence Returns the current change sequence number for the specified DocumentStatus. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get DocumentStatus by ID Source: https://developer.kodexa.ai/api-reference/document-statuses/get-documentstatus-by-id /api-reference/api-docs.yaml get /api/document-statuses/{id} Retrieves a single DocumentStatus by its unique identifier. Document statuses define named states for tracking document processing. # Get DocumentStatus change sequence Source: https://developer.kodexa.ai/api-reference/document-statuses/get-documentstatus-change-sequence /api-reference/api-docs.yaml get /api/document-statuses/{id}/sequence Returns the current change sequence number for the specified DocumentStatus. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Document Statuses Source: https://developer.kodexa.ai/api-reference/document-statuses/list-document-statuses /api-reference/api-docs.yaml get /api/document-statuses Returns a paginated list of Document Statuses. Document statuses define named states for tracking document processing. # Create DocumentStatus Source: https://developer.kodexa.ai/api-reference/document-statuses/post-document-statuses post /api/document-statuses Creates a new DocumentStatus. Document statuses define named states for tracking document processing. # Update DocumentStatus Source: https://developer.kodexa.ai/api-reference/document-statuses/put-document-statuses-id put /api/document-statuses/{id} Updates an existing DocumentStatus. Document statuses define named states for tracking document processing. # Update DocumentStatus Source: https://developer.kodexa.ai/api-reference/document-statuses/update-documentstatus /api-reference/api-docs.yaml put /api/document-statuses/{id} Updates an existing DocumentStatus. Document statuses define named states for tracking document processing. # Create DocumentStores Source: https://developer.kodexa.ai/api-reference/document-stores/create-documentstores /api-reference/api-docs.yaml post /api/document-stores Creates a new DocumentStores. Document stores are containers for organizing and processing documents within a project. # Delete DocumentStores Source: https://developer.kodexa.ai/api-reference/document-stores/delete-document-stores-id delete /api/document-stores/{id} Permanently deletes the specified DocumentStores. Document stores are containers for organizing and processing documents within a project. # Delete DocumentStores Source: https://developer.kodexa.ai/api-reference/document-stores/delete-documentstores /api-reference/api-docs.yaml delete /api/document-stores/{id} Permanently deletes the specified DocumentStores. Document stores are containers for organizing and processing documents within a project. # List Document Stores Source: https://developer.kodexa.ai/api-reference/document-stores/get-document-stores get /api/document-stores Returns a paginated list of Document Stores. Document stores are containers for organizing and processing documents within a project. # Get DocumentStores by ID Source: https://developer.kodexa.ai/api-reference/document-stores/get-document-stores-id get /api/document-stores/{id} Retrieves a single DocumentStores by its unique identifier. Document stores are containers for organizing and processing documents within a project. # Get DocumentStores change sequence Source: https://developer.kodexa.ai/api-reference/document-stores/get-document-stores-id-sequence get /api/document-stores/{id}/sequence Returns the current change sequence number for the specified DocumentStores. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get DocumentStores by ID Source: https://developer.kodexa.ai/api-reference/document-stores/get-documentstores-by-id /api-reference/api-docs.yaml get /api/document-stores/{id} Retrieves a single DocumentStores by its unique identifier. Document stores are containers for organizing and processing documents within a project. # Get DocumentStores change sequence Source: https://developer.kodexa.ai/api-reference/document-stores/get-documentstores-change-sequence /api-reference/api-docs.yaml get /api/document-stores/{id}/sequence Returns the current change sequence number for the specified DocumentStores. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Document Stores Source: https://developer.kodexa.ai/api-reference/document-stores/list-document-stores /api-reference/api-docs.yaml get /api/document-stores Returns a paginated list of Document Stores. Document stores are containers for organizing and processing documents within a project. # List DocumentStores audit history Source: https://developer.kodexa.ai/api-reference/document-stores/list-documentstores-audit-history /api-reference/api-docs.yaml get /api/document-stores/{id}/audit Returns the paginated audit history for a single DocumentStores. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create DocumentStores Source: https://developer.kodexa.ai/api-reference/document-stores/post-document-stores post /api/document-stores Creates a new DocumentStores. Document stores are containers for organizing and processing documents within a project. # Upload file to document store Source: https://developer.kodexa.ai/api-reference/document-stores/post-document-stores-id-upload post /api/document-stores/{id}/upload Uploads a file to the document store, creating a new document family and native content object. The file is stored in S3 and a document family record is created with the given path and metadata. # Update DocumentStores Source: https://developer.kodexa.ai/api-reference/document-stores/put-document-stores-id put /api/document-stores/{id} Updates an existing DocumentStores. Document stores are containers for organizing and processing documents within a project. # Update DocumentStores Source: https://developer.kodexa.ai/api-reference/document-stores/update-documentstores /api-reference/api-docs.yaml put /api/document-stores/{id} Updates an existing DocumentStores. Document stores are containers for organizing and processing documents within a project. # Upload file to document store Source: https://developer.kodexa.ai/api-reference/document-stores/upload-file-to-document-store /api-reference/api-docs.yaml post /api/document-stores/{id}/upload Uploads a file to the document store, creating a new document family and native content object. The file is stored in S3 and a document family record is created with the given path and metadata. # Create DocumentTransition Source: https://developer.kodexa.ai/api-reference/document-transitions/create-documenttransition /api-reference/api-docs.yaml post /api/document-transitions Creates a new DocumentTransition. Document transitions record state changes through processing stages. # Delete DocumentTransition Source: https://developer.kodexa.ai/api-reference/document-transitions/delete-document-transitions-id delete /api/document-transitions/{id} Permanently deletes the specified DocumentTransition. Document transitions record state changes through processing stages. # Delete DocumentTransition Source: https://developer.kodexa.ai/api-reference/document-transitions/delete-documenttransition /api-reference/api-docs.yaml delete /api/document-transitions/{id} Permanently deletes the specified DocumentTransition. Document transitions record state changes through processing stages. # List Document Transitions Source: https://developer.kodexa.ai/api-reference/document-transitions/get-document-transitions get /api/document-transitions Returns a paginated list of Document Transitions. Document transitions record state changes through processing stages. # Get DocumentTransition by ID Source: https://developer.kodexa.ai/api-reference/document-transitions/get-document-transitions-id get /api/document-transitions/{id} Retrieves a single DocumentTransition by its unique identifier. Document transitions record state changes through processing stages. # Get DocumentTransition change sequence Source: https://developer.kodexa.ai/api-reference/document-transitions/get-document-transitions-id-sequence get /api/document-transitions/{id}/sequence Returns the current change sequence number for the specified DocumentTransition. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get DocumentTransition by ID Source: https://developer.kodexa.ai/api-reference/document-transitions/get-documenttransition-by-id /api-reference/api-docs.yaml get /api/document-transitions/{id} Retrieves a single DocumentTransition by its unique identifier. Document transitions record state changes through processing stages. # Get DocumentTransition change sequence Source: https://developer.kodexa.ai/api-reference/document-transitions/get-documenttransition-change-sequence /api-reference/api-docs.yaml get /api/document-transitions/{id}/sequence Returns the current change sequence number for the specified DocumentTransition. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Document Transitions Source: https://developer.kodexa.ai/api-reference/document-transitions/list-document-transitions /api-reference/api-docs.yaml get /api/document-transitions Returns a paginated list of Document Transitions. Document transitions record state changes through processing stages. # Create DocumentTransition Source: https://developer.kodexa.ai/api-reference/document-transitions/post-document-transitions post /api/document-transitions Creates a new DocumentTransition. Document transitions record state changes through processing stages. # Update DocumentTransition Source: https://developer.kodexa.ai/api-reference/document-transitions/put-document-transitions-id put /api/document-transitions/{id} Updates an existing DocumentTransition. Document transitions record state changes through processing stages. # Update DocumentTransition Source: https://developer.kodexa.ai/api-reference/document-transitions/update-documenttransition /api-reference/api-docs.yaml put /api/document-transitions/{id} Updates an existing DocumentTransition. Document transitions record state changes through processing stages. # Add a label to a document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/add-a-label-to-a-document-family /api-reference/api-docs.yaml post /api/document-families/{id}/labels Adds a label (tag) to the document family for categorization and filtering. # Assess document for knowledge Source: https://developer.kodexa.ai/api-reference/documentfamilies/assess-document-for-knowledge /api-reference/api-docs.yaml post /api/document-families/{id}/assess Evaluates the document family's features against all active knowledge sets for the organization using CNF expression matching. Returns applicable and newly applied knowledge sets, along with any additional features inherited from matching sets. # Clear document family status Source: https://developer.kodexa.ai/api-reference/documentfamilies/clear-document-family-status /api-reference/api-docs.yaml delete /api/document-families/{id}/status Clears the processing status of the document family (sets it to none). The explicit counterpart to the status update, which requires a statusId. # Create DocumentFamily Source: https://developer.kodexa.ai/api-reference/documentfamilies/create-documentfamily /api-reference/api-docs.yaml post /api/document-families Creates a new DocumentFamily. Document families group related content objects representing a single logical document. # Delete DocumentFamily Source: https://developer.kodexa.ai/api-reference/documentfamilies/delete-documentfamily /api-reference/api-docs.yaml delete /api/document-families/{id} Permanently deletes the specified DocumentFamily. Document families group related content objects representing a single logical document. # Download original native file Source: https://developer.kodexa.ai/api-reference/documentfamilies/download-original-native-file /api-reference/api-docs.yaml get /api/document-families/{id}/native # Export document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/export-document-family /api-reference/api-docs.yaml get /api/document-families/{id}/export Exports the document family as a .dfm archive containing all content objects and metadata. # Export document family data Source: https://developer.kodexa.ai/api-reference/documentfamilies/export-document-family-data /api-reference/api-docs.yaml get /api/document-families/{id}/data Exports the extracted data from the document family in the specified format (JSON, CSV, XML, or NDJson). # Export document family extracted data Source: https://developer.kodexa.ai/api-reference/documentfamilies/export-document-family-extracted-data /api-reference/api-docs.yaml get /api/document-families/{id}/data-export # Get applied knowledge sets Source: https://developer.kodexa.ai/api-reference/documentfamilies/get-applied-knowledge-sets /api-reference/api-docs.yaml get /api/document-families/{id}/applied-knowledge-sets Returns the knowledge sets that have been applied to the specified document family. # Get document family page image Source: https://developer.kodexa.ai/api-reference/documentfamilies/get-document-family-page-image /api-reference/api-docs.yaml get /api/document-families/{id}/image Returns a rendered page image (PNG) of the document family's latest native content object. Use the page parameter to specify which page to render. Serves pre-generated images when available; falls back to on-demand PDF rendering. # Get DocumentFamily by ID Source: https://developer.kodexa.ai/api-reference/documentfamilies/get-documentfamily-by-id /api-reference/api-docs.yaml get /api/document-families/{id} Retrieves a single DocumentFamily by its unique identifier. Document families group related content objects representing a single logical document. # Get DocumentFamily change sequence Source: https://developer.kodexa.ai/api-reference/documentfamilies/get-documentfamily-change-sequence /api-reference/api-docs.yaml get /api/document-families/{id}/sequence Returns the current change sequence number for the specified DocumentFamily. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get related knowledge items Source: https://developer.kodexa.ai/api-reference/documentfamilies/get-related-knowledge-items /api-reference/api-docs.yaml get /api/document-families/{id}/knowledge-items Returns knowledge items related to the specified document family. # Link knowledge feature to document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/link-knowledge-feature-to-document-family /api-reference/api-docs.yaml post /api/document-families/{id}/add-knowledge-feature Links an existing knowledge feature to the document family via the join table, enabling knowledge-based classification and AI-assisted processing. # List DocumentFamilies Source: https://developer.kodexa.ai/api-reference/documentfamilies/list-documentfamilies /api-reference/api-docs.yaml get /api/document-families Returns a paginated list of DocumentFamilies. Document families group related content objects representing a single logical document. # Lock a document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/lock-a-document-family /api-reference/api-docs.yaml post /api/document-families/{id}/lock Acquires an exclusive lock on the document family, preventing other users from modifying it. # Remove a label from a document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/remove-a-label-from-a-document-family /api-reference/api-docs.yaml delete /api/document-families/{id}/labels/{labelId} Removes a label from the document family. # Remove document family assignees Source: https://developer.kodexa.ai/api-reference/documentfamilies/remove-document-family-assignees /api-reference/api-docs.yaml delete /api/document-families/{id}/assignees Removes all assignees from the document family. # Rename a document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/rename-a-document-family /api-reference/api-docs.yaml put /api/document-families/{id}/rename Renames the document family by updating its path. # Reprocess a document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/reprocess-a-document-family /api-reference/api-docs.yaml post /api/document-families/{id}/reprocess Rolls back assistant contributions and re-triggers the processing pipeline. If assistantIds are provided, only those assistants' contributions are rolled back; otherwise all assistants are auto-detected from the transition chain. # Touch document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/touch-document-family /api-reference/api-docs.yaml get /api/document-families/{id}/touch Triggers event processing for the document family without modifying its content. Use this to re-trigger assistant processing. # Unlink knowledge feature from document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/unlink-knowledge-feature-from-document-family /api-reference/api-docs.yaml post /api/document-families/{id}/remove-knowledge-feature Removes the link between a knowledge feature and the document family. The knowledge feature itself is not deleted. # Unlock a document family Source: https://developer.kodexa.ai/api-reference/documentfamilies/unlock-a-document-family /api-reference/api-docs.yaml post /api/document-families/{id}/unlock Releases the exclusive lock on the document family, allowing other users to modify it. # Update document family assignees Source: https://developer.kodexa.ai/api-reference/documentfamilies/update-document-family-assignees /api-reference/api-docs.yaml put /api/document-families/{id}/assignees Assigns a user to the document family for review. # Update document family status Source: https://developer.kodexa.ai/api-reference/documentfamilies/update-document-family-status /api-reference/api-docs.yaml put /api/document-families/{id}/status Updates the processing status of the document family. # Update DocumentFamily Source: https://developer.kodexa.ai/api-reference/documentfamilies/update-documentfamily /api-reference/api-docs.yaml put /api/document-families/{id} Updates an existing DocumentFamily. Document families group related content objects representing a single logical document. # Upload new content Source: https://developer.kodexa.ai/api-reference/documentfamilies/upload-new-content /api-reference/api-docs.yaml post /api/document-families/{id}/new-content Uploads new content to the document family. Accepts multipart/form-data with the file and optional transition tracking metadata. Returns 400 if the document family is locked. # Abort a multipart upload Source: https://developer.kodexa.ai/api-reference/documentstores/abort-a-multipart-upload /api-reference/api-docs.yaml post /api/document-stores/{id}/multipart-upload-abort Cancels an in-progress multipart upload session and releases the reserved S3 resources. # Complete a multipart upload Source: https://developer.kodexa.ai/api-reference/documentstores/complete-a-multipart-upload /api-reference/api-docs.yaml post /api/document-stores/{id}/multipart-upload-complete Finalises a multipart upload and registers the assembled file as a document family in the store. # Complete a presigned upload Source: https://developer.kodexa.ai/api-reference/documentstores/complete-a-presigned-upload /api-reference/api-docs.yaml post /api/document-stores/{id}/presigned-upload-complete Notify the platform that a presigned upload finished so it can register the document family. # Create a document inline (no upload) Source: https://developer.kodexa.ai/api-reference/documentstores/create-a-document-inline-no-upload /api-reference/api-docs.yaml post /api/document-stores/{id}/create-document # Get presigned URLs for upload parts Source: https://developer.kodexa.ai/api-reference/documentstores/get-presigned-urls-for-upload-parts /api-reference/api-docs.yaml post /api/document-stores/{id}/multipart-upload-part-urls Returns presigned PUT URLs for the requested part numbers of an in-progress multipart upload. # Initiate a multipart upload Source: https://developer.kodexa.ai/api-reference/documentstores/initiate-a-multipart-upload /api-reference/api-docs.yaml post /api/document-stores/{id}/multipart-upload-request Initiates an S3 multipart upload session and returns the uploadId, s3Key, and expiry. Use with /multipart-upload-part-urls and /multipart-upload-complete for large files. # Request a presigned upload URL Source: https://developer.kodexa.ai/api-reference/documentstores/request-a-presigned-upload-url /api-reference/api-docs.yaml post /api/document-stores/{id}/presigned-upload-request Returns a presigned URL the client can PUT directly to S3 for large file uploads. # Create PlatformEvent Source: https://developer.kodexa.ai/api-reference/events/create-platformevent /api-reference/api-docs.yaml post /api/events Creates a new PlatformEvent. Platform events record system events for notifications and integrations. # Delete PlatformEvent Source: https://developer.kodexa.ai/api-reference/events/delete-events-id delete /api/events/{id} Permanently deletes the specified PlatformEvent. Platform events record system events for notifications and integrations. # Delete PlatformEvent Source: https://developer.kodexa.ai/api-reference/events/delete-platformevent /api-reference/api-docs.yaml delete /api/events/{id} Permanently deletes the specified PlatformEvent. Platform events record system events for notifications and integrations. # List Events Source: https://developer.kodexa.ai/api-reference/events/get-events get /api/events Returns a paginated list of Events. Platform events record system events for notifications and integrations. # Get PlatformEvent by ID Source: https://developer.kodexa.ai/api-reference/events/get-events-id get /api/events/{id} Retrieves a single PlatformEvent by its unique identifier. Platform events record system events for notifications and integrations. # Get PlatformEvent change sequence Source: https://developer.kodexa.ai/api-reference/events/get-events-id-sequence get /api/events/{id}/sequence Returns the current change sequence number for the specified PlatformEvent. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get PlatformEvent by ID Source: https://developer.kodexa.ai/api-reference/events/get-platformevent-by-id /api-reference/api-docs.yaml get /api/events/{id} Retrieves a single PlatformEvent by its unique identifier. Platform events record system events for notifications and integrations. # Get PlatformEvent change sequence Source: https://developer.kodexa.ai/api-reference/events/get-platformevent-change-sequence /api-reference/api-docs.yaml get /api/events/{id}/sequence Returns the current change sequence number for the specified PlatformEvent. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Events Source: https://developer.kodexa.ai/api-reference/events/list-events /api-reference/api-docs.yaml get /api/events Returns a paginated list of Events. Platform events record system events for notifications and integrations. # Create PlatformEvent Source: https://developer.kodexa.ai/api-reference/events/post-events post /api/events Creates a new PlatformEvent. Platform events record system events for notifications and integrations. # Publish UI event Source: https://developer.kodexa.ai/api-reference/events/post-events-ui post /api/events/ui Publishes a single UI event for real-time processing and notification delivery. # Publish UI events batch Source: https://developer.kodexa.ai/api-reference/events/post-events-ui-batch post /api/events/ui/batch Publishes multiple UI events in a single request for efficient batch processing. # Publish UI event Source: https://developer.kodexa.ai/api-reference/events/publish-ui-event /api-reference/api-docs.yaml post /api/events/ui Publishes a single UI event for real-time processing and notification delivery. # Publish UI events batch Source: https://developer.kodexa.ai/api-reference/events/publish-ui-events-batch /api-reference/api-docs.yaml post /api/events/ui/batch Publishes multiple UI events in a single request for efficient batch processing. # Update PlatformEvent Source: https://developer.kodexa.ai/api-reference/events/put-events-id put /api/events/{id} Updates an existing PlatformEvent. Platform events record system events for notifications and integrations. # Update PlatformEvent Source: https://developer.kodexa.ai/api-reference/events/update-platformevent /api-reference/api-docs.yaml put /api/events/{id} Updates an existing PlatformEvent. Platform events record system events for notifications and integrations. # Create ExecutionSlice Source: https://developer.kodexa.ai/api-reference/execution-slices/create-executionslice /api-reference/api-docs.yaml post /api/execution-slices Creates a new ExecutionSlice. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Delete ExecutionSlice Source: https://developer.kodexa.ai/api-reference/execution-slices/delete-execution-slices-id delete /api/execution-slices/{id} Permanently deletes the specified ExecutionSlice. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Delete ExecutionSlice Source: https://developer.kodexa.ai/api-reference/execution-slices/delete-executionslice /api-reference/api-docs.yaml delete /api/execution-slices/{id} Permanently deletes the specified ExecutionSlice. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # List Execution Slices Source: https://developer.kodexa.ai/api-reference/execution-slices/get-execution-slices get /api/execution-slices Returns a paginated list of Execution Slices. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Get ExecutionSlice by ID Source: https://developer.kodexa.ai/api-reference/execution-slices/get-execution-slices-id get /api/execution-slices/{id} Retrieves a single ExecutionSlice by its unique identifier. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Get ExecutionSlice change sequence Source: https://developer.kodexa.ai/api-reference/execution-slices/get-execution-slices-id-sequence get /api/execution-slices/{id}/sequence Returns the current change sequence number for the specified ExecutionSlice. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get ExecutionSlice by ID Source: https://developer.kodexa.ai/api-reference/execution-slices/get-executionslice-by-id /api-reference/api-docs.yaml get /api/execution-slices/{id} Retrieves a single ExecutionSlice by its unique identifier. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Get ExecutionSlice change sequence Source: https://developer.kodexa.ai/api-reference/execution-slices/get-executionslice-change-sequence /api-reference/api-docs.yaml get /api/execution-slices/{id}/sequence Returns the current change sequence number for the specified ExecutionSlice. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Execution Slices Source: https://developer.kodexa.ai/api-reference/execution-slices/list-execution-slices /api-reference/api-docs.yaml get /api/execution-slices Returns a paginated list of Execution Slices. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Create ExecutionSlice Source: https://developer.kodexa.ai/api-reference/execution-slices/post-execution-slices post /api/execution-slices Creates a new ExecutionSlice. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Update ExecutionSlice Source: https://developer.kodexa.ai/api-reference/execution-slices/put-execution-slices-id put /api/execution-slices/{id} Updates an existing ExecutionSlice. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Update ExecutionSlice Source: https://developer.kodexa.ai/api-reference/execution-slices/update-executionslice /api-reference/api-docs.yaml put /api/execution-slices/{id} Updates an existing ExecutionSlice. Execution slices represent batches of work dispatched as a single Lambda/K8s invocation. # Cancel execution Source: https://developer.kodexa.ai/api-reference/executions/cancel-execution /api-reference/api-docs.yaml put /api/executions/{executionId}/cancel Cancels a running execution. The execution will be marked as cancelled and processing will stop. # Clean execution cache Source: https://developer.kodexa.ai/api-reference/executions/clean-execution-cache /api-reference/api-docs.yaml delete /api/executions/{executionId}/cache # Create Execution Source: https://developer.kodexa.ai/api-reference/executions/create-execution /api-reference/api-docs.yaml post /api/executions Creates a new Execution. Executions record the details and results of processing sessions. # Create execution status message Source: https://developer.kodexa.ai/api-reference/executions/create-execution-status-message /api-reference/api-docs.yaml post /api/executions/{executionId}/status-messages Creates a new status message for the specified execution. A maximum of 10 messages are kept per execution; older messages are automatically removed. # Delete Execution Source: https://developer.kodexa.ai/api-reference/executions/delete-execution /api-reference/api-docs.yaml delete /api/executions/{id} Permanently deletes the specified Execution. Executions record the details and results of processing sessions. # Delete Execution Source: https://developer.kodexa.ai/api-reference/executions/delete-executions-id delete /api/executions/{id} Permanently deletes the specified Execution. Executions record the details and results of processing sessions. # Download slice cache content Source: https://developer.kodexa.ai/api-reference/executions/download-slice-cache-content /api-reference/api-docs.yaml get /api/executions/{executionId}/slices/{sliceId}/cache # Get Execution by ID Source: https://developer.kodexa.ai/api-reference/executions/get-execution-by-id /api-reference/api-docs.yaml get /api/executions/{id} Retrieves a single Execution by its unique identifier. Executions record the details and results of processing sessions. # Get Execution change sequence Source: https://developer.kodexa.ai/api-reference/executions/get-execution-change-sequence /api-reference/api-docs.yaml get /api/executions/{id}/sequence Returns the current change sequence number for the specified Execution. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get execution status messages Source: https://developer.kodexa.ai/api-reference/executions/get-execution-status-messages /api-reference/api-docs.yaml get /api/executions/{executionId}/status-messages Returns all status messages for the specified execution, ordered by datetime ascending. # List Executions Source: https://developer.kodexa.ai/api-reference/executions/get-executions get /api/executions Returns a paginated list of Executions. Executions record the details and results of processing sessions. # Get execution status messages Source: https://developer.kodexa.ai/api-reference/executions/get-executions-executionId-status-messages get /api/executions/{executionId}/status-messages Returns all status messages for the specified execution, ordered by datetime ascending. # Get Execution by ID Source: https://developer.kodexa.ai/api-reference/executions/get-executions-id get /api/executions/{id} Retrieves a single Execution by its unique identifier. Executions record the details and results of processing sessions. # Get Execution change sequence Source: https://developer.kodexa.ai/api-reference/executions/get-executions-id-sequence get /api/executions/{id}/sequence Returns the current change sequence number for the specified Execution. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List execution status messages (legacy session route) Source: https://developer.kodexa.ai/api-reference/executions/list-execution-status-messages-legacy-session-route /api-reference/api-docs.yaml get /api/sessions/{sessionId}/executions/{executionId}/status-messages Backward-compatible alias of /executions/{executionId}/status-messages. sessionId is accepted but ignored. # List Executions Source: https://developer.kodexa.ai/api-reference/executions/list-executions /api-reference/api-docs.yaml get /api/executions Returns a paginated list of Executions. Executions record the details and results of processing sessions. # Post execution status message (legacy session route) Source: https://developer.kodexa.ai/api-reference/executions/post-execution-status-message-legacy-session-route /api-reference/api-docs.yaml post /api/sessions/{sessionId}/executions/{executionId}/status-messages Backward-compatible alias of /executions/{executionId}/status-messages. sessionId is accepted but ignored. # Create Execution Source: https://developer.kodexa.ai/api-reference/executions/post-executions post /api/executions Creates a new Execution. Executions record the details and results of processing sessions. # Create execution status message Source: https://developer.kodexa.ai/api-reference/executions/post-executions-executionId-status-messages post /api/executions/{executionId}/status-messages Creates a new status message for the specified execution. A maximum of 10 messages are kept per execution; older messages are automatically removed. # Update Execution Source: https://developer.kodexa.ai/api-reference/executions/put-executions-id put /api/executions/{id} Updates an existing Execution. Executions record the details and results of processing sessions. # Cancel execution Source: https://developer.kodexa.ai/api-reference/executions/put-executions-id-cancel put /api/executions/{executionId}/cancel Cancels a running execution. The execution will be marked as cancelled and processing will stop. # Update Execution Source: https://developer.kodexa.ai/api-reference/executions/update-execution /api-reference/api-docs.yaml put /api/executions/{id} Updates an existing Execution. Executions record the details and results of processing sessions. # Upload slice cache content Source: https://developer.kodexa.ai/api-reference/executions/upload-slice-cache-content /api-reference/api-docs.yaml post /api/executions/{executionId}/slices/{sliceId}/cache Caches an intermediate pipeline result for a slice within an execution. # Abort a multipart intake upload Source: https://developer.kodexa.ai/api-reference/intake/abort-a-multipart-intake-upload /api-reference/api-docs.yaml post /api/intake/{orgSlug}/{intakeSlug}/multipart-upload-abort Cancels an in-progress intake multipart upload session. # Complete a multipart intake upload Source: https://developer.kodexa.ai/api-reference/intake/complete-a-multipart-intake-upload /api-reference/api-docs.yaml post /api/intake/{orgSlug}/{intakeSlug}/multipart-upload-complete Finalises a multipart intake upload and processes the assembled file through the intake pipeline. # Complete a presigned upload via intake Source: https://developer.kodexa.ai/api-reference/intake/complete-a-presigned-upload-via-intake /api-reference/api-docs.yaml post /api/intake/{orgSlug}/{intakeSlug}/presigned-upload-complete Notifies the platform that a presigned upload finished; the file is run through the intake pipeline and registered as a document family. # Create intake token Source: https://developer.kodexa.ai/api-reference/intake/create-intake-token /api-reference/api-docs.yaml post /api/intakes/{id}/tokens # Delete intake token Source: https://developer.kodexa.ai/api-reference/intake/delete-intake-token /api-reference/api-docs.yaml delete /api/intakes/{id}/tokens/{tokenId} # Get presigned URLs for intake upload parts Source: https://developer.kodexa.ai/api-reference/intake/get-presigned-urls-for-intake-upload-parts /api-reference/api-docs.yaml post /api/intake/{orgSlug}/{intakeSlug}/multipart-upload-part-urls Returns presigned PUT URLs for the requested part numbers of an in-progress intake multipart upload. # Initiate a multipart intake upload Source: https://developer.kodexa.ai/api-reference/intake/initiate-a-multipart-intake-upload /api-reference/api-docs.yaml post /api/intake/{orgSlug}/{intakeSlug}/multipart-upload-request Initiates an S3 multipart upload session for a large file delivered via intake. # List intake tokens Source: https://developer.kodexa.ai/api-reference/intake/list-intake-tokens /api-reference/api-docs.yaml get /api/intakes/{id}/tokens # Request a presigned upload URL via intake Source: https://developer.kodexa.ai/api-reference/intake/request-a-presigned-upload-url-via-intake /api-reference/api-docs.yaml post /api/intake/{orgSlug}/{intakeSlug}/presigned-upload-request Returns a presigned S3 PUT URL the client can use to upload a file directly. Use with /presigned-upload-complete to process the uploaded file through the intake pipeline. # Upload via intake endpoint Source: https://developer.kodexa.ai/api-reference/intake/upload-via-intake-endpoint /api-reference/api-docs.yaml post /api/intake/{orgSlug}/{intakeSlug} Uploads a document via a public intake endpoint identified by org and intake slugs. # Create Intake Source: https://developer.kodexa.ai/api-reference/intakes/create-intake /api-reference/api-docs.yaml post /api/intakes Creates a new Intake. Intakes configure how documents are ingested from external sources. # Delete Intake Source: https://developer.kodexa.ai/api-reference/intakes/delete-intake /api-reference/api-docs.yaml delete /api/intakes/{id} Permanently deletes the specified Intake. Intakes configure how documents are ingested from external sources. # Delete Intake Source: https://developer.kodexa.ai/api-reference/intakes/delete-intakes-id delete /api/intakes/{id} Permanently deletes the specified Intake. Intakes configure how documents are ingested from external sources. # Get Intake by ID Source: https://developer.kodexa.ai/api-reference/intakes/get-intake-by-id /api-reference/api-docs.yaml get /api/intakes/{id} Retrieves a single Intake by its unique identifier. Intakes configure how documents are ingested from external sources. # Get Intake change sequence Source: https://developer.kodexa.ai/api-reference/intakes/get-intake-change-sequence /api-reference/api-docs.yaml get /api/intakes/{id}/sequence Returns the current change sequence number for the specified Intake. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Intakes Source: https://developer.kodexa.ai/api-reference/intakes/get-intakes get /api/intakes Returns a paginated list of Intakes. Intakes configure how documents are ingested from external sources. # Get Intake by ID Source: https://developer.kodexa.ai/api-reference/intakes/get-intakes-id get /api/intakes/{id} Retrieves a single Intake by its unique identifier. Intakes configure how documents are ingested from external sources. # Get Intake change sequence Source: https://developer.kodexa.ai/api-reference/intakes/get-intakes-id-sequence get /api/intakes/{id}/sequence Returns the current change sequence number for the specified Intake. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Intakes Source: https://developer.kodexa.ai/api-reference/intakes/list-intakes /api-reference/api-docs.yaml get /api/intakes Returns a paginated list of Intakes. Intakes configure how documents are ingested from external sources. # Create Intake Source: https://developer.kodexa.ai/api-reference/intakes/post-intakes post /api/intakes Creates a new Intake. Intakes configure how documents are ingested from external sources. # Update Intake Source: https://developer.kodexa.ai/api-reference/intakes/put-intakes-id put /api/intakes/{id} Updates an existing Intake. Intakes configure how documents are ingested from external sources. # Update Intake Source: https://developer.kodexa.ai/api-reference/intakes/update-intake /api-reference/api-docs.yaml put /api/intakes/{id} Updates an existing Intake. Intakes configure how documents are ingested from external sources. # API Reference Source: https://developer.kodexa.ai/api-reference/introduction RESTful HTTP API for the Kodexa Platform, covering documents, assistants, agents, knowledge, and workflow orchestration with full programmatic access.
Kodexa API reference with authenticated requests, endpoints, and platform resources
## Overview The Kodexa Platform API provides programmatic access to all platform capabilities including document processing, AI assistants, knowledge management, and workflow orchestration. This REST API is the foundation that powers the [Kodexa Python SDK](/sdk/python/getting-started) and enables custom integrations. ## Base URL All API endpoints are relative to your Kodexa Platform instance: ```text theme={null} https://platform.kodexa-enterprise.com/api ``` For Enterprise deployments, replace with your instance URL. ## Authentication ### Getting Your API Key 1. Log in to the Kodexa Platform 2. Navigate to your profile settings 3. Go to the **API Keys** section 4. Generate a new API key or copy an existing one ### Using Your API Key Include your API key in the `x-api-key` header with every request: ```bash theme={null} curl -X GET "https://platform.kodexa-enterprise.com/api/projects" \ -H "x-api-key: your-api-key-here" ``` **Python example:** ```python theme={null} import requests headers = { "x-api-key": "your-api-key-here", "Content-Type": "application/json" } response = requests.get( "https://platform.kodexa-enterprise.com/api/projects", headers=headers ) ``` ## OpenAPI Specification The machine-readable specification for this API is served by your instance at `GET /v3/api-docs`, using the same authentication as any other endpoint. Use it to generate typed clients; the endpoint reference on this site is generated from it and has been refreshed to the 2026.10 contract. ### Request and Response Schemas Each resource is described by three schemas rather than one: * **``** - the response shape. `required` lists the fields the server always returns, and `nullable` marks the ones that can come back as `null`. * **`CreateRequest`** - the body accepted by `POST`. Server-generated fields (`id`, `uuid`, `createdOn`, `updatedOn`, `changeSequence`) are removed, and `required` names only what the server insists on - `TaskCreateRequest`, for example, requires just `projectId`. * **`UpdateRequest`** - the body accepted by `PUT`. Nothing is required, `changeSequence` is kept for optimistic locking (see [Update Semantics](#update-semantics)), and the ownership fields an update never writes are omitted. A nullable reference to another object is expressed as `allOf` plus `nullable: true`, so a generated type carries both the target schema and the fact that the field can be `null`. Fields that hold free-form JSON - workspace storage, prompt metadata, platform-event payloads - are described as JSON values (object, array, or scalar) rather than base64-encoded strings. ### Named Enumerations Every enumeration is emitted as a named component and referenced by `$ref`: `TaskStatusType`, `ExecutionStatus`, `ExecutionStatusMessageType`, `SortDirection`, `AuditAction`, `ChatMessageRole`, `WebLlmModelSize`, and the rest. Generators therefore produce one stable type per enumeration instead of inventing names such as `Type1` or `StatusType3`. `TaskStatusType` is `OPEN | IN_PROGRESS | DONE | BLOCKED | PENDING`. The older project-scoped task status type (`TODO` / `IN_PROGRESS` / `DONE`) is retired; a project template that still declares a task status with `statusType: TODO` is accepted and stored as `OPEN`. ### Operation IDs Operation IDs are unique, so no operation is dropped from a generated client by a name collision: * `PUT /api/tasks/{id}/status` is `setTaskStatus`, which leaves `updateTaskStatus` to `PUT /api/task-statuses/{id}`. * `GET /api/task-groups/{id}/history` is `listHistoryForTaskGroup`, which leaves `listTaskGroupHistory` to `GET /api/task-group-history`. * `cancelExecution` is defined once, on `PUT /api/executions/{executionId}/cancel`. **Changed in 2026.10:** the request and response JSON on the wire is unchanged - only the specification and the clients generated from it change. If you generate a client, regenerate it against a 2026.10 instance and expect compile-time changes: create calls take `CreateRequest` and update calls take `UpdateRequest` instead of the response type; response fields that were all optional are now typed as required where the server always sends them; enum types take their component names; JSON-blob fields become objects instead of strings; and the two operations above are renamed (`updateTaskStatus` and `listTaskGroupHistory` now refer to the task-status and task-group-history endpoints). Hand-written HTTP integrations need no change. ## API Conventions ### Resource Patterns The Kodexa API follows RESTful conventions: * **List resources:** `GET /api/{resource}` - Returns paginated list * **Get single resource:** `GET /api/{resource}/{id}` - Returns specific resource * **Create resource:** `POST /api/{resource}` - Creates new resource * **Update resource:** `PUT /api/{resource}/{id}` - Updates existing resource * **Delete resource:** `DELETE /api/{resource}/{id}` - Deletes resource ### Update Semantics Create and update requests persist exactly the fields you send: * **Explicit values always persist** - sending `false` or `0` writes `false` or `0`, including on fields that have a server-side default * **Omitted fields stay unchanged** - leave a field out of the body to leave its stored value untouched, so sparse updates are reliable * **`null` clears** - sending `null` clears a nullable field * **Round-trips are safe** - echoing back the body of a `GET` as a `PUT` is a no-op System-managed and ownership fields - `id`, `uuid`, `createdOn`, `createdByUserId`, `organizationId`, `projectId`, and soft-delete state - are ignored if included in an update body. `changeSequence` is never written directly; it serves only as the optimistic-locking token. Locking is enforced whenever the body carries a non-null `changeSequence` - including `0` on a resource that has never been updated - and a stale value returns `409 Conflict` with the current sequence so you can re-fetch and retry. Omit the field (or send `null`) to opt out of locking. Malformed writes are rejected up front: a body that is not a JSON object, or that repeats the same field under case-variant keys (for example `name` and `Name`), returns `400 Bad Request`. Uniqueness violations return `409 Conflict`, and setting a non-nullable field to `null` or referencing a record that doesn't exist returns `400 Bad Request`. **Changed in 2026.9:** previously, a field set to `false` or `0` in an update body could be silently dropped - the request returned `200 OK` but the value never changed - and on create a server-side default could overwrite an explicit `false` or `0`. Explicit values now always persist; to leave a field unchanged, omit it from the body. ### Pagination List endpoints support pagination via query parameters: ```text theme={null} GET /api/tasks?page=0&pageSize=20 ``` **Parameters:** * `page` (integer, optional) - Zero-indexed page number (default: 0) * `pageSize` (integer, optional) - Items per page (default: 10, max: 100) **Response structure:** ```json theme={null} { "content": [...], "totalPages": 5, "totalElements": 47, "number": 0, "size": 10, "first": true, "last": false } ``` ### Filtering & Sorting Most list endpoints support `filter`, `query`, and `sort` query parameters to narrow and order results. * **`filter`** - Filter results using the [SpringFilter-style syntax](/guides/reference/filtering-api). Common operators include `==`, `!=`, `=like=`, `=in=`, `and`, and `or`. Strings must be single-quoted. ```text theme={null} GET /api/executions?filter=status=='FAILED' and assistantId=='{id}' ``` * **`query`** - Free-text search across searchable fields (typically `name` and `description`). ```text theme={null} GET /api/projects?query=invoice ``` * **`sort`** - Sort results with `field:direction` pairs. Separate multiple sorts with `;`. Direction defaults to `asc`. ```text theme={null} GET /api/executions?sort=createdOn:desc ``` See the full [Filtering API Reference](/guides/reference/filtering-api) for all operators and examples. ### Error Responses The API uses standard HTTP status codes: * `200 OK` - Request succeeded * `201 Created` - Resource created successfully * `400 Bad Request` - Invalid request parameters * `401 Unauthorized` - Missing or invalid API key * `403 Forbidden` - Authenticated but not authorized * `404 Not Found` - Resource doesn't exist * `500 Internal Server Error` - Server error **Error response format:** ```json theme={null} { "error": "Resource not found", "message": "Task with ID 'abc123' does not exist", "status": 404 } ``` ## Common Resources The API is organized around these core resource types: * **[Projects](/api-reference/projects/get-projects)** - Container for tasks, assistants, and resources * **Tasks** - Document processing and workflow tasks * **[Assistants](/api-reference/assistants/get-assistants)** - AI assistant configurations and definitions * **[Documents](/api-reference/documentfamilies/get-document-families)** - Document families and content * **[Stores](/api-reference/document-stores/get-document-stores)** - Document, data, and model storage * **[Knowledge](/api-reference/knowledge-sets/get-knowledge-sets)** - Knowledge sets, items, and features * **[Executions](/api-reference/executions/get-executions)** - Pipeline and process execution tracking ## Rate Limiting API requests are subject to rate limiting to ensure platform stability: * **Rate limit:** 100 requests per minute per API key * **Burst limit:** 20 requests per second Rate limit headers are included in all responses: ```text theme={null} X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1634567890 ``` When rate limited, you'll receive a `429 Too Many Requests` response. ## Using the Python SDK For Python developers, we recommend using the [Kodexa Python SDK](/sdk/python/getting-started) which provides a high-level interface to this API: ```python theme={null} from kodexa import KodexaPlatform # Initialize with API key platform = KodexaPlatform( url="https://platform.kodexa-enterprise.com", api_key="your-api-key-here" ) # Work with resources naturally project = platform.get_project("my-project") tasks = project.get_tasks() ``` The SDK handles authentication, pagination, error handling, and provides typed models for all resources. ## API Endpoints Browse the complete API endpoint documentation in the **Endpoints** section below. Each endpoint includes: * HTTP methods and paths * Request/response schemas * Required and optional parameters * Example requests and responses * Authentication requirements # Create KnowledgeClauseFeature Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/create-knowledgeclausefeature /api-reference/api-docs.yaml post /api/knowledge-clause-features Creates a new KnowledgeClauseFeature. Knowledge clause features represent data points within knowledge clauses. # Delete KnowledgeClauseFeature Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/delete-knowledge-clause-features-id delete /api/knowledge-clause-features/{id} Permanently deletes the specified KnowledgeClauseFeature. Knowledge clause features represent data points within knowledge clauses. # Delete KnowledgeClauseFeature Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/delete-knowledgeclausefeature /api-reference/api-docs.yaml delete /api/knowledge-clause-features/{id} Permanently deletes the specified KnowledgeClauseFeature. Knowledge clause features represent data points within knowledge clauses. # List Knowledge Clause Features Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/get-knowledge-clause-features get /api/knowledge-clause-features Returns a paginated list of Knowledge Clause Features. Knowledge clause features represent data points within knowledge clauses. # Get KnowledgeClauseFeature by ID Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/get-knowledge-clause-features-id get /api/knowledge-clause-features/{id} Retrieves a single KnowledgeClauseFeature by its unique identifier. Knowledge clause features represent data points within knowledge clauses. # Get KnowledgeClauseFeature change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/get-knowledge-clause-features-id-sequence get /api/knowledge-clause-features/{id}/sequence Returns the current change sequence number for the specified KnowledgeClauseFeature. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeClauseFeature by ID Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/get-knowledgeclausefeature-by-id /api-reference/api-docs.yaml get /api/knowledge-clause-features/{id} Retrieves a single KnowledgeClauseFeature by its unique identifier. Knowledge clause features represent data points within knowledge clauses. # Get KnowledgeClauseFeature change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/get-knowledgeclausefeature-change-sequence /api-reference/api-docs.yaml get /api/knowledge-clause-features/{id}/sequence Returns the current change sequence number for the specified KnowledgeClauseFeature. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Clause Features Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/list-knowledge-clause-features /api-reference/api-docs.yaml get /api/knowledge-clause-features Returns a paginated list of Knowledge Clause Features. Knowledge clause features represent data points within knowledge clauses. # Create KnowledgeClauseFeature Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/post-knowledge-clause-features post /api/knowledge-clause-features Creates a new KnowledgeClauseFeature. Knowledge clause features represent data points within knowledge clauses. # Update KnowledgeClauseFeature Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/put-knowledge-clause-features-id put /api/knowledge-clause-features/{id} Updates an existing KnowledgeClauseFeature. Knowledge clause features represent data points within knowledge clauses. # Update KnowledgeClauseFeature Source: https://developer.kodexa.ai/api-reference/knowledge-clause-features/update-knowledgeclausefeature /api-reference/api-docs.yaml put /api/knowledge-clause-features/{id} Updates an existing KnowledgeClauseFeature. Knowledge clause features represent data points within knowledge clauses. # Create KnowledgeClause Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/create-knowledgeclause /api-reference/api-docs.yaml post /api/knowledge-clauses Creates a new KnowledgeClause. Knowledge clauses are segments within knowledge items. # Delete KnowledgeClause Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/delete-knowledge-clauses-id delete /api/knowledge-clauses/{id} Permanently deletes the specified KnowledgeClause. Knowledge clauses are segments within knowledge items. # Delete KnowledgeClause Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/delete-knowledgeclause /api-reference/api-docs.yaml delete /api/knowledge-clauses/{id} Permanently deletes the specified KnowledgeClause. Knowledge clauses are segments within knowledge items. # List Knowledge Clauses Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/get-knowledge-clauses get /api/knowledge-clauses Returns a paginated list of Knowledge Clauses. Knowledge clauses are segments within knowledge items. # Get KnowledgeClause by ID Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/get-knowledge-clauses-id get /api/knowledge-clauses/{id} Retrieves a single KnowledgeClause by its unique identifier. Knowledge clauses are segments within knowledge items. # Get KnowledgeClause change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/get-knowledge-clauses-id-sequence get /api/knowledge-clauses/{id}/sequence Returns the current change sequence number for the specified KnowledgeClause. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeClause by ID Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/get-knowledgeclause-by-id /api-reference/api-docs.yaml get /api/knowledge-clauses/{id} Retrieves a single KnowledgeClause by its unique identifier. Knowledge clauses are segments within knowledge items. # Get KnowledgeClause change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/get-knowledgeclause-change-sequence /api-reference/api-docs.yaml get /api/knowledge-clauses/{id}/sequence Returns the current change sequence number for the specified KnowledgeClause. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Clauses Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/list-knowledge-clauses /api-reference/api-docs.yaml get /api/knowledge-clauses Returns a paginated list of Knowledge Clauses. Knowledge clauses are segments within knowledge items. # Create KnowledgeClause Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/post-knowledge-clauses post /api/knowledge-clauses Creates a new KnowledgeClause. Knowledge clauses are segments within knowledge items. # Update KnowledgeClause Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/put-knowledge-clauses-id put /api/knowledge-clauses/{id} Updates an existing KnowledgeClause. Knowledge clauses are segments within knowledge items. # Update KnowledgeClause Source: https://developer.kodexa.ai/api-reference/knowledge-clauses/update-knowledgeclause /api-reference/api-docs.yaml put /api/knowledge-clauses/{id} Updates an existing KnowledgeClause. Knowledge clauses are segments within knowledge items. # Create KnowledgeFeatureType Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/create-knowledgefeaturetype /api-reference/api-docs.yaml post /api/knowledge-feature-types Creates a new KnowledgeFeatureType. Knowledge feature types define the kind of data a knowledge feature can hold. # Delete KnowledgeFeatureType Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/delete-knowledge-feature-types-id delete /api/knowledge-feature-types/{id} Permanently deletes the specified KnowledgeFeatureType. Knowledge feature types define the kind of data a knowledge feature can hold. # Delete KnowledgeFeatureType Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/delete-knowledgefeaturetype /api-reference/api-docs.yaml delete /api/knowledge-feature-types/{id} Permanently deletes the specified KnowledgeFeatureType. Knowledge feature types define the kind of data a knowledge feature can hold. # List Knowledge Feature Types Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/get-knowledge-feature-types get /api/knowledge-feature-types Returns a paginated list of Knowledge Feature Types. Knowledge feature types define the kind of data a knowledge feature can hold. # Get KnowledgeFeatureType by ID Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/get-knowledge-feature-types-id get /api/knowledge-feature-types/{id} Retrieves a single KnowledgeFeatureType by its unique identifier. Knowledge feature types define the kind of data a knowledge feature can hold. # Get KnowledgeFeatureType change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/get-knowledge-feature-types-id-sequence get /api/knowledge-feature-types/{id}/sequence Returns the current change sequence number for the specified KnowledgeFeatureType. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeFeatureType by ID Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/get-knowledgefeaturetype-by-id /api-reference/api-docs.yaml get /api/knowledge-feature-types/{id} Retrieves a single KnowledgeFeatureType by its unique identifier. Knowledge feature types define the kind of data a knowledge feature can hold. # Get KnowledgeFeatureType change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/get-knowledgefeaturetype-change-sequence /api-reference/api-docs.yaml get /api/knowledge-feature-types/{id}/sequence Returns the current change sequence number for the specified KnowledgeFeatureType. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Feature Types Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/list-knowledge-feature-types /api-reference/api-docs.yaml get /api/knowledge-feature-types Returns a paginated list of Knowledge Feature Types. Knowledge feature types define the kind of data a knowledge feature can hold. # Create KnowledgeFeatureType Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/post-knowledge-feature-types post /api/knowledge-feature-types Creates a new KnowledgeFeatureType. Knowledge feature types define the kind of data a knowledge feature can hold. # Update KnowledgeFeatureType Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/put-knowledge-feature-types-id put /api/knowledge-feature-types/{id} Updates an existing KnowledgeFeatureType. Knowledge feature types define the kind of data a knowledge feature can hold. # Update KnowledgeFeatureType Source: https://developer.kodexa.ai/api-reference/knowledge-feature-types/update-knowledgefeaturetype /api-reference/api-docs.yaml put /api/knowledge-feature-types/{id} Updates an existing KnowledgeFeatureType. Knowledge feature types define the kind of data a knowledge feature can hold. # Assign feature to document family Source: https://developer.kodexa.ai/api-reference/knowledge-features/assign-feature-to-document-family /api-reference/api-docs.yaml post /api/knowledge-features/assignToDocumentFamily Links a knowledge feature to a document family via the join table. # Assign feature to knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-features/assign-feature-to-knowledge-item /api-reference/api-docs.yaml post /api/knowledge-features/assignToKnowledgeItem Links a knowledge feature to a knowledge item. # Create KnowledgeFeature Source: https://developer.kodexa.ai/api-reference/knowledge-features/create-knowledgefeature /api-reference/api-docs.yaml post /api/knowledge-features Creates a new KnowledgeFeature. Knowledge features represent specific data points within knowledge items. # Delete KnowledgeFeature Source: https://developer.kodexa.ai/api-reference/knowledge-features/delete-knowledge-features-id delete /api/knowledge-features/{id} Permanently deletes the specified KnowledgeFeature. Knowledge features represent specific data points within knowledge items. # Unassign feature from document family Source: https://developer.kodexa.ai/api-reference/knowledge-features/delete-knowledge-features-unassignFromDocumentFamily delete /api/knowledge-features/unassignFromDocumentFamily Removes the link between a knowledge feature and a document family. # Unassign feature from knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-features/delete-knowledge-features-unassignFromKnowledgeItem delete /api/knowledge-features/unassignFromKnowledgeItem Removes the link between a knowledge feature and a knowledge item. # Delete KnowledgeFeature Source: https://developer.kodexa.ai/api-reference/knowledge-features/delete-knowledgefeature /api-reference/api-docs.yaml delete /api/knowledge-features/{id} Permanently deletes the specified KnowledgeFeature. Knowledge features represent specific data points within knowledge items. # Get features by document family Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-features-by-document-family /api-reference/api-docs.yaml get /api/knowledge-features/byDocumentFamily/{documentFamilyId} Returns knowledge features linked to the specified document family. # Get features by knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-features-by-knowledge-item /api-reference/api-docs.yaml get /api/knowledge-features/byKnowledgeItem/{knowledgeItemId} Returns knowledge features linked to the specified knowledge item. # List Knowledge Features Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-knowledge-features get /api/knowledge-features Returns a paginated list of Knowledge Features. Knowledge features represent specific data points within knowledge items. # Get features by document family Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-knowledge-features-byDocumentFamily-documentFamilyId get /api/knowledge-features/byDocumentFamily/{documentFamilyId} Returns knowledge features linked to the specified document family. # Get features by knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-knowledge-features-byKnowledgeItem-knowledgeItemId get /api/knowledge-features/byKnowledgeItem/{knowledgeItemId} Returns knowledge features linked to the specified knowledge item. # Get KnowledgeFeature by ID Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-knowledge-features-id get /api/knowledge-features/{id} Retrieves a single KnowledgeFeature by its unique identifier. Knowledge features represent specific data points within knowledge items. # Get KnowledgeFeature change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-knowledge-features-id-sequence get /api/knowledge-features/{id}/sequence Returns the current change sequence number for the specified KnowledgeFeature. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeFeature by ID Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-knowledgefeature-by-id /api-reference/api-docs.yaml get /api/knowledge-features/{id} Retrieves a single KnowledgeFeature by its unique identifier. Knowledge features represent specific data points within knowledge items. # Get KnowledgeFeature change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-features/get-knowledgefeature-change-sequence /api-reference/api-docs.yaml get /api/knowledge-features/{id}/sequence Returns the current change sequence number for the specified KnowledgeFeature. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Features Source: https://developer.kodexa.ai/api-reference/knowledge-features/list-knowledge-features /api-reference/api-docs.yaml get /api/knowledge-features Returns a paginated list of Knowledge Features. Knowledge features represent specific data points within knowledge items. # Create KnowledgeFeature Source: https://developer.kodexa.ai/api-reference/knowledge-features/post-knowledge-features post /api/knowledge-features Creates a new KnowledgeFeature. Knowledge features represent specific data points within knowledge items. # Assign feature to document family Source: https://developer.kodexa.ai/api-reference/knowledge-features/post-knowledge-features-assignToDocumentFamily post /api/knowledge-features/assignToDocumentFamily Links a knowledge feature to a document family via the join table. # Assign feature to knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-features/post-knowledge-features-assignToKnowledgeItem post /api/knowledge-features/assignToKnowledgeItem Links a knowledge feature to a knowledge item. # Update KnowledgeFeature Source: https://developer.kodexa.ai/api-reference/knowledge-features/put-knowledge-features-id put /api/knowledge-features/{id} Updates an existing KnowledgeFeature. Knowledge features represent specific data points within knowledge items. # Unassign feature from document family Source: https://developer.kodexa.ai/api-reference/knowledge-features/unassign-feature-from-document-family /api-reference/api-docs.yaml delete /api/knowledge-features/unassignFromDocumentFamily Removes the link between a knowledge feature and a document family. # Unassign feature from knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-features/unassign-feature-from-knowledge-item /api-reference/api-docs.yaml delete /api/knowledge-features/unassignFromKnowledgeItem Removes the link between a knowledge feature and a knowledge item. # Update KnowledgeFeature Source: https://developer.kodexa.ai/api-reference/knowledge-features/update-knowledgefeature /api-reference/api-docs.yaml put /api/knowledge-features/{id} Updates an existing KnowledgeFeature. Knowledge features represent specific data points within knowledge items. # Create KnowledgeItemType Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/create-knowledgeitemtype /api-reference/api-docs.yaml post /api/knowledge-item-types Creates a new KnowledgeItemType. Knowledge item types define the kind of knowledge item. # Delete KnowledgeItemType Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/delete-knowledge-item-types-id delete /api/knowledge-item-types/{id} Permanently deletes the specified KnowledgeItemType. Knowledge item types define the kind of knowledge item. # Delete KnowledgeItemType Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/delete-knowledgeitemtype /api-reference/api-docs.yaml delete /api/knowledge-item-types/{id} Permanently deletes the specified KnowledgeItemType. Knowledge item types define the kind of knowledge item. # List Knowledge Item Types Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/get-knowledge-item-types get /api/knowledge-item-types Returns a paginated list of Knowledge Item Types. Knowledge item types define the kind of knowledge item. # Get KnowledgeItemType by ID Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/get-knowledge-item-types-id get /api/knowledge-item-types/{id} Retrieves a single KnowledgeItemType by its unique identifier. Knowledge item types define the kind of knowledge item. # Get KnowledgeItemType change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/get-knowledge-item-types-id-sequence get /api/knowledge-item-types/{id}/sequence Returns the current change sequence number for the specified KnowledgeItemType. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeItemType by ID Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/get-knowledgeitemtype-by-id /api-reference/api-docs.yaml get /api/knowledge-item-types/{id} Retrieves a single KnowledgeItemType by its unique identifier. Knowledge item types define the kind of knowledge item. # Get KnowledgeItemType change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/get-knowledgeitemtype-change-sequence /api-reference/api-docs.yaml get /api/knowledge-item-types/{id}/sequence Returns the current change sequence number for the specified KnowledgeItemType. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Item Types Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/list-knowledge-item-types /api-reference/api-docs.yaml get /api/knowledge-item-types Returns a paginated list of Knowledge Item Types. Knowledge item types define the kind of knowledge item. # Create KnowledgeItemType Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/post-knowledge-item-types post /api/knowledge-item-types Creates a new KnowledgeItemType. Knowledge item types define the kind of knowledge item. # Update KnowledgeItemType Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/put-knowledge-item-types-id put /api/knowledge-item-types/{id} Updates an existing KnowledgeItemType. Knowledge item types define the kind of knowledge item. # Update KnowledgeItemType Source: https://developer.kodexa.ai/api-reference/knowledge-item-types/update-knowledgeitemtype /api-reference/api-docs.yaml put /api/knowledge-item-types/{id} Updates an existing KnowledgeItemType. Knowledge item types define the kind of knowledge item. # Create KnowledgeItem Source: https://developer.kodexa.ai/api-reference/knowledge-items/create-knowledgeitem /api-reference/api-docs.yaml post /api/knowledge-items Creates a new KnowledgeItem. Knowledge items are individual pieces of knowledge within knowledge sets. # Delete knowledge item attachment Source: https://developer.kodexa.ai/api-reference/knowledge-items/delete-knowledge-item-attachment /api-reference/api-docs.yaml delete /api/knowledge-items/{id}/attachment Deletes the attachment from the knowledge item. # Delete KnowledgeItem Source: https://developer.kodexa.ai/api-reference/knowledge-items/delete-knowledge-items-id delete /api/knowledge-items/{id} Permanently deletes the specified KnowledgeItem. Knowledge items are individual pieces of knowledge within knowledge sets. # Delete knowledge item attachment Source: https://developer.kodexa.ai/api-reference/knowledge-items/delete-knowledge-items-id-attachment delete /api/knowledge-items/{id}/attachment Deletes the attachment from the knowledge item. # Delete KnowledgeItem Source: https://developer.kodexa.ai/api-reference/knowledge-items/delete-knowledgeitem /api-reference/api-docs.yaml delete /api/knowledge-items/{id} Permanently deletes the specified KnowledgeItem. Knowledge items are individual pieces of knowledge within knowledge sets. # Get document families for knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-document-families-for-knowledge-item /api-reference/api-docs.yaml get /api/knowledge-items/{id}/documentFamilies Returns document families that share features with the specified knowledge item. # Get knowledge item attachment Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledge-item-attachment /api-reference/api-docs.yaml get /api/knowledge-items/{id}/attachment Downloads the attachment file for the knowledge item. # List Knowledge Items Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledge-items get /api/knowledge-items Returns a paginated list of Knowledge Items. Knowledge items are individual pieces of knowledge within knowledge sets. # Get KnowledgeItem by ID Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledge-items-id get /api/knowledge-items/{id} Retrieves a single KnowledgeItem by its unique identifier. Knowledge items are individual pieces of knowledge within knowledge sets. # Get knowledge item attachment Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledge-items-id-attachment get /api/knowledge-items/{id}/attachment Downloads the attachment file for the knowledge item. # Get document families for knowledge item Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledge-items-id-documentFamilies get /api/knowledge-items/{id}/documentFamilies Returns document families that share features with the specified knowledge item. # Get KnowledgeItem change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledge-items-id-sequence get /api/knowledge-items/{id}/sequence Returns the current change sequence number for the specified KnowledgeItem. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeItem by ID Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledgeitem-by-id /api-reference/api-docs.yaml get /api/knowledge-items/{id} Retrieves a single KnowledgeItem by its unique identifier. Knowledge items are individual pieces of knowledge within knowledge sets. # Get KnowledgeItem change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-items/get-knowledgeitem-change-sequence /api-reference/api-docs.yaml get /api/knowledge-items/{id}/sequence Returns the current change sequence number for the specified KnowledgeItem. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Items Source: https://developer.kodexa.ai/api-reference/knowledge-items/list-knowledge-items /api-reference/api-docs.yaml get /api/knowledge-items Returns a paginated list of Knowledge Items. Knowledge items are individual pieces of knowledge within knowledge sets. # Create KnowledgeItem Source: https://developer.kodexa.ai/api-reference/knowledge-items/post-knowledge-items post /api/knowledge-items Creates a new KnowledgeItem. Knowledge items are individual pieces of knowledge within knowledge sets. # Upload knowledge item attachment Source: https://developer.kodexa.ai/api-reference/knowledge-items/post-knowledge-items-id-attachment post /api/knowledge-items/{id}/attachment Uploads an attachment file to the knowledge item. # Update KnowledgeItem Source: https://developer.kodexa.ai/api-reference/knowledge-items/put-knowledge-items-id put /api/knowledge-items/{id} Updates an existing KnowledgeItem. Knowledge items are individual pieces of knowledge within knowledge sets. # Update KnowledgeItem Source: https://developer.kodexa.ai/api-reference/knowledge-items/update-knowledgeitem /api-reference/api-docs.yaml put /api/knowledge-items/{id} Updates an existing KnowledgeItem. Knowledge items are individual pieces of knowledge within knowledge sets. # Upload knowledge item attachment Source: https://developer.kodexa.ai/api-reference/knowledge-items/upload-knowledge-item-attachment /api-reference/api-docs.yaml post /api/knowledge-items/{id}/attachment Uploads an attachment file to the knowledge item. # Create KnowledgeSetSnapshot Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/create-knowledgesetsnapshot /api-reference/api-docs.yaml post /api/knowledge-set-snapshots Creates a new KnowledgeSetSnapshot. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Delete KnowledgeSetSnapshot Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/delete-knowledge-set-snapshots-id delete /api/knowledge-set-snapshots/{id} Permanently deletes the specified KnowledgeSetSnapshot. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Delete KnowledgeSetSnapshot Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/delete-knowledgesetsnapshot /api-reference/api-docs.yaml delete /api/knowledge-set-snapshots/{id} Permanently deletes the specified KnowledgeSetSnapshot. Knowledge set snapshots capture the state of a knowledge set at a point in time. # List Knowledge Set Snapshots Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/get-knowledge-set-snapshots get /api/knowledge-set-snapshots Returns a paginated list of Knowledge Set Snapshots. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Get KnowledgeSetSnapshot by ID Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/get-knowledge-set-snapshots-id get /api/knowledge-set-snapshots/{id} Retrieves a single KnowledgeSetSnapshot by its unique identifier. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Get KnowledgeSetSnapshot change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/get-knowledge-set-snapshots-id-sequence get /api/knowledge-set-snapshots/{id}/sequence Returns the current change sequence number for the specified KnowledgeSetSnapshot. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeSetSnapshot by ID Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/get-knowledgesetsnapshot-by-id /api-reference/api-docs.yaml get /api/knowledge-set-snapshots/{id} Retrieves a single KnowledgeSetSnapshot by its unique identifier. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Get KnowledgeSetSnapshot change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/get-knowledgesetsnapshot-change-sequence /api-reference/api-docs.yaml get /api/knowledge-set-snapshots/{id}/sequence Returns the current change sequence number for the specified KnowledgeSetSnapshot. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Set Snapshots Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/list-knowledge-set-snapshots /api-reference/api-docs.yaml get /api/knowledge-set-snapshots Returns a paginated list of Knowledge Set Snapshots. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Create KnowledgeSetSnapshot Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/post-knowledge-set-snapshots post /api/knowledge-set-snapshots Creates a new KnowledgeSetSnapshot. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Update KnowledgeSetSnapshot Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/put-knowledge-set-snapshots-id put /api/knowledge-set-snapshots/{id} Updates an existing KnowledgeSetSnapshot. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Update KnowledgeSetSnapshot Source: https://developer.kodexa.ai/api-reference/knowledge-set-snapshots/update-knowledgesetsnapshot /api-reference/api-docs.yaml put /api/knowledge-set-snapshots/{id} Updates an existing KnowledgeSetSnapshot. Knowledge set snapshots capture the state of a knowledge set at a point in time. # Create KnowledgeSetSource Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/create-knowledgesetsource /api-reference/api-docs.yaml post /api/knowledge-set-sources Creates a new KnowledgeSetSource. Knowledge set sources reference the origin of knowledge set data. # Delete KnowledgeSetSource Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/delete-knowledge-set-sources-id delete /api/knowledge-set-sources/{id} Permanently deletes the specified KnowledgeSetSource. Knowledge set sources reference the origin of knowledge set data. # Delete KnowledgeSetSource Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/delete-knowledgesetsource /api-reference/api-docs.yaml delete /api/knowledge-set-sources/{id} Permanently deletes the specified KnowledgeSetSource. Knowledge set sources reference the origin of knowledge set data. # List Knowledge Set Sources Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/get-knowledge-set-sources get /api/knowledge-set-sources Returns a paginated list of Knowledge Set Sources. Knowledge set sources reference the origin of knowledge set data. # Get KnowledgeSetSource by ID Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/get-knowledge-set-sources-id get /api/knowledge-set-sources/{id} Retrieves a single KnowledgeSetSource by its unique identifier. Knowledge set sources reference the origin of knowledge set data. # Get KnowledgeSetSource change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/get-knowledge-set-sources-id-sequence get /api/knowledge-set-sources/{id}/sequence Returns the current change sequence number for the specified KnowledgeSetSource. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeSetSource by ID Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/get-knowledgesetsource-by-id /api-reference/api-docs.yaml get /api/knowledge-set-sources/{id} Retrieves a single KnowledgeSetSource by its unique identifier. Knowledge set sources reference the origin of knowledge set data. # Get KnowledgeSetSource change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/get-knowledgesetsource-change-sequence /api-reference/api-docs.yaml get /api/knowledge-set-sources/{id}/sequence Returns the current change sequence number for the specified KnowledgeSetSource. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Knowledge Set Sources Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/list-knowledge-set-sources /api-reference/api-docs.yaml get /api/knowledge-set-sources Returns a paginated list of Knowledge Set Sources. Knowledge set sources reference the origin of knowledge set data. # Create KnowledgeSetSource Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/post-knowledge-set-sources post /api/knowledge-set-sources Creates a new KnowledgeSetSource. Knowledge set sources reference the origin of knowledge set data. # Update KnowledgeSetSource Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/put-knowledge-set-sources-id put /api/knowledge-set-sources/{id} Updates an existing KnowledgeSetSource. Knowledge set sources reference the origin of knowledge set data. # Update KnowledgeSetSource Source: https://developer.kodexa.ai/api-reference/knowledge-set-sources/update-knowledgesetsource /api-reference/api-docs.yaml put /api/knowledge-set-sources/{id} Updates an existing KnowledgeSetSource. Knowledge set sources reference the origin of knowledge set data. # Create KnowledgeSet Source: https://developer.kodexa.ai/api-reference/knowledge-sets/create-knowledgeset /api-reference/api-docs.yaml post /api/knowledge-sets Creates a new KnowledgeSet. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Delete knowledge set attachment Source: https://developer.kodexa.ai/api-reference/knowledge-sets/delete-knowledge-set-attachment /api-reference/api-docs.yaml delete /api/knowledge-sets/{id}/attachments/{attachmentId} # Delete KnowledgeSet Source: https://developer.kodexa.ai/api-reference/knowledge-sets/delete-knowledge-sets-id delete /api/knowledge-sets/{id} Permanently deletes the specified KnowledgeSet. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Delete KnowledgeSet Source: https://developer.kodexa.ai/api-reference/knowledge-sets/delete-knowledgeset /api-reference/api-docs.yaml delete /api/knowledge-sets/{id} Permanently deletes the specified KnowledgeSet. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Download knowledge set attachment Source: https://developer.kodexa.ai/api-reference/knowledge-sets/download-knowledge-set-attachment /api-reference/api-docs.yaml get /api/knowledge-sets/{id}/attachments/{attachmentId} # List Knowledge Sets Source: https://developer.kodexa.ai/api-reference/knowledge-sets/get-knowledge-sets get /api/knowledge-sets Returns a paginated list of Knowledge Sets. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Get knowledge sets by type Source: https://developer.kodexa.ai/api-reference/knowledge-sets/get-knowledge-sets-by-type /api-reference/api-docs.yaml get /api/knowledge-sets/byType/{setType} Returns knowledge sets filtered by the specified type. # Get knowledge sets by type Source: https://developer.kodexa.ai/api-reference/knowledge-sets/get-knowledge-sets-byType-setType get /api/knowledge-sets/byType/{setType} Returns knowledge sets filtered by the specified type. # Get KnowledgeSet by ID Source: https://developer.kodexa.ai/api-reference/knowledge-sets/get-knowledge-sets-id get /api/knowledge-sets/{id} Retrieves a single KnowledgeSet by its unique identifier. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Get KnowledgeSet change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-sets/get-knowledge-sets-id-sequence get /api/knowledge-sets/{id}/sequence Returns the current change sequence number for the specified KnowledgeSet. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get KnowledgeSet by ID Source: https://developer.kodexa.ai/api-reference/knowledge-sets/get-knowledgeset-by-id /api-reference/api-docs.yaml get /api/knowledge-sets/{id} Retrieves a single KnowledgeSet by its unique identifier. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Get KnowledgeSet change sequence Source: https://developer.kodexa.ai/api-reference/knowledge-sets/get-knowledgeset-change-sequence /api-reference/api-docs.yaml get /api/knowledge-sets/{id}/sequence Returns the current change sequence number for the specified KnowledgeSet. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List knowledge set attachments Source: https://developer.kodexa.ai/api-reference/knowledge-sets/list-knowledge-set-attachments /api-reference/api-docs.yaml get /api/knowledge-sets/{id}/attachments # List Knowledge Sets Source: https://developer.kodexa.ai/api-reference/knowledge-sets/list-knowledge-sets /api-reference/api-docs.yaml get /api/knowledge-sets Returns a paginated list of Knowledge Sets. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # List KnowledgeSet audit history Source: https://developer.kodexa.ai/api-reference/knowledge-sets/list-knowledgeset-audit-history /api-reference/api-docs.yaml get /api/knowledge-sets/{id}/audit Returns the paginated audit history for a single KnowledgeSet. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create KnowledgeSet Source: https://developer.kodexa.ai/api-reference/knowledge-sets/post-knowledge-sets post /api/knowledge-sets Creates a new KnowledgeSet. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Update KnowledgeSet Source: https://developer.kodexa.ai/api-reference/knowledge-sets/put-knowledge-sets-id put /api/knowledge-sets/{id} Updates an existing KnowledgeSet. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Update KnowledgeSet Source: https://developer.kodexa.ai/api-reference/knowledge-sets/update-knowledgeset /api-reference/api-docs.yaml put /api/knowledge-sets/{id} Updates an existing KnowledgeSet. Knowledge sets are collections of extracted knowledge used for AI-assisted processing. # Upload knowledge set attachment Source: https://developer.kodexa.ai/api-reference/knowledge-sets/upload-knowledge-set-attachment /api-reference/api-docs.yaml post /api/knowledge-sets/{id}/attachments # Create Label Source: https://developer.kodexa.ai/api-reference/labels/create-label /api-reference/api-docs.yaml post /api/labels Creates a new Label. Labels are used for categorizing and tagging resources within an organization. # Delete Label Source: https://developer.kodexa.ai/api-reference/labels/delete-label /api-reference/api-docs.yaml delete /api/labels/{id} Permanently deletes the specified Label. Labels are used for categorizing and tagging resources within an organization. # Delete Label Source: https://developer.kodexa.ai/api-reference/labels/delete-labels-id delete /api/labels/{id} Permanently deletes the specified Label. Labels are used for categorizing and tagging resources within an organization. # Get Label by ID Source: https://developer.kodexa.ai/api-reference/labels/get-label-by-id /api-reference/api-docs.yaml get /api/labels/{id} Retrieves a single Label by its unique identifier. Labels are used for categorizing and tagging resources within an organization. # Get Label change sequence Source: https://developer.kodexa.ai/api-reference/labels/get-label-change-sequence /api-reference/api-docs.yaml get /api/labels/{id}/sequence Returns the current change sequence number for the specified Label. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Labels Source: https://developer.kodexa.ai/api-reference/labels/get-labels get /api/labels Returns a paginated list of Labels. Labels are used for categorizing and tagging resources within an organization. # Get Label by ID Source: https://developer.kodexa.ai/api-reference/labels/get-labels-id get /api/labels/{id} Retrieves a single Label by its unique identifier. Labels are used for categorizing and tagging resources within an organization. # Get Label change sequence Source: https://developer.kodexa.ai/api-reference/labels/get-labels-id-sequence get /api/labels/{id}/sequence Returns the current change sequence number for the specified Label. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Labels Source: https://developer.kodexa.ai/api-reference/labels/list-labels /api-reference/api-docs.yaml get /api/labels Returns a paginated list of Labels. Labels are used for categorizing and tagging resources within an organization. # Create Label Source: https://developer.kodexa.ai/api-reference/labels/post-labels post /api/labels Creates a new Label. Labels are used for categorizing and tagging resources within an organization. # Update Label Source: https://developer.kodexa.ai/api-reference/labels/put-labels-id put /api/labels/{id} Updates an existing Label. Labels are used for categorizing and tagging resources within an organization. # Update Label Source: https://developer.kodexa.ai/api-reference/labels/update-label /api-reference/api-docs.yaml put /api/labels/{id} Updates an existing Label. Labels are used for categorizing and tagging resources within an organization. # Create LinkedDelta Source: https://developer.kodexa.ai/api-reference/linked-deltas/create-linkeddelta /api-reference/api-docs.yaml post /api/linked-deltas Creates a new LinkedDelta. Linked deltas represent changes between document versions. # Delete LinkedDelta Source: https://developer.kodexa.ai/api-reference/linked-deltas/delete-linked-deltas-id delete /api/linked-deltas/{id} Permanently deletes the specified LinkedDelta. Linked deltas represent changes between document versions. # Delete LinkedDelta Source: https://developer.kodexa.ai/api-reference/linked-deltas/delete-linkeddelta /api-reference/api-docs.yaml delete /api/linked-deltas/{id} Permanently deletes the specified LinkedDelta. Linked deltas represent changes between document versions. # List Linked Deltas Source: https://developer.kodexa.ai/api-reference/linked-deltas/get-linked-deltas get /api/linked-deltas Returns a paginated list of Linked Deltas. Linked deltas represent changes between document versions. A linked delta is one saved set of changes queued against a document's stored contents. Every item in the response reports how far that save got in `state`, with `errorMessage` and `retryCount` carrying the detail. ## Delta states * **`PENDING`** — queued, waiting to be applied. A save that hit a transient error returns to `PENDING` for another attempt, with `retryCount` incremented and `errorMessage` holding the last error. A save that a full-document write replaced — one still being applied, or one already applied while that write was in progress — is also requeued, so it re-applies on top of the new contents. * **`APPLYING`** — claimed and being applied to the document. * **`APPLIED`** — every operation in the save landed, and the result was stored. * **`APPLIED_PARTIAL`** — some operations referenced a parent or object the document no longer has, so they could never land. Those operations were dropped, the remaining ones were applied, and the result was stored. `errorMessage` summarizes what was dropped. * **`FAILED`** — nothing in the save could be applied, or the attempts were exhausted. `errorMessage` carries the reason, and none of the changes were stored. * **`SUPERSEDED`** — a save that had failed and was then replaced by a full-document write. It records that the earlier failure no longer reflects the stored document. `APPLIED_PARTIAL` is a success state — the document was written. Treat it alongside `APPLIED` when checking whether a save landed, and read `errorMessage` to see which changes were dropped. Only `FAILED` means nothing was stored. To review the saves that landed only in part, filter the list on the state — for example `filter=state=='APPLIED_PARTIAL'`. # Get LinkedDelta by ID Source: https://developer.kodexa.ai/api-reference/linked-deltas/get-linked-deltas-id get /api/linked-deltas/{id} Retrieves a single LinkedDelta by its unique identifier. Linked deltas represent changes between document versions. # Get LinkedDelta change sequence Source: https://developer.kodexa.ai/api-reference/linked-deltas/get-linked-deltas-id-sequence get /api/linked-deltas/{id}/sequence Returns the current change sequence number for the specified LinkedDelta. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get LinkedDelta by ID Source: https://developer.kodexa.ai/api-reference/linked-deltas/get-linkeddelta-by-id /api-reference/api-docs.yaml get /api/linked-deltas/{id} Retrieves a single LinkedDelta by its unique identifier. Linked deltas represent changes between document versions. # Get LinkedDelta change sequence Source: https://developer.kodexa.ai/api-reference/linked-deltas/get-linkeddelta-change-sequence /api-reference/api-docs.yaml get /api/linked-deltas/{id}/sequence Returns the current change sequence number for the specified LinkedDelta. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Linked Deltas Source: https://developer.kodexa.ai/api-reference/linked-deltas/list-linked-deltas /api-reference/api-docs.yaml get /api/linked-deltas Returns a paginated list of Linked Deltas. Linked deltas represent changes between document versions. # Create LinkedDelta Source: https://developer.kodexa.ai/api-reference/linked-deltas/post-linked-deltas post /api/linked-deltas Creates a new LinkedDelta. Linked deltas represent changes between document versions. # Update LinkedDelta Source: https://developer.kodexa.ai/api-reference/linked-deltas/put-linked-deltas-id put /api/linked-deltas/{id} Updates an existing LinkedDelta. Linked deltas represent changes between document versions. # Update LinkedDelta Source: https://developer.kodexa.ai/api-reference/linked-deltas/update-linkeddelta /api-reference/api-docs.yaml put /api/linked-deltas/{id} Updates an existing LinkedDelta. Linked deltas represent changes between document versions. # Create Message Source: https://developer.kodexa.ai/api-reference/messages/create-message /api-reference/api-docs.yaml post /api/messages Creates a new Message. Messages are individual communications within a channel. # Delete Message Source: https://developer.kodexa.ai/api-reference/messages/delete-message /api-reference/api-docs.yaml delete /api/messages/{id} Permanently deletes the specified Message. Messages are individual communications within a channel. # Delete Message Source: https://developer.kodexa.ai/api-reference/messages/delete-messages-id delete /api/messages/{id} Permanently deletes the specified Message. Messages are individual communications within a channel. # Get Message by ID Source: https://developer.kodexa.ai/api-reference/messages/get-message-by-id /api-reference/api-docs.yaml get /api/messages/{id} Retrieves a single Message by its unique identifier. Messages are individual communications within a channel. # Get Message change sequence Source: https://developer.kodexa.ai/api-reference/messages/get-message-change-sequence /api-reference/api-docs.yaml get /api/messages/{id}/sequence Returns the current change sequence number for the specified Message. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Messages Source: https://developer.kodexa.ai/api-reference/messages/get-messages get /api/messages Returns a paginated list of Messages. Messages are individual communications within a channel. # Get Message by ID Source: https://developer.kodexa.ai/api-reference/messages/get-messages-id get /api/messages/{id} Retrieves a single Message by its unique identifier. Messages are individual communications within a channel. # Get Message change sequence Source: https://developer.kodexa.ai/api-reference/messages/get-messages-id-sequence get /api/messages/{id}/sequence Returns the current change sequence number for the specified Message. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Messages Source: https://developer.kodexa.ai/api-reference/messages/list-messages /api-reference/api-docs.yaml get /api/messages Returns a paginated list of Messages. Messages are individual communications within a channel. # Create Message Source: https://developer.kodexa.ai/api-reference/messages/post-messages post /api/messages Creates a new Message. Messages are individual communications within a channel. # Update Message Source: https://developer.kodexa.ai/api-reference/messages/put-messages-id put /api/messages/{id} Updates an existing Message. Messages are individual communications within a channel. # Update Message Source: https://developer.kodexa.ai/api-reference/messages/update-message /api-reference/api-docs.yaml put /api/messages/{id} Updates an existing Message. Messages are individual communications within a channel. # Get aggregated model costs Source: https://developer.kodexa.ai/api-reference/model-costs/get-aggregated-model-costs /api-reference/api-docs.yaml get /api/model-costs Returns aggregated model cost data for the current organization, including token usage and billing information. # Get aggregated model costs Source: https://developer.kodexa.ai/api-reference/model-costs/get-model-costs get /api/model-costs Returns aggregated model cost data for the current organization, including token usage and billing information. # Create OrchestratorModuleRuntime Source: https://developer.kodexa.ai/api-reference/module-runtimes/create-orchestratormoduleruntime /api-reference/api-docs.yaml post /api/module-runtimes Creates a new OrchestratorModuleRuntime. Module runtimes define processing environments for modules. # Delete OrchestratorModuleRuntime Source: https://developer.kodexa.ai/api-reference/module-runtimes/delete-module-runtimes-id delete /api/module-runtimes/{id} Permanently deletes the specified OrchestratorModuleRuntime. Module runtimes define processing environments for modules. # Delete OrchestratorModuleRuntime Source: https://developer.kodexa.ai/api-reference/module-runtimes/delete-orchestratormoduleruntime /api-reference/api-docs.yaml delete /api/module-runtimes/{id} Permanently deletes the specified OrchestratorModuleRuntime. Module runtimes define processing environments for modules. # Deploy agent runtimes Source: https://developer.kodexa.ai/api-reference/module-runtimes/deploy-agent-runtimes /api-reference/api-docs.yaml post /api/agent-runtimes/deploy Proxied to the orchestrator. Platform admin only. # Deploy module runtimes Source: https://developer.kodexa.ai/api-reference/module-runtimes/deploy-module-runtimes /api-reference/api-docs.yaml post /api/module-runtimes/deploy Proxied to the orchestrator. Platform admin only. # Get module runtime statistics Source: https://developer.kodexa.ai/api-reference/module-runtimes/get-module-runtime-statistics /api-reference/api-docs.yaml get /api/module-runtimes/{id}/stats Returns runtime statistics (invocations, latency, errors) proxied from the orchestrator. # List Module Runtimes Source: https://developer.kodexa.ai/api-reference/module-runtimes/get-module-runtimes get /api/module-runtimes Returns a paginated list of Module Runtimes. Module runtimes define processing environments for modules. # Get OrchestratorModuleRuntime by ID Source: https://developer.kodexa.ai/api-reference/module-runtimes/get-module-runtimes-id get /api/module-runtimes/{id} Retrieves a single OrchestratorModuleRuntime by its unique identifier. Module runtimes define processing environments for modules. # Get OrchestratorModuleRuntime change sequence Source: https://developer.kodexa.ai/api-reference/module-runtimes/get-module-runtimes-id-sequence get /api/module-runtimes/{id}/sequence Returns the current change sequence number for the specified OrchestratorModuleRuntime. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get OrchestratorModuleRuntime by ID Source: https://developer.kodexa.ai/api-reference/module-runtimes/get-orchestratormoduleruntime-by-id /api-reference/api-docs.yaml get /api/module-runtimes/{id} Retrieves a single OrchestratorModuleRuntime by its unique identifier. Module runtimes define processing environments for modules. # Get OrchestratorModuleRuntime change sequence Source: https://developer.kodexa.ai/api-reference/module-runtimes/get-orchestratormoduleruntime-change-sequence /api-reference/api-docs.yaml get /api/module-runtimes/{id}/sequence Returns the current change sequence number for the specified OrchestratorModuleRuntime. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Module Runtimes Source: https://developer.kodexa.ai/api-reference/module-runtimes/list-module-runtimes /api-reference/api-docs.yaml get /api/module-runtimes Returns a paginated list of Module Runtimes. Module runtimes define processing environments for modules. # List OrchestratorModuleRuntime audit history Source: https://developer.kodexa.ai/api-reference/module-runtimes/list-orchestratormoduleruntime-audit-history /api-reference/api-docs.yaml get /api/module-runtimes/{id}/audit Returns the paginated audit history for a single OrchestratorModuleRuntime. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create OrchestratorModuleRuntime Source: https://developer.kodexa.ai/api-reference/module-runtimes/post-module-runtimes post /api/module-runtimes Creates a new OrchestratorModuleRuntime. Module runtimes define processing environments for modules. # Update OrchestratorModuleRuntime Source: https://developer.kodexa.ai/api-reference/module-runtimes/put-module-runtimes-id put /api/module-runtimes/{id} Updates an existing OrchestratorModuleRuntime. Module runtimes define processing environments for modules. # Undeploy agent runtimes Source: https://developer.kodexa.ai/api-reference/module-runtimes/undeploy-agent-runtimes /api-reference/api-docs.yaml post /api/agent-runtimes/undeploy Proxied to the orchestrator. Platform admin only. # Undeploy module runtimes Source: https://developer.kodexa.ai/api-reference/module-runtimes/undeploy-module-runtimes /api-reference/api-docs.yaml post /api/module-runtimes/undeploy Proxied to the orchestrator. Platform admin only. # Update OrchestratorModuleRuntime Source: https://developer.kodexa.ai/api-reference/module-runtimes/update-orchestratormoduleruntime /api-reference/api-docs.yaml put /api/module-runtimes/{id} Updates an existing OrchestratorModuleRuntime. Module runtimes define processing environments for modules. # Create Modules Source: https://developer.kodexa.ai/api-reference/modules/create-modules /api-reference/api-docs.yaml post /api/modules Creates a new Modules. Modules are processing modules that can be executed on documents. # Delete Modules Source: https://developer.kodexa.ai/api-reference/modules/delete-modules /api-reference/api-docs.yaml delete /api/modules/{id} Permanently deletes the specified Modules. Modules are processing modules that can be executed on documents. # Delete Modules Source: https://developer.kodexa.ai/api-reference/modules/delete-modules-id delete /api/modules/{id} Permanently deletes the specified Modules. Modules are processing modules that can be executed on documents. # Download module implementation ZIP Source: https://developer.kodexa.ai/api-reference/modules/download-module-implementation-zip /api-reference/api-docs.yaml get /api/modules/{id}/implementation # List Modules Source: https://developer.kodexa.ai/api-reference/modules/get-modules get /api/modules Returns a paginated list of Modules. Modules are processing modules that can be executed on documents. # Get Modules by ID Source: https://developer.kodexa.ai/api-reference/modules/get-modules-by-id /api-reference/api-docs.yaml get /api/modules/{id} Retrieves a single Modules by its unique identifier. Modules are processing modules that can be executed on documents. # Get Modules change sequence Source: https://developer.kodexa.ai/api-reference/modules/get-modules-change-sequence /api-reference/api-docs.yaml get /api/modules/{id}/sequence Returns the current change sequence number for the specified Modules. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get Modules by ID Source: https://developer.kodexa.ai/api-reference/modules/get-modules-id get /api/modules/{id} Retrieves a single Modules by its unique identifier. Modules are processing modules that can be executed on documents. # Get Modules change sequence Source: https://developer.kodexa.ai/api-reference/modules/get-modules-id-sequence get /api/modules/{id}/sequence Returns the current change sequence number for the specified Modules. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Modules Source: https://developer.kodexa.ai/api-reference/modules/list-modules /api-reference/api-docs.yaml get /api/modules Returns a paginated list of Modules. Modules are processing modules that can be executed on documents. # List Modules audit history Source: https://developer.kodexa.ai/api-reference/modules/list-modules-audit-history /api-reference/api-docs.yaml get /api/modules/{id}/audit Returns the paginated audit history for a single Modules. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create Modules Source: https://developer.kodexa.ai/api-reference/modules/post-modules post /api/modules Creates a new Modules. Modules are processing modules that can be executed on documents. # Update Modules Source: https://developer.kodexa.ai/api-reference/modules/put-modules-id put /api/modules/{id} Updates an existing Modules. Modules are processing modules that can be executed on documents. # Update Modules Source: https://developer.kodexa.ai/api-reference/modules/update-modules /api-reference/api-docs.yaml put /api/modules/{id} Updates an existing Modules. Modules are processing modules that can be executed on documents. # Upload module implementation ZIP Source: https://developer.kodexa.ai/api-reference/modules/upload-module-implementation-zip /api-reference/api-docs.yaml post /api/modules/{id}/implementation # Get fair-scheduler config for an org Source: https://developer.kodexa.ai/api-reference/operations/get-fair-scheduler-config-for-an-org /api-reference/api-docs.yaml get /api/fair-scheduler/orgs/{orgId} # List fair-scheduler orgs Source: https://developer.kodexa.ai/api-reference/operations/list-fair-scheduler-orgs /api-reference/api-docs.yaml get /api/fair-scheduler/orgs # Operations dashboard executions Source: https://developer.kodexa.ai/api-reference/operations/operations-dashboard-executions /api-reference/api-docs.yaml get /api/ops/executions Platform-admin-only proxy returning the operations dashboard's execution list. # Operations dashboard fair-scheduler view Source: https://developer.kodexa.ai/api-reference/operations/operations-dashboard-fair-scheduler-view /api-reference/api-docs.yaml get /api/ops/fair-scheduler/orgs # Update fair-scheduler config for an org Source: https://developer.kodexa.ai/api-reference/operations/update-fair-scheduler-config-for-an-org /api-reference/api-docs.yaml put /api/fair-scheduler/orgs/{orgId} # Activity step error callback Source: https://developer.kodexa.ai/api-reference/orchestrator-callbacks/activity-step-error-callback /api-reference/api-docs.yaml post /api/orchestrator/activities/{id}/steps/{stepId}/error Internal callback from the orchestrator. Not for external consumers. # Activity step result callback Source: https://developer.kodexa.ai/api-reference/orchestrator-callbacks/activity-step-result-callback /api-reference/api-docs.yaml post /api/orchestrator/activities/{id}/steps/{stepId}/result Internal callback from the orchestrator. Not for external consumers. # Activity step skip callback Source: https://developer.kodexa.ai/api-reference/orchestrator-callbacks/activity-step-skip-callback /api-reference/api-docs.yaml post /api/orchestrator/activities/{id}/steps/{stepId}/skip Internal callback from the orchestrator. Not for external consumers. # Activity step status callback Source: https://developer.kodexa.ai/api-reference/orchestrator-callbacks/activity-step-status-callback /api-reference/api-docs.yaml post /api/orchestrator/activities/{id}/steps/{stepId}/status Internal callback from the orchestrator. Not for external consumers. # Create OrchestratorModule Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/create-orchestratormodule /api-reference/api-docs.yaml post /api/orchestrator-modules Creates a new OrchestratorModule. Orchestrator modules are processing components used within pipelines. # Delete OrchestratorModule Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/delete-orchestrator-modules-id delete /api/orchestrator-modules/{id} Permanently deletes the specified OrchestratorModule. Orchestrator modules are processing components used within pipelines. # Delete OrchestratorModule Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/delete-orchestratormodule /api-reference/api-docs.yaml delete /api/orchestrator-modules/{id} Permanently deletes the specified OrchestratorModule. Orchestrator modules are processing components used within pipelines. # List Orchestrator Modules Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/get-orchestrator-modules get /api/orchestrator-modules Returns a paginated list of Orchestrator Modules. Orchestrator modules are processing components used within pipelines. # Get OrchestratorModule by ID Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/get-orchestrator-modules-id get /api/orchestrator-modules/{id} Retrieves a single OrchestratorModule by its unique identifier. Orchestrator modules are processing components used within pipelines. # Get OrchestratorModule change sequence Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/get-orchestrator-modules-id-sequence get /api/orchestrator-modules/{id}/sequence Returns the current change sequence number for the specified OrchestratorModule. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get OrchestratorModule by ID Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/get-orchestratormodule-by-id /api-reference/api-docs.yaml get /api/orchestrator-modules/{id} Retrieves a single OrchestratorModule by its unique identifier. Orchestrator modules are processing components used within pipelines. # Get OrchestratorModule change sequence Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/get-orchestratormodule-change-sequence /api-reference/api-docs.yaml get /api/orchestrator-modules/{id}/sequence Returns the current change sequence number for the specified OrchestratorModule. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Orchestrator Modules Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/list-orchestrator-modules /api-reference/api-docs.yaml get /api/orchestrator-modules Returns a paginated list of Orchestrator Modules. Orchestrator modules are processing components used within pipelines. # List OrchestratorModule audit history Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/list-orchestratormodule-audit-history /api-reference/api-docs.yaml get /api/orchestrator-modules/{id}/audit Returns the paginated audit history for a single OrchestratorModule. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create OrchestratorModule Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/post-orchestrator-modules post /api/orchestrator-modules Creates a new OrchestratorModule. Orchestrator modules are processing components used within pipelines. # Update OrchestratorModule Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/put-orchestrator-modules-id put /api/orchestrator-modules/{id} Updates an existing OrchestratorModule. Orchestrator modules are processing components used within pipelines. # Update OrchestratorModule Source: https://developer.kodexa.ai/api-reference/orchestrator-modules/update-orchestratormodule /api-reference/api-docs.yaml put /api/orchestrator-modules/{id} Updates an existing OrchestratorModule. Orchestrator modules are processing components used within pipelines. # Create Organization Source: https://developer.kodexa.ai/api-reference/organizations/create-organization /api-reference/api-docs.yaml post /api/organizations Creates a new Organization. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Delete an org secret Source: https://developer.kodexa.ai/api-reference/organizations/delete-an-org-secret /api-reference/api-docs.yaml delete /api/organizations/{orgId}/secrets/{secretName} # Delete Organization Source: https://developer.kodexa.ai/api-reference/organizations/delete-organization /api-reference/api-docs.yaml delete /api/organizations/{id} Permanently deletes the specified Organization. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Delete Organization Source: https://developer.kodexa.ai/api-reference/organizations/delete-organizations-id delete /api/organizations/{id} Permanently deletes the specified Organization. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Get Organization by ID Source: https://developer.kodexa.ai/api-reference/organizations/get-organization-by-id /api-reference/api-docs.yaml get /api/organizations/{id} Retrieves a single Organization by its unique identifier. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Get Organization change sequence Source: https://developer.kodexa.ai/api-reference/organizations/get-organization-change-sequence /api-reference/api-docs.yaml get /api/organizations/{id}/sequence Returns the current change sequence number for the specified Organization. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get organization labels Source: https://developer.kodexa.ai/api-reference/organizations/get-organization-labels /api-reference/api-docs.yaml get /api/organizations/{orgId}/labels Returns all labels defined for the specified organization. # Get organization members Source: https://developer.kodexa.ai/api-reference/organizations/get-organization-members /api-reference/api-docs.yaml get /api/organizations/{orgId}/members Returns all users who are members of the specified organization via team assignments. # List Organizations Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations get /api/organizations Returns a paginated list of Organizations. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Get Organization by ID Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations-id get /api/organizations/{id} Retrieves a single Organization by its unique identifier. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # List available modules Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations-id-available-modules get /api/organizations/{id}/available-modules Returns the processing modules available to the specified organization. # List available project templates Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations-id-available-project-templates get /api/organizations/{id}/available-project-templates Returns the project templates available to the specified organization. # Get Organization change sequence Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations-id-sequence get /api/organizations/{id}/sequence Returns the current change sequence number for the specified Organization. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List available AI models for an organization Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations-orgId-ai-models get /api/organizations/{orgId}/ai/models Returns the list of models available through the AI Gateway for the specified organization. # Get organization labels Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations-orgId-labels get /api/organizations/{orgId}/labels Returns all labels defined for the specified organization. # Get organization members Source: https://developer.kodexa.ai/api-reference/organizations/get-organizations-orgId-members get /api/organizations/{orgId}/members Returns all users who are members of the specified organization via team assignments. # Invoke LLM Source: https://developer.kodexa.ai/api-reference/organizations/invoke-llm /api-reference/api-docs.yaml post /api/organizations/{orgId}/llm Invokes the platform LLM for the specified organization. Resolves the prompt template (either from a stored prompt reference or an inline string), renders template parameters, and returns the LLM response as plain text. # List available modules Source: https://developer.kodexa.ai/api-reference/organizations/list-available-modules /api-reference/api-docs.yaml get /api/organizations/{id}/available-modules Returns the processing modules available to the specified organization. # List available project templates Source: https://developer.kodexa.ai/api-reference/organizations/list-available-project-templates /api-reference/api-docs.yaml get /api/organizations/{id}/available-project-templates Returns the project templates available to the specified organization. # List Organizations Source: https://developer.kodexa.ai/api-reference/organizations/list-organizations /api-reference/api-docs.yaml get /api/organizations Returns a paginated list of Organizations. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # List project templates available to an org Source: https://developer.kodexa.ai/api-reference/organizations/list-project-templates-available-to-an-org /api-reference/api-docs.yaml get /api/organizations/{orgId}/available-project-templates # List secrets for an org Source: https://developer.kodexa.ai/api-reference/organizations/list-secrets-for-an-org /api-reference/api-docs.yaml get /api/organizations/{orgId}/secrets Lists AWS Secrets Manager secrets owned by the organisation. Values are not returned. # Create Organization Source: https://developer.kodexa.ai/api-reference/organizations/post-organizations post /api/organizations Creates a new Organization. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Create a chat completion for an organization Source: https://developer.kodexa.ai/api-reference/organizations/post-organizations-orgId-ai-chat-completions post /api/organizations/{orgId}/ai/chat/completions Creates a chat completion using the specified model via the AI Gateway. Supports both synchronous and streaming (SSE) responses. Set `stream: true` in the request body to receive server-sent events. The request is proxied to the organization's configured AI provider (Bedrock, Azure, Gemini, etc.). # Invoke LLM Source: https://developer.kodexa.ai/api-reference/organizations/post-organizations-orgId-llm post /api/organizations/{orgId}/llm Invokes the platform LLM for the specified organization. Resolves the prompt template (either from a stored prompt reference or an inline string), renders template parameters, and returns the LLM response as plain text. # Update Organization Source: https://developer.kodexa.ai/api-reference/organizations/put-organizations-id put /api/organizations/{id} Updates an existing Organization. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Reveal an org secret value Source: https://developer.kodexa.ai/api-reference/organizations/reveal-an-org-secret-value /api-reference/api-docs.yaml get /api/organizations/{orgId}/secrets/{secretName}/value Returns the secret value. Permission gated; intended for secure server-side use. # Store a secret for an org Source: https://developer.kodexa.ai/api-reference/organizations/store-a-secret-for-an-org /api-reference/api-docs.yaml post /api/organizations/{orgId}/secrets # Update Organization Source: https://developer.kodexa.ai/api-reference/organizations/update-organization /api-reference/api-docs.yaml put /api/organizations/{id} Updates an existing Organization. Organizations are the top-level tenant in Kodexa. All projects, stores, and resources belong to an organization. # Get Permission by ID Source: https://developer.kodexa.ai/api-reference/permissions/get-permission-by-id /api-reference/api-docs.yaml get /api/permissions/{id} Retrieves a single Permission by its unique identifier. Permissions define resource:action pairs for fine-grained access control. # Get Permission change sequence Source: https://developer.kodexa.ai/api-reference/permissions/get-permission-change-sequence /api-reference/api-docs.yaml get /api/permissions/{id}/sequence Returns the current change sequence number for the specified Permission. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Permissions Source: https://developer.kodexa.ai/api-reference/permissions/get-permissions get /api/permissions Returns a paginated list of Permissions. Permissions define resource:action pairs for fine-grained access control. # Get Permission by ID Source: https://developer.kodexa.ai/api-reference/permissions/get-permissions-id get /api/permissions/{id} Retrieves a single Permission by its unique identifier. Permissions define resource:action pairs for fine-grained access control. # Get Permission change sequence Source: https://developer.kodexa.ai/api-reference/permissions/get-permissions-id-sequence get /api/permissions/{id}/sequence Returns the current change sequence number for the specified Permission. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Permissions Source: https://developer.kodexa.ai/api-reference/permissions/list-permissions /api-reference/api-docs.yaml get /api/permissions Returns a paginated list of Permissions. Permissions define resource:action pairs for fine-grained access control. # Create PlatformConfiguration Source: https://developer.kodexa.ai/api-reference/platform-configuration/create-platformconfiguration /api-reference/api-docs.yaml post /api/platform-configurations Creates a new PlatformConfiguration. Platform configurations store platform-wide settings and feature flags. # Delete PlatformConfiguration Source: https://developer.kodexa.ai/api-reference/platform-configuration/delete-platformconfiguration /api-reference/api-docs.yaml delete /api/platform-configurations/{id} Permanently deletes the specified PlatformConfiguration. Platform configurations store platform-wide settings and feature flags. # Get PlatformConfiguration by ID Source: https://developer.kodexa.ai/api-reference/platform-configuration/get-platformconfiguration-by-id /api-reference/api-docs.yaml get /api/platform-configurations/{id} Retrieves a single PlatformConfiguration by its unique identifier. Platform configurations store platform-wide settings and feature flags. # Get PlatformConfiguration change sequence Source: https://developer.kodexa.ai/api-reference/platform-configuration/get-platformconfiguration-change-sequence /api-reference/api-docs.yaml get /api/platform-configurations/{id}/sequence Returns the current change sequence number for the specified PlatformConfiguration. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Platform Configuration Source: https://developer.kodexa.ai/api-reference/platform-configuration/list-platform-configuration /api-reference/api-docs.yaml get /api/platform-configurations Returns a paginated list of Platform Configuration. Platform configurations store platform-wide settings and feature flags. # Update PlatformConfiguration Source: https://developer.kodexa.ai/api-reference/platform-configuration/update-platformconfiguration /api-reference/api-docs.yaml put /api/platform-configurations/{id} Updates an existing PlatformConfiguration. Platform configurations store platform-wide settings and feature flags. # Get platform configuration Source: https://developer.kodexa.ai/api-reference/platform-overview/get-platform-configuration /api-reference/api-docs.yaml get /api/configuration Returns the public platform configuration including authentication settings, available features, and UI configuration. # Get platform overview Source: https://developer.kodexa.ai/api-reference/platform-overview/get-platform-overview /api-reference/api-docs.yaml get /api/overview Returns an overview of the Kodexa platform including version information, feature availability, and system status. # Get platform terms and conditions Source: https://developer.kodexa.ai/api-reference/platform-overview/get-platform-terms-and-conditions /api-reference/api-docs.yaml get /api/terms # Reset platform configuration Source: https://developer.kodexa.ai/api-reference/platform-overview/reset-platform-configuration /api-reference/api-docs.yaml put /api/configuration/reset Resets the platform configuration to defaults. Requires platform admin role. # Update platform configuration Source: https://developer.kodexa.ai/api-reference/platform-overview/update-platform-configuration /api-reference/api-docs.yaml put /api/configuration Updates the platform configuration. Requires platform admin role. # Create ProductGroup Source: https://developer.kodexa.ai/api-reference/product-groups/create-productgroup /api-reference/api-docs.yaml post /api/product-groups Creates a new ProductGroup. Product groups bundle related products together. # Delete ProductGroup Source: https://developer.kodexa.ai/api-reference/product-groups/delete-product-groups-id delete /api/product-groups/{id} Permanently deletes the specified ProductGroup. Product groups bundle related products together. # Delete ProductGroup Source: https://developer.kodexa.ai/api-reference/product-groups/delete-productgroup /api-reference/api-docs.yaml delete /api/product-groups/{id} Permanently deletes the specified ProductGroup. Product groups bundle related products together. # List Product Groups Source: https://developer.kodexa.ai/api-reference/product-groups/get-product-groups get /api/product-groups Returns a paginated list of Product Groups. Product groups bundle related products together. # Get ProductGroup by ID Source: https://developer.kodexa.ai/api-reference/product-groups/get-product-groups-id get /api/product-groups/{id} Retrieves a single ProductGroup by its unique identifier. Product groups bundle related products together. # Get ProductGroup change sequence Source: https://developer.kodexa.ai/api-reference/product-groups/get-product-groups-id-sequence get /api/product-groups/{id}/sequence Returns the current change sequence number for the specified ProductGroup. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get ProductGroup by ID Source: https://developer.kodexa.ai/api-reference/product-groups/get-productgroup-by-id /api-reference/api-docs.yaml get /api/product-groups/{id} Retrieves a single ProductGroup by its unique identifier. Product groups bundle related products together. # Get ProductGroup change sequence Source: https://developer.kodexa.ai/api-reference/product-groups/get-productgroup-change-sequence /api-reference/api-docs.yaml get /api/product-groups/{id}/sequence Returns the current change sequence number for the specified ProductGroup. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Product Groups Source: https://developer.kodexa.ai/api-reference/product-groups/list-product-groups /api-reference/api-docs.yaml get /api/product-groups Returns a paginated list of Product Groups. Product groups bundle related products together. # Create ProductGroup Source: https://developer.kodexa.ai/api-reference/product-groups/post-product-groups post /api/product-groups Creates a new ProductGroup. Product groups bundle related products together. # Update ProductGroup Source: https://developer.kodexa.ai/api-reference/product-groups/put-product-groups-id put /api/product-groups/{id} Updates an existing ProductGroup. Product groups bundle related products together. # Update ProductGroup Source: https://developer.kodexa.ai/api-reference/product-groups/update-productgroup /api-reference/api-docs.yaml put /api/product-groups/{id} Updates an existing ProductGroup. Product groups bundle related products together. # Create Products Source: https://developer.kodexa.ai/api-reference/product-overview/create-products /api-reference/api-docs.yaml post /api/products Creates a new Products. Products represent purchasable offerings on the Kodexa platform. # Delete Products Source: https://developer.kodexa.ai/api-reference/product-overview/delete-products /api-reference/api-docs.yaml delete /api/products/{id} Permanently deletes the specified Products. Products represent purchasable offerings on the Kodexa platform. # Get Products by ID Source: https://developer.kodexa.ai/api-reference/product-overview/get-products-by-id /api-reference/api-docs.yaml get /api/products/{id} Retrieves a single Products by its unique identifier. Products represent purchasable offerings on the Kodexa platform. # Get Products change sequence Source: https://developer.kodexa.ai/api-reference/product-overview/get-products-change-sequence /api-reference/api-docs.yaml get /api/products/{id}/sequence Returns the current change sequence number for the specified Products. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Product Overview Source: https://developer.kodexa.ai/api-reference/product-overview/list-product-overview /api-reference/api-docs.yaml get /api/products Returns a paginated list of Product Overview. Products represent purchasable offerings on the Kodexa platform. # Update Products Source: https://developer.kodexa.ai/api-reference/product-overview/update-products /api-reference/api-docs.yaml put /api/products/{id} Updates an existing Products. Products represent purchasable offerings on the Kodexa platform. # Create ProductSubscription Source: https://developer.kodexa.ai/api-reference/product-subscriptions/create-productsubscription /api-reference/api-docs.yaml post /api/product-subscriptions Creates a new ProductSubscription. Product subscriptions link organizations to purchased products. # Delete ProductSubscription Source: https://developer.kodexa.ai/api-reference/product-subscriptions/delete-product-subscriptions-id delete /api/product-subscriptions/{id} Permanently deletes the specified ProductSubscription. Product subscriptions link organizations to purchased products. # Delete ProductSubscription Source: https://developer.kodexa.ai/api-reference/product-subscriptions/delete-productsubscription /api-reference/api-docs.yaml delete /api/product-subscriptions/{id} Permanently deletes the specified ProductSubscription. Product subscriptions link organizations to purchased products. # List Product Subscriptions Source: https://developer.kodexa.ai/api-reference/product-subscriptions/get-product-subscriptions get /api/product-subscriptions Returns a paginated list of Product Subscriptions. Product subscriptions link organizations to purchased products. # Get ProductSubscription by ID Source: https://developer.kodexa.ai/api-reference/product-subscriptions/get-product-subscriptions-id get /api/product-subscriptions/{id} Retrieves a single ProductSubscription by its unique identifier. Product subscriptions link organizations to purchased products. # Get ProductSubscription change sequence Source: https://developer.kodexa.ai/api-reference/product-subscriptions/get-product-subscriptions-id-sequence get /api/product-subscriptions/{id}/sequence Returns the current change sequence number for the specified ProductSubscription. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get ProductSubscription by ID Source: https://developer.kodexa.ai/api-reference/product-subscriptions/get-productsubscription-by-id /api-reference/api-docs.yaml get /api/product-subscriptions/{id} Retrieves a single ProductSubscription by its unique identifier. Product subscriptions link organizations to purchased products. # Get ProductSubscription change sequence Source: https://developer.kodexa.ai/api-reference/product-subscriptions/get-productsubscription-change-sequence /api-reference/api-docs.yaml get /api/product-subscriptions/{id}/sequence Returns the current change sequence number for the specified ProductSubscription. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Product Subscriptions Source: https://developer.kodexa.ai/api-reference/product-subscriptions/list-product-subscriptions /api-reference/api-docs.yaml get /api/product-subscriptions Returns a paginated list of Product Subscriptions. Product subscriptions link organizations to purchased products. # Create ProductSubscription Source: https://developer.kodexa.ai/api-reference/product-subscriptions/post-product-subscriptions post /api/product-subscriptions Creates a new ProductSubscription. Product subscriptions link organizations to purchased products. # Update ProductSubscription Source: https://developer.kodexa.ai/api-reference/product-subscriptions/put-product-subscriptions-id put /api/product-subscriptions/{id} Updates an existing ProductSubscription. Product subscriptions link organizations to purchased products. # Update ProductSubscription Source: https://developer.kodexa.ai/api-reference/product-subscriptions/update-productsubscription /api-reference/api-docs.yaml put /api/product-subscriptions/{id} Updates an existing ProductSubscription. Product subscriptions link organizations to purchased products. # Create ProductProjectTemplate Source: https://developer.kodexa.ai/api-reference/productprojecttemplates/create-productprojecttemplate /api-reference/api-docs.yaml post /api/product-project-templates Creates a new ProductProjectTemplate. Product project templates define default project configurations for products. # Delete ProductProjectTemplate Source: https://developer.kodexa.ai/api-reference/productprojecttemplates/delete-productprojecttemplate /api-reference/api-docs.yaml delete /api/product-project-templates/{id} Permanently deletes the specified ProductProjectTemplate. Product project templates define default project configurations for products. # Get ProductProjectTemplate by ID Source: https://developer.kodexa.ai/api-reference/productprojecttemplates/get-productprojecttemplate-by-id /api-reference/api-docs.yaml get /api/product-project-templates/{id} Retrieves a single ProductProjectTemplate by its unique identifier. Product project templates define default project configurations for products. # Get ProductProjectTemplate change sequence Source: https://developer.kodexa.ai/api-reference/productprojecttemplates/get-productprojecttemplate-change-sequence /api-reference/api-docs.yaml get /api/product-project-templates/{id}/sequence Returns the current change sequence number for the specified ProductProjectTemplate. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List ProductProjectTemplates Source: https://developer.kodexa.ai/api-reference/productprojecttemplates/list-productprojecttemplates /api-reference/api-docs.yaml get /api/product-project-templates Returns a paginated list of ProductProjectTemplates. Product project templates define default project configurations for products. # Update ProductProjectTemplate Source: https://developer.kodexa.ai/api-reference/productprojecttemplates/update-productprojecttemplate /api-reference/api-docs.yaml put /api/product-project-templates/{id} Updates an existing ProductProjectTemplate. Product project templates define default project configurations for products. # Bind a resource to a project Source: https://developer.kodexa.ai/api-reference/project-resources/bind-a-resource-to-a-project /api-reference/api-docs.yaml post /api/project-resources/bind Creates an entry in the kdxa_project_resources binding table linking a project to a shared resource (task-template, task-status, etc.). # Create ProjectResource Source: https://developer.kodexa.ai/api-reference/project-resources/create-projectresource /api-reference/api-docs.yaml post /api/project-resources Creates a new ProjectResource. Project resources link entities to projects for access control scoping. # Delete ProjectResource Source: https://developer.kodexa.ai/api-reference/project-resources/delete-project-resources-id delete /api/project-resources/{id} Permanently deletes the specified ProjectResource. Project resources link entities to projects for access control scoping. # Delete ProjectResource Source: https://developer.kodexa.ai/api-reference/project-resources/delete-projectresource /api-reference/api-docs.yaml delete /api/project-resources/{id} Permanently deletes the specified ProjectResource. Project resources link entities to projects for access control scoping. # List Project Resources Source: https://developer.kodexa.ai/api-reference/project-resources/get-project-resources get /api/project-resources Returns a paginated list of Project Resources. Project resources link entities to projects for access control scoping. # Get ProjectResource by ID Source: https://developer.kodexa.ai/api-reference/project-resources/get-project-resources-id get /api/project-resources/{id} Retrieves a single ProjectResource by its unique identifier. Project resources link entities to projects for access control scoping. # Get ProjectResource change sequence Source: https://developer.kodexa.ai/api-reference/project-resources/get-project-resources-id-sequence get /api/project-resources/{id}/sequence Returns the current change sequence number for the specified ProjectResource. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get ProjectResource by ID Source: https://developer.kodexa.ai/api-reference/project-resources/get-projectresource-by-id /api-reference/api-docs.yaml get /api/project-resources/{id} Retrieves a single ProjectResource by its unique identifier. Project resources link entities to projects for access control scoping. # Get ProjectResource change sequence Source: https://developer.kodexa.ai/api-reference/project-resources/get-projectresource-change-sequence /api-reference/api-docs.yaml get /api/project-resources/{id}/sequence Returns the current change sequence number for the specified ProjectResource. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Project Resources Source: https://developer.kodexa.ai/api-reference/project-resources/list-project-resources /api-reference/api-docs.yaml get /api/project-resources Returns a paginated list of Project Resources. Project resources link entities to projects for access control scoping. # Create ProjectResource Source: https://developer.kodexa.ai/api-reference/project-resources/post-project-resources post /api/project-resources Creates a new ProjectResource. Project resources link entities to projects for access control scoping. # Update ProjectResource Source: https://developer.kodexa.ai/api-reference/project-resources/put-project-resources-id put /api/project-resources/{id} Updates an existing ProjectResource. Project resources link entities to projects for access control scoping. # Update ProjectResource Source: https://developer.kodexa.ai/api-reference/project-resources/update-projectresource /api-reference/api-docs.yaml put /api/project-resources/{id} Updates an existing ProjectResource. Project resources link entities to projects for access control scoping. # Create ProjectStatus Source: https://developer.kodexa.ai/api-reference/project-status/create-projectstatus /api-reference/api-docs.yaml post /api/project-statuses Creates a new ProjectStatus. Project statuses define named states for tracking project lifecycle. # Delete ProjectStatus Source: https://developer.kodexa.ai/api-reference/project-status/delete-projectstatus /api-reference/api-docs.yaml delete /api/project-statuses/{id} Permanently deletes the specified ProjectStatus. Project statuses define named states for tracking project lifecycle. # Get ProjectStatus by ID Source: https://developer.kodexa.ai/api-reference/project-status/get-projectstatus-by-id /api-reference/api-docs.yaml get /api/project-statuses/{id} Retrieves a single ProjectStatus by its unique identifier. Project statuses define named states for tracking project lifecycle. # Get ProjectStatus change sequence Source: https://developer.kodexa.ai/api-reference/project-status/get-projectstatus-change-sequence /api-reference/api-docs.yaml get /api/project-statuses/{id}/sequence Returns the current change sequence number for the specified ProjectStatus. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Project Status Source: https://developer.kodexa.ai/api-reference/project-status/list-project-status /api-reference/api-docs.yaml get /api/project-statuses Returns a paginated list of Project Status. Project statuses define named states for tracking project lifecycle. # Update ProjectStatus Source: https://developer.kodexa.ai/api-reference/project-status/update-projectstatus /api-reference/api-docs.yaml put /api/project-statuses/{id} Updates an existing ProjectStatus. Project statuses define named states for tracking project lifecycle. # Create ProjectTemplateMetadata Source: https://developer.kodexa.ai/api-reference/project-templates/create-projecttemplatemetadata /api-reference/api-docs.yaml post /api/project-templates Creates a new ProjectTemplateMetadata. Project templates define reusable project configurations. # Delete ProjectTemplateMetadata Source: https://developer.kodexa.ai/api-reference/project-templates/delete-project-templates-id delete /api/project-templates/{id} Permanently deletes the specified ProjectTemplateMetadata. Project templates define reusable project configurations. # Delete ProjectTemplateMetadata Source: https://developer.kodexa.ai/api-reference/project-templates/delete-projecttemplatemetadata /api-reference/api-docs.yaml delete /api/project-templates/{id} Permanently deletes the specified ProjectTemplateMetadata. Project templates define reusable project configurations. # List Project Templates Source: https://developer.kodexa.ai/api-reference/project-templates/get-project-templates get /api/project-templates Returns a paginated list of Project Templates. Project templates define reusable project configurations. # Get ProjectTemplateMetadata by ID Source: https://developer.kodexa.ai/api-reference/project-templates/get-project-templates-id get /api/project-templates/{id} Retrieves a single ProjectTemplateMetadata by its unique identifier. Project templates define reusable project configurations. # Get ProjectTemplateMetadata change sequence Source: https://developer.kodexa.ai/api-reference/project-templates/get-project-templates-id-sequence get /api/project-templates/{id}/sequence Returns the current change sequence number for the specified ProjectTemplateMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get ProjectTemplateMetadata by ID Source: https://developer.kodexa.ai/api-reference/project-templates/get-projecttemplatemetadata-by-id /api-reference/api-docs.yaml get /api/project-templates/{id} Retrieves a single ProjectTemplateMetadata by its unique identifier. Project templates define reusable project configurations. # Get ProjectTemplateMetadata change sequence Source: https://developer.kodexa.ai/api-reference/project-templates/get-projecttemplatemetadata-change-sequence /api-reference/api-docs.yaml get /api/project-templates/{id}/sequence Returns the current change sequence number for the specified ProjectTemplateMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Project Templates Source: https://developer.kodexa.ai/api-reference/project-templates/list-project-templates /api-reference/api-docs.yaml get /api/project-templates Returns a paginated list of Project Templates. Project templates define reusable project configurations. # List ProjectTemplateMetadata audit history Source: https://developer.kodexa.ai/api-reference/project-templates/list-projecttemplatemetadata-audit-history /api-reference/api-docs.yaml get /api/project-templates/{id}/audit Returns the paginated audit history for a single ProjectTemplateMetadata. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create ProjectTemplateMetadata Source: https://developer.kodexa.ai/api-reference/project-templates/post-project-templates post /api/project-templates Creates a new ProjectTemplateMetadata. Project templates define reusable project configurations. # Update ProjectTemplateMetadata Source: https://developer.kodexa.ai/api-reference/project-templates/put-project-templates-id put /api/project-templates/{id} Updates an existing ProjectTemplateMetadata. Project templates define reusable project configurations. # Update ProjectTemplateMetadata Source: https://developer.kodexa.ai/api-reference/project-templates/update-projecttemplatemetadata /api-reference/api-docs.yaml put /api/project-templates/{id} Updates an existing ProjectTemplateMetadata. Project templates define reusable project configurations. # Activate assistant Source: https://developer.kodexa.ai/api-reference/projects/activate-assistant /api-reference/api-docs.yaml put /api/projects/{id}/assistants/{assistantId}/activate Activates the specified assistant, enabling it to process documents. # Add assistant to project Source: https://developer.kodexa.ai/api-reference/projects/add-assistant-to-project /api-reference/api-docs.yaml post /api/projects/{id}/assistants Adds a new assistant to the specified project. # Add data form to project Source: https://developer.kodexa.ai/api-reference/projects/add-data-form-to-project /api-reference/api-docs.yaml post /api/projects/{id}/data-forms Associates a data form with the specified project. # Create Project Source: https://developer.kodexa.ai/api-reference/projects/create-project /api-reference/api-docs.yaml post /api/projects Creates a new Project. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Create project template request Source: https://developer.kodexa.ai/api-reference/projects/create-project-template-request /api-reference/api-docs.yaml get /api/projects/{id}/create-project-template-request Generates a project template request from the current project configuration, which can be used to create a reusable template. # Deactivate assistant Source: https://developer.kodexa.ai/api-reference/projects/deactivate-assistant /api-reference/api-docs.yaml put /api/projects/{id}/assistants/{assistantId}/deactivate Deactivates the specified assistant, stopping it from processing documents. # Delete Project Source: https://developer.kodexa.ai/api-reference/projects/delete-project /api-reference/api-docs.yaml delete /api/projects/{id} Permanently deletes the specified Project. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Delete Project Source: https://developer.kodexa.ai/api-reference/projects/delete-projects-id delete /api/projects/{id} Permanently deletes the specified Project. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Remove assistant from project Source: https://developer.kodexa.ai/api-reference/projects/delete-projects-id-assistants-assistantId delete /api/projects/{id}/assistants/{assistantId} Removes a specific assistant from the project. # Get assistant event options Source: https://developer.kodexa.ai/api-reference/projects/get-assistant-event-options /api-reference/api-docs.yaml get /api/projects/{id}/assistants/{assistantId}/events/{eventTypeName}/options Returns the available options for the specified event type on the assistant. # Get assistant stores Source: https://developer.kodexa.ai/api-reference/projects/get-assistant-stores /api-reference/api-docs.yaml get /api/projects/{id}/assistants/{assistantId}/stores Returns the document stores associated with the specified assistant in the project. # Get Project by ID Source: https://developer.kodexa.ai/api-reference/projects/get-project-by-id /api-reference/api-docs.yaml get /api/projects/{id} Retrieves a single Project by its unique identifier. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Get Project change sequence Source: https://developer.kodexa.ai/api-reference/projects/get-project-change-sequence /api-reference/api-docs.yaml get /api/projects/{id}/sequence Returns the current change sequence number for the specified Project. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get project dashboards Source: https://developer.kodexa.ai/api-reference/projects/get-project-dashboards /api-reference/api-docs.yaml get /api/projects/{id}/dashboards Returns the dashboards associated with the specified project. # Get project guidance Source: https://developer.kodexa.ai/api-reference/projects/get-project-guidance /api-reference/api-docs.yaml get /api/projects/{id}/guidance Returns the AI extraction guidance associated with the specified project. # Get project resources Source: https://developer.kodexa.ai/api-reference/projects/get-project-resources /api-reference/api-docs.yaml get /api/projects/{id}/resources Returns the resources (stores, taxonomies) associated with the specified project. # List Projects Source: https://developer.kodexa.ai/api-reference/projects/get-projects get /api/projects Returns a paginated list of Projects. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Get Project by ID Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id get /api/projects/{id} Retrieves a single Project by its unique identifier. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # List project assistants Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-assistants get /api/projects/{id}/assistants Returns a paginated list of assistants configured for the specified project. # Get assistant event options Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-assistants-assistantId-events-eventTypeName-options get /api/projects/{id}/assistants/{assistantId}/events/{eventTypeName}/options Returns the available options for the specified event type on the assistant. # List assistant executions Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-assistants-assistantId-executions get /api/projects/{id}/assistants/{assistantId}/executions Returns a paginated list of executions for the specified assistant. # Get assistant stores Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-assistants-assistantId-stores get /api/projects/{id}/assistants/{assistantId}/stores Returns the document stores associated with the specified assistant in the project. # Create project template request Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-create-project-template-request get /api/projects/{id}/create-project-template-request Generates a project template request from the current project configuration, which can be used to create a reusable template. # Get project dashboards Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-dashboards get /api/projects/{id}/dashboards Returns the dashboards associated with the specified project. # List project data forms Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-data-forms get /api/projects/{id}/data-forms Returns the data forms associated with the specified project. # Get project guidance Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-guidance get /api/projects/{id}/guidance Returns the AI extraction guidance associated with the specified project. # Get project resources Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-resources get /api/projects/{id}/resources Returns the resources (stores, taxonomies) associated with the specified project. # Get Project change sequence Source: https://developer.kodexa.ai/api-reference/projects/get-projects-id-sequence get /api/projects/{id}/sequence Returns the current change sequence number for the specified Project. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List assistant executions Source: https://developer.kodexa.ai/api-reference/projects/list-assistant-executions /api-reference/api-docs.yaml get /api/projects/{id}/assistants/{assistantId}/executions Returns a paginated list of executions for the specified assistant. # List project assistants Source: https://developer.kodexa.ai/api-reference/projects/list-project-assistants /api-reference/api-docs.yaml get /api/projects/{id}/assistants Returns a paginated list of assistants configured for the specified project. # List project data forms Source: https://developer.kodexa.ai/api-reference/projects/list-project-data-forms /api-reference/api-docs.yaml get /api/projects/{id}/data-forms Returns the data forms associated with the specified project. # List Projects Source: https://developer.kodexa.ai/api-reference/projects/list-projects /api-reference/api-docs.yaml get /api/projects Returns a paginated list of Projects. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Create Project Source: https://developer.kodexa.ai/api-reference/projects/post-projects post /api/projects Creates a new Project. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Add assistant to project Source: https://developer.kodexa.ai/api-reference/projects/post-projects-id-assistants post /api/projects/{id}/assistants Adds a new assistant to the specified project. # Send assistant event Source: https://developer.kodexa.ai/api-reference/projects/post-projects-id-assistants-assistantId-events post /api/projects/{id}/assistants/{assistantId}/events Sends an event to the specified assistant for processing. # Add data form to project Source: https://developer.kodexa.ai/api-reference/projects/post-projects-id-data-forms post /api/projects/{id}/data-forms Associates a data form with the specified project. # Update Project Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id put /api/projects/{id} Updates an existing Project. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Update project assistants Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id-assistants put /api/projects/{id}/assistants Updates all assistants for the specified project. # Update project assistant Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id-assistants-assistantId put /api/projects/{id}/assistants/{assistantId} Updates a specific assistant within the project. # Activate assistant Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id-assistants-assistantId-activate put /api/projects/{id}/assistants/{assistantId}/activate Activates the specified assistant, enabling it to process documents. # Deactivate assistant Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id-assistants-assistantId-deactivate put /api/projects/{id}/assistants/{assistantId}/deactivate Deactivates the specified assistant, stopping it from processing documents. # Trigger assistant schedule Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id-assistants-assistantId-schedule put /api/projects/{id}/assistants/{assistantId}/schedule Triggers a manual execution of the specified assistant's processing schedule. # Update assistant stores Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id-assistants-assistantId-stores put /api/projects/{id}/assistants/{assistantId}/stores Updates the document stores associated with the specified assistant in the project. # Update project resources Source: https://developer.kodexa.ai/api-reference/projects/put-projects-id-resources put /api/projects/{id}/resources Updates the resources associated with the specified project. # Remove assistant from project Source: https://developer.kodexa.ai/api-reference/projects/remove-assistant-from-project /api-reference/api-docs.yaml delete /api/projects/{id}/assistants/{assistantId} Removes a specific assistant from the project. # Send assistant event Source: https://developer.kodexa.ai/api-reference/projects/send-assistant-event /api-reference/api-docs.yaml post /api/projects/{id}/assistants/{assistantId}/events Sends an event to the specified assistant for processing. # Trigger assistant schedule Source: https://developer.kodexa.ai/api-reference/projects/trigger-assistant-schedule /api-reference/api-docs.yaml put /api/projects/{id}/assistants/{assistantId}/schedule Triggers a manual execution of the specified assistant's processing schedule. # Update assistant stores Source: https://developer.kodexa.ai/api-reference/projects/update-assistant-stores /api-reference/api-docs.yaml put /api/projects/{id}/assistants/{assistantId}/stores Updates the document stores associated with the specified assistant in the project. # Update Project Source: https://developer.kodexa.ai/api-reference/projects/update-project /api-reference/api-docs.yaml put /api/projects/{id} Updates an existing Project. Projects group related document stores, assistants, tasks, and processing pipelines within an organization. # Update project assistant Source: https://developer.kodexa.ai/api-reference/projects/update-project-assistant /api-reference/api-docs.yaml put /api/projects/{id}/assistants/{assistantId} Updates a specific assistant within the project. # Update project assistants Source: https://developer.kodexa.ai/api-reference/projects/update-project-assistants /api-reference/api-docs.yaml put /api/projects/{id}/assistants Updates all assistants for the specified project. # Update project resources Source: https://developer.kodexa.ai/api-reference/projects/update-project-resources /api-reference/api-docs.yaml put /api/projects/{id}/resources Updates the resources associated with the specified project. # Create Prompts Source: https://developer.kodexa.ai/api-reference/prompts/create-prompts /api-reference/api-docs.yaml post /api/prompts Creates a new Prompts. Prompts are templates used by AI assistants for document processing. # Delete Prompts Source: https://developer.kodexa.ai/api-reference/prompts/delete-prompts /api-reference/api-docs.yaml delete /api/prompts/{id} Permanently deletes the specified Prompts. Prompts are templates used by AI assistants for document processing. # Delete Prompts Source: https://developer.kodexa.ai/api-reference/prompts/delete-prompts-id delete /api/prompts/{id} Permanently deletes the specified Prompts. Prompts are templates used by AI assistants for document processing. # List Prompts Source: https://developer.kodexa.ai/api-reference/prompts/get-prompts get /api/prompts Returns a paginated list of Prompts. Prompts are templates used by AI assistants for document processing. # Get Prompts by ID Source: https://developer.kodexa.ai/api-reference/prompts/get-prompts-by-id /api-reference/api-docs.yaml get /api/prompts/{id} Retrieves a single Prompts by its unique identifier. Prompts are templates used by AI assistants for document processing. # Get Prompts change sequence Source: https://developer.kodexa.ai/api-reference/prompts/get-prompts-change-sequence /api-reference/api-docs.yaml get /api/prompts/{id}/sequence Returns the current change sequence number for the specified Prompts. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get Prompts by ID Source: https://developer.kodexa.ai/api-reference/prompts/get-prompts-id get /api/prompts/{id} Retrieves a single Prompts by its unique identifier. Prompts are templates used by AI assistants for document processing. # Get Prompts change sequence Source: https://developer.kodexa.ai/api-reference/prompts/get-prompts-id-sequence get /api/prompts/{id}/sequence Returns the current change sequence number for the specified Prompts. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Prompts Source: https://developer.kodexa.ai/api-reference/prompts/list-prompts /api-reference/api-docs.yaml get /api/prompts Returns a paginated list of Prompts. Prompts are templates used by AI assistants for document processing. # List Prompts audit history Source: https://developer.kodexa.ai/api-reference/prompts/list-prompts-audit-history /api-reference/api-docs.yaml get /api/prompts/{id}/audit Returns the paginated audit history for a single Prompts. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create Prompts Source: https://developer.kodexa.ai/api-reference/prompts/post-prompts post /api/prompts Creates a new Prompts. Prompts are templates used by AI assistants for document processing. # Update Prompts Source: https://developer.kodexa.ai/api-reference/prompts/put-prompts-id put /api/prompts/{id} Updates an existing Prompts. Prompts are templates used by AI assistants for document processing. # Update Prompts Source: https://developer.kodexa.ai/api-reference/prompts/update-prompts /api-reference/api-docs.yaml put /api/prompts/{id} Updates an existing Prompts. Prompts are templates used by AI assistants for document processing. # Create PurgeItem Source: https://developer.kodexa.ai/api-reference/purge-items/create-purgeitem /api-reference/api-docs.yaml post /api/purge-items Creates a new PurgeItem. Purge items mark content for scheduled cleanup and deletion. # Delete PurgeItem Source: https://developer.kodexa.ai/api-reference/purge-items/delete-purge-items-id delete /api/purge-items/{id} Permanently deletes the specified PurgeItem. Purge items mark content for scheduled cleanup and deletion. # Delete PurgeItem Source: https://developer.kodexa.ai/api-reference/purge-items/delete-purgeitem /api-reference/api-docs.yaml delete /api/purge-items/{id} Permanently deletes the specified PurgeItem. Purge items mark content for scheduled cleanup and deletion. # List Purge Items Source: https://developer.kodexa.ai/api-reference/purge-items/get-purge-items get /api/purge-items Returns a paginated list of Purge Items. Purge items mark content for scheduled cleanup and deletion. # Get PurgeItem by ID Source: https://developer.kodexa.ai/api-reference/purge-items/get-purge-items-id get /api/purge-items/{id} Retrieves a single PurgeItem by its unique identifier. Purge items mark content for scheduled cleanup and deletion. # Get PurgeItem change sequence Source: https://developer.kodexa.ai/api-reference/purge-items/get-purge-items-id-sequence get /api/purge-items/{id}/sequence Returns the current change sequence number for the specified PurgeItem. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get PurgeItem by ID Source: https://developer.kodexa.ai/api-reference/purge-items/get-purgeitem-by-id /api-reference/api-docs.yaml get /api/purge-items/{id} Retrieves a single PurgeItem by its unique identifier. Purge items mark content for scheduled cleanup and deletion. # Get PurgeItem change sequence Source: https://developer.kodexa.ai/api-reference/purge-items/get-purgeitem-change-sequence /api-reference/api-docs.yaml get /api/purge-items/{id}/sequence Returns the current change sequence number for the specified PurgeItem. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Purge Items Source: https://developer.kodexa.ai/api-reference/purge-items/list-purge-items /api-reference/api-docs.yaml get /api/purge-items Returns a paginated list of Purge Items. Purge items mark content for scheduled cleanup and deletion. # Create PurgeItem Source: https://developer.kodexa.ai/api-reference/purge-items/post-purge-items post /api/purge-items Creates a new PurgeItem. Purge items mark content for scheduled cleanup and deletion. # Update PurgeItem Source: https://developer.kodexa.ai/api-reference/purge-items/put-purge-items-id put /api/purge-items/{id} Updates an existing PurgeItem. Purge items mark content for scheduled cleanup and deletion. # Update PurgeItem Source: https://developer.kodexa.ai/api-reference/purge-items/update-purgeitem /api-reference/api-docs.yaml put /api/purge-items/{id} Updates an existing PurgeItem. Purge items mark content for scheduled cleanup and deletion. # Create RecentExecution Source: https://developer.kodexa.ai/api-reference/recent-executions/create-recentexecution /api-reference/api-docs.yaml post /api/recent-executions Creates a new RecentExecution. Recent executions record document processing executions for quick access. # Delete RecentExecution Source: https://developer.kodexa.ai/api-reference/recent-executions/delete-recent-executions-id delete /api/recent-executions/{id} Permanently deletes the specified RecentExecution. Recent executions record document processing executions for quick access. # Delete RecentExecution Source: https://developer.kodexa.ai/api-reference/recent-executions/delete-recentexecution /api-reference/api-docs.yaml delete /api/recent-executions/{id} Permanently deletes the specified RecentExecution. Recent executions record document processing executions for quick access. # List Recent Executions Source: https://developer.kodexa.ai/api-reference/recent-executions/get-recent-executions get /api/recent-executions Returns a paginated list of Recent Executions. Recent executions record document processing executions for quick access. # Get RecentExecution by ID Source: https://developer.kodexa.ai/api-reference/recent-executions/get-recent-executions-id get /api/recent-executions/{id} Retrieves a single RecentExecution by its unique identifier. Recent executions record document processing executions for quick access. # Get RecentExecution change sequence Source: https://developer.kodexa.ai/api-reference/recent-executions/get-recent-executions-id-sequence get /api/recent-executions/{id}/sequence Returns the current change sequence number for the specified RecentExecution. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get RecentExecution by ID Source: https://developer.kodexa.ai/api-reference/recent-executions/get-recentexecution-by-id /api-reference/api-docs.yaml get /api/recent-executions/{id} Retrieves a single RecentExecution by its unique identifier. Recent executions record document processing executions for quick access. # Get RecentExecution change sequence Source: https://developer.kodexa.ai/api-reference/recent-executions/get-recentexecution-change-sequence /api-reference/api-docs.yaml get /api/recent-executions/{id}/sequence Returns the current change sequence number for the specified RecentExecution. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Recent Executions Source: https://developer.kodexa.ai/api-reference/recent-executions/list-recent-executions /api-reference/api-docs.yaml get /api/recent-executions Returns a paginated list of Recent Executions. Recent executions record document processing executions for quick access. # Create RecentExecution Source: https://developer.kodexa.ai/api-reference/recent-executions/post-recent-executions post /api/recent-executions Creates a new RecentExecution. Recent executions record document processing executions for quick access. # Update RecentExecution Source: https://developer.kodexa.ai/api-reference/recent-executions/put-recent-executions-id put /api/recent-executions/{id} Updates an existing RecentExecution. Recent executions record document processing executions for quick access. # Update RecentExecution Source: https://developer.kodexa.ai/api-reference/recent-executions/update-recentexecution /api-reference/api-docs.yaml put /api/recent-executions/{id} Updates an existing RecentExecution. Recent executions record document processing executions for quick access. # List supported resource schemes Source: https://developer.kodexa.ai/api-reference/resource-resolution/list-supported-resource-schemes /api-reference/api-docs.yaml get /api/resolve/schemes Returns the list of supported resource URI schemes. Used by the CLI for discovery of available resource types. # Resolve resource URI Source: https://developer.kodexa.ai/api-reference/resource-resolution/resolve-resource-uri /api-reference/api-docs.yaml post /api/resolve Resolves a semantic resource URI (slug-based) into a concrete API path (ID-based). Supported URI format: `scheme://orgSlug/slug`. Supported schemes: module, model, model-runtime, store, document-store, data-store, taxonomy, data-definition, project-template, prompt, data-form. # Create RolePermission Source: https://developer.kodexa.ai/api-reference/role-permissions/create-rolepermission /api-reference/api-docs.yaml post /api/role-permissions Creates a new RolePermission. Role permissions link roles to their granted permissions. # Delete RolePermission Source: https://developer.kodexa.ai/api-reference/role-permissions/delete-role-permissions-id delete /api/role-permissions/{id} Permanently deletes the specified RolePermission. Role permissions link roles to their granted permissions. # Delete RolePermission Source: https://developer.kodexa.ai/api-reference/role-permissions/delete-rolepermission /api-reference/api-docs.yaml delete /api/role-permissions/{id} Permanently deletes the specified RolePermission. Role permissions link roles to their granted permissions. # List Role Permissions Source: https://developer.kodexa.ai/api-reference/role-permissions/get-role-permissions get /api/role-permissions Returns a paginated list of Role Permissions. Role permissions link roles to their granted permissions. # Get RolePermission by ID Source: https://developer.kodexa.ai/api-reference/role-permissions/get-role-permissions-id get /api/role-permissions/{id} Retrieves a single RolePermission by its unique identifier. Role permissions link roles to their granted permissions. # Get RolePermission change sequence Source: https://developer.kodexa.ai/api-reference/role-permissions/get-role-permissions-id-sequence get /api/role-permissions/{id}/sequence Returns the current change sequence number for the specified RolePermission. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get RolePermission by ID Source: https://developer.kodexa.ai/api-reference/role-permissions/get-rolepermission-by-id /api-reference/api-docs.yaml get /api/role-permissions/{id} Retrieves a single RolePermission by its unique identifier. Role permissions link roles to their granted permissions. # Get RolePermission change sequence Source: https://developer.kodexa.ai/api-reference/role-permissions/get-rolepermission-change-sequence /api-reference/api-docs.yaml get /api/role-permissions/{id}/sequence Returns the current change sequence number for the specified RolePermission. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Role Permissions Source: https://developer.kodexa.ai/api-reference/role-permissions/list-role-permissions /api-reference/api-docs.yaml get /api/role-permissions Returns a paginated list of Role Permissions. Role permissions link roles to their granted permissions. # Create RolePermission Source: https://developer.kodexa.ai/api-reference/role-permissions/post-role-permissions post /api/role-permissions Creates a new RolePermission. Role permissions link roles to their granted permissions. # Update RolePermission Source: https://developer.kodexa.ai/api-reference/role-permissions/put-role-permissions-id put /api/role-permissions/{id} Updates an existing RolePermission. Role permissions link roles to their granted permissions. # Update RolePermission Source: https://developer.kodexa.ai/api-reference/role-permissions/update-rolepermission /api-reference/api-docs.yaml put /api/role-permissions/{id} Updates an existing RolePermission. Role permissions link roles to their granted permissions. # Create AccessRole Source: https://developer.kodexa.ai/api-reference/roles/create-accessrole /api-reference/api-docs.yaml post /api/roles Creates a new AccessRole. Roles define sets of permissions that can be assigned to teams at org or project level. # Delete AccessRole Source: https://developer.kodexa.ai/api-reference/roles/delete-accessrole /api-reference/api-docs.yaml delete /api/roles/{id} Permanently deletes the specified AccessRole. Roles define sets of permissions that can be assigned to teams at org or project level. # Delete AccessRole Source: https://developer.kodexa.ai/api-reference/roles/delete-roles-id delete /api/roles/{id} Permanently deletes the specified AccessRole. Roles define sets of permissions that can be assigned to teams at org or project level. # Get AccessRole by ID Source: https://developer.kodexa.ai/api-reference/roles/get-accessrole-by-id /api-reference/api-docs.yaml get /api/roles/{id} Retrieves a single AccessRole by its unique identifier. Roles define sets of permissions that can be assigned to teams at org or project level. # Get AccessRole change sequence Source: https://developer.kodexa.ai/api-reference/roles/get-accessrole-change-sequence /api-reference/api-docs.yaml get /api/roles/{id}/sequence Returns the current change sequence number for the specified AccessRole. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Roles Source: https://developer.kodexa.ai/api-reference/roles/get-roles get /api/roles Returns a paginated list of Roles. Roles define sets of permissions that can be assigned to teams at org or project level. # Get AccessRole by ID Source: https://developer.kodexa.ai/api-reference/roles/get-roles-id get /api/roles/{id} Retrieves a single AccessRole by its unique identifier. Roles define sets of permissions that can be assigned to teams at org or project level. # Get AccessRole change sequence Source: https://developer.kodexa.ai/api-reference/roles/get-roles-id-sequence get /api/roles/{id}/sequence Returns the current change sequence number for the specified AccessRole. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Roles Source: https://developer.kodexa.ai/api-reference/roles/list-roles /api-reference/api-docs.yaml get /api/roles Returns a paginated list of Roles. Roles define sets of permissions that can be assigned to teams at org or project level. # Create AccessRole Source: https://developer.kodexa.ai/api-reference/roles/post-roles post /api/roles Creates a new AccessRole. Roles define sets of permissions that can be assigned to teams at org or project level. # Update AccessRole Source: https://developer.kodexa.ai/api-reference/roles/put-roles-id put /api/roles/{id} Updates an existing AccessRole. Roles define sets of permissions that can be assigned to teams at org or project level. # Update AccessRole Source: https://developer.kodexa.ai/api-reference/roles/update-accessrole /api-reference/api-docs.yaml put /api/roles/{id} Updates an existing AccessRole. Roles define sets of permissions that can be assigned to teams at org or project level. # Create ServiceBridgeMetadata Source: https://developer.kodexa.ai/api-reference/service-bridges/create-servicebridgemetadata /api-reference/api-docs.yaml post /api/service-bridges Creates a new ServiceBridgeMetadata. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Delete ServiceBridgeMetadata Source: https://developer.kodexa.ai/api-reference/service-bridges/delete-service-bridges-id delete /api/service-bridges/{id} Permanently deletes the specified ServiceBridgeMetadata. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Delete ServiceBridgeMetadata Source: https://developer.kodexa.ai/api-reference/service-bridges/delete-servicebridgemetadata /api-reference/api-docs.yaml delete /api/service-bridges/{id} Permanently deletes the specified ServiceBridgeMetadata. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # List Service Bridges Source: https://developer.kodexa.ai/api-reference/service-bridges/get-service-bridges get /api/service-bridges Returns a paginated list of Service Bridges. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Get ServiceBridgeMetadata by ID Source: https://developer.kodexa.ai/api-reference/service-bridges/get-service-bridges-id get /api/service-bridges/{id} Retrieves a single ServiceBridgeMetadata by its unique identifier. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Get ServiceBridgeMetadata change sequence Source: https://developer.kodexa.ai/api-reference/service-bridges/get-service-bridges-id-sequence get /api/service-bridges/{id}/sequence Returns the current change sequence number for the specified ServiceBridgeMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get ServiceBridgeMetadata by ID Source: https://developer.kodexa.ai/api-reference/service-bridges/get-servicebridgemetadata-by-id /api-reference/api-docs.yaml get /api/service-bridges/{id} Retrieves a single ServiceBridgeMetadata by its unique identifier. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Get ServiceBridgeMetadata change sequence Source: https://developer.kodexa.ai/api-reference/service-bridges/get-servicebridgemetadata-change-sequence /api-reference/api-docs.yaml get /api/service-bridges/{id}/sequence Returns the current change sequence number for the specified ServiceBridgeMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Service Bridges Source: https://developer.kodexa.ai/api-reference/service-bridges/list-service-bridges /api-reference/api-docs.yaml get /api/service-bridges Returns a paginated list of Service Bridges. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # List ServiceBridgeMetadata audit history Source: https://developer.kodexa.ai/api-reference/service-bridges/list-servicebridgemetadata-audit-history /api-reference/api-docs.yaml get /api/service-bridges/{id}/audit Returns the paginated audit history for a single ServiceBridgeMetadata. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create ServiceBridgeMetadata Source: https://developer.kodexa.ai/api-reference/service-bridges/post-service-bridges post /api/service-bridges Creates a new ServiceBridgeMetadata. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Proxy request through a service bridge endpoint Source: https://developer.kodexa.ai/api-reference/service-bridges/proxy-request-through-a-service-bridge-endpoint /api-reference/api-docs.yaml get /api/service-bridges/{id}/proxy/{endpointName} Forwards the incoming request to the configured external API behind the service bridge, applying the bridge's auth + headers. # Proxy request through a service bridge endpoint Source: https://developer.kodexa.ai/api-reference/service-bridges/proxy-request-through-a-service-bridge-endpoint-1 /api-reference/api-docs.yaml put /api/service-bridges/{id}/proxy/{endpointName} Forwards the incoming request to the configured external API behind the service bridge, applying the bridge's auth + headers. # Proxy request through a service bridge endpoint Source: https://developer.kodexa.ai/api-reference/service-bridges/proxy-request-through-a-service-bridge-endpoint-2 /api-reference/api-docs.yaml post /api/service-bridges/{id}/proxy/{endpointName} Forwards the incoming request to the configured external API behind the service bridge, applying the bridge's auth + headers. # Proxy request through a service bridge endpoint Source: https://developer.kodexa.ai/api-reference/service-bridges/proxy-request-through-a-service-bridge-endpoint-3 /api-reference/api-docs.yaml delete /api/service-bridges/{id}/proxy/{endpointName} Forwards the incoming request to the configured external API behind the service bridge, applying the bridge's auth + headers. # Proxy request through a service bridge endpoint Source: https://developer.kodexa.ai/api-reference/service-bridges/proxy-request-through-a-service-bridge-endpoint-4 /api-reference/api-docs.yaml patch /api/service-bridges/{id}/proxy/{endpointName} Forwards the incoming request to the configured external API behind the service bridge, applying the bridge's auth + headers. # Update ServiceBridgeMetadata Source: https://developer.kodexa.ai/api-reference/service-bridges/put-service-bridges-id put /api/service-bridges/{id} Updates an existing ServiceBridgeMetadata. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Update ServiceBridgeMetadata Source: https://developer.kodexa.ai/api-reference/service-bridges/update-servicebridgemetadata /api-reference/api-docs.yaml put /api/service-bridges/{id} Updates an existing ServiceBridgeMetadata. Service bridges define external API endpoints that kodexa-api proxies on behalf of users. # Create Session Source: https://developer.kodexa.ai/api-reference/sessions/create-session /api-reference/api-docs.yaml post /api/sessions Creates a new Session. Sessions represent processing sessions for document operations. # Delete Session Source: https://developer.kodexa.ai/api-reference/sessions/delete-session /api-reference/api-docs.yaml delete /api/sessions/{id} Permanently deletes the specified Session. Sessions represent processing sessions for document operations. # Delete Session Source: https://developer.kodexa.ai/api-reference/sessions/delete-sessions-id delete /api/sessions/{id} Permanently deletes the specified Session. Sessions represent processing sessions for document operations. # Get Session by ID Source: https://developer.kodexa.ai/api-reference/sessions/get-session-by-id /api-reference/api-docs.yaml get /api/sessions/{id} Retrieves a single Session by its unique identifier. Sessions represent processing sessions for document operations. # Get Session change sequence Source: https://developer.kodexa.ai/api-reference/sessions/get-session-change-sequence /api-reference/api-docs.yaml get /api/sessions/{id}/sequence Returns the current change sequence number for the specified Session. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Sessions Source: https://developer.kodexa.ai/api-reference/sessions/get-sessions get /api/sessions Returns a paginated list of Sessions. Sessions represent processing sessions for document operations. # Get Session by ID Source: https://developer.kodexa.ai/api-reference/sessions/get-sessions-id get /api/sessions/{id} Retrieves a single Session by its unique identifier. Sessions represent processing sessions for document operations. # Get Session change sequence Source: https://developer.kodexa.ai/api-reference/sessions/get-sessions-id-sequence get /api/sessions/{id}/sequence Returns the current change sequence number for the specified Session. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Sessions Source: https://developer.kodexa.ai/api-reference/sessions/list-sessions /api-reference/api-docs.yaml get /api/sessions Returns a paginated list of Sessions. Sessions represent processing sessions for document operations. # Create Session Source: https://developer.kodexa.ai/api-reference/sessions/post-sessions post /api/sessions Creates a new Session. Sessions represent processing sessions for document operations. # Update Session Source: https://developer.kodexa.ai/api-reference/sessions/put-sessions-id put /api/sessions/{id} Updates an existing Session. Sessions represent processing sessions for document operations. # Update Session Source: https://developer.kodexa.ai/api-reference/sessions/update-session /api-reference/api-docs.yaml put /api/sessions/{id} Updates an existing Session. Sessions represent processing sessions for document operations. # Create StepExecution Source: https://developer.kodexa.ai/api-reference/step-executions/create-stepexecution /api-reference/api-docs.yaml post /api/step-executions Creates a new StepExecution. Step executions record results of individual processing steps within execution slices. # Delete StepExecution Source: https://developer.kodexa.ai/api-reference/step-executions/delete-step-executions-id delete /api/step-executions/{id} Permanently deletes the specified StepExecution. Step executions record results of individual processing steps within execution slices. # Delete StepExecution Source: https://developer.kodexa.ai/api-reference/step-executions/delete-stepexecution /api-reference/api-docs.yaml delete /api/step-executions/{id} Permanently deletes the specified StepExecution. Step executions record results of individual processing steps within execution slices. # List Step Executions Source: https://developer.kodexa.ai/api-reference/step-executions/get-step-executions get /api/step-executions Returns a paginated list of Step Executions. Step executions record results of individual processing steps within execution slices. # Get StepExecution by ID Source: https://developer.kodexa.ai/api-reference/step-executions/get-step-executions-id get /api/step-executions/{id} Retrieves a single StepExecution by its unique identifier. Step executions record results of individual processing steps within execution slices. # Get StepExecution change sequence Source: https://developer.kodexa.ai/api-reference/step-executions/get-step-executions-id-sequence get /api/step-executions/{id}/sequence Returns the current change sequence number for the specified StepExecution. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get StepExecution by ID Source: https://developer.kodexa.ai/api-reference/step-executions/get-stepexecution-by-id /api-reference/api-docs.yaml get /api/step-executions/{id} Retrieves a single StepExecution by its unique identifier. Step executions record results of individual processing steps within execution slices. # Get StepExecution change sequence Source: https://developer.kodexa.ai/api-reference/step-executions/get-stepexecution-change-sequence /api-reference/api-docs.yaml get /api/step-executions/{id}/sequence Returns the current change sequence number for the specified StepExecution. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Step Executions Source: https://developer.kodexa.ai/api-reference/step-executions/list-step-executions /api-reference/api-docs.yaml get /api/step-executions Returns a paginated list of Step Executions. Step executions record results of individual processing steps within execution slices. # Create StepExecution Source: https://developer.kodexa.ai/api-reference/step-executions/post-step-executions post /api/step-executions Creates a new StepExecution. Step executions record results of individual processing steps within execution slices. # Update StepExecution Source: https://developer.kodexa.ai/api-reference/step-executions/put-step-executions-id put /api/step-executions/{id} Updates an existing StepExecution. Step executions record results of individual processing steps within execution slices. # Update StepExecution Source: https://developer.kodexa.ai/api-reference/step-executions/update-stepexecution /api-reference/api-docs.yaml put /api/step-executions/{id} Updates an existing StepExecution. Step executions record results of individual processing steps within execution slices. # Create TagMetadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/create-tagmetadata /api-reference/api-docs.yaml post /api/tag-metadata Creates a new TagMetadata. Tag metadata defines tag types for document labeling and categorization. # Delete TagMetadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/delete-tag-metadata-id delete /api/tag-metadata/{id} Permanently deletes the specified TagMetadata. Tag metadata defines tag types for document labeling and categorization. # Delete TagMetadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/delete-tagmetadata /api-reference/api-docs.yaml delete /api/tag-metadata/{id} Permanently deletes the specified TagMetadata. Tag metadata defines tag types for document labeling and categorization. # List Tag Metadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/get-tag-metadata get /api/tag-metadata Returns a paginated list of Tag Metadata. Tag metadata defines tag types for document labeling and categorization. # Get TagMetadata by ID Source: https://developer.kodexa.ai/api-reference/tag-metadata/get-tag-metadata-id get /api/tag-metadata/{id} Retrieves a single TagMetadata by its unique identifier. Tag metadata defines tag types for document labeling and categorization. # Get TagMetadata change sequence Source: https://developer.kodexa.ai/api-reference/tag-metadata/get-tag-metadata-id-sequence get /api/tag-metadata/{id}/sequence Returns the current change sequence number for the specified TagMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get TagMetadata by ID Source: https://developer.kodexa.ai/api-reference/tag-metadata/get-tagmetadata-by-id /api-reference/api-docs.yaml get /api/tag-metadata/{id} Retrieves a single TagMetadata by its unique identifier. Tag metadata defines tag types for document labeling and categorization. # Get TagMetadata change sequence Source: https://developer.kodexa.ai/api-reference/tag-metadata/get-tagmetadata-change-sequence /api-reference/api-docs.yaml get /api/tag-metadata/{id}/sequence Returns the current change sequence number for the specified TagMetadata. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Tag Metadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/list-tag-metadata /api-reference/api-docs.yaml get /api/tag-metadata Returns a paginated list of Tag Metadata. Tag metadata defines tag types for document labeling and categorization. # Create TagMetadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/post-tag-metadata post /api/tag-metadata Creates a new TagMetadata. Tag metadata defines tag types for document labeling and categorization. # Update TagMetadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/put-tag-metadata-id put /api/tag-metadata/{id} Updates an existing TagMetadata. Tag metadata defines tag types for document labeling and categorization. # Update TagMetadata Source: https://developer.kodexa.ai/api-reference/tag-metadata/update-tagmetadata /api-reference/api-docs.yaml put /api/tag-metadata/{id} Updates an existing TagMetadata. Tag metadata defines tag types for document labeling and categorization. # Add tasks to a task group Source: https://developer.kodexa.ai/api-reference/task-groups/add-tasks-to-a-task-group /api-reference/api-docs.yaml post /api/task-groups/{id}/tasks Adds the given tasks to the task group, stamping each task's task_group_id. If the group has an assignee, it cascades to the new tasks. # Assign a task group Source: https://developer.kodexa.ai/api-reference/task-groups/assign-a-task-group /api-reference/api-docs.yaml put /api/task-groups/{id}/assignee Sets the assignee for a task group and cascades the same assignee to every non-deleted member task atomically. # Clear the task-group assignee Source: https://developer.kodexa.ai/api-reference/task-groups/clear-the-task-group-assignee /api-reference/api-docs.yaml delete /api/task-groups/{id}/assignee Clears the assignee on the task group and cascades the null assignee to every member task. # Create a task group Source: https://developer.kodexa.ai/api-reference/task-groups/create-a-task-group /api-reference/api-docs.yaml post /api/task-groups Creates a task group within a project and optionally stamps an initial set of tasks with group membership. # Delete a task group Source: https://developer.kodexa.ai/api-reference/task-groups/delete-a-task-group /api-reference/api-docs.yaml delete /api/task-groups/{id} Soft-deletes a task group and ungroups its member tasks. Fails if any member task is locked. # Get TaskGroup by ID Source: https://developer.kodexa.ai/api-reference/task-groups/get-taskgroup-by-id /api-reference/api-docs.yaml get /api/task-groups/{id} Retrieves a single TaskGroup by its unique identifier. Task groups bundle multiple tasks into a single unit of assignment. # Get TaskGroup change sequence Source: https://developer.kodexa.ai/api-reference/task-groups/get-taskgroup-change-sequence /api-reference/api-docs.yaml get /api/task-groups/{id}/sequence Returns the current change sequence number for the specified TaskGroup. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List task-group history Source: https://developer.kodexa.ai/api-reference/task-groups/list-task-group-history /api-reference/api-docs.yaml get /api/task-groups/{id}/history Returns the audit trail of lifecycle events (created, assigned, status changed, completed, etc.) for the given task group. # List Task Groups Source: https://developer.kodexa.ai/api-reference/task-groups/list-task-groups /api-reference/api-docs.yaml get /api/task-groups Returns a paginated list of Task Groups. Task groups bundle multiple tasks into a single unit of assignment. # Remove a task from a task group Source: https://developer.kodexa.ai/api-reference/task-groups/remove-a-task-from-a-task-group /api-reference/api-docs.yaml delete /api/task-groups/{id}/tasks/{taskId} Removes a single task from the task group (clearing task_group_id on that task) and decrements the group's counters. # Update a task group Source: https://developer.kodexa.ai/api-reference/task-groups/update-a-task-group /api-reference/api-docs.yaml put /api/task-groups/{id} Updates the mutable fields of a task group. The assigneeId field is rejected here — use PUT /task-groups/{id}/assignee for assignment changes (which cascade to member tasks). # Create TaskStatus Source: https://developer.kodexa.ai/api-reference/task-statuses/create-taskstatus /api-reference/api-docs.yaml post /api/task-statuses Creates a new TaskStatus. Task statuses define named states for tracking task workflow progress. # Delete TaskStatus Source: https://developer.kodexa.ai/api-reference/task-statuses/delete-task-statuses-id delete /api/task-statuses/{id} Permanently deletes the specified TaskStatus. Task statuses define named states for tracking task workflow progress. # Delete TaskStatus Source: https://developer.kodexa.ai/api-reference/task-statuses/delete-taskstatus /api-reference/api-docs.yaml delete /api/task-statuses/{id} Permanently deletes the specified TaskStatus. Task statuses define named states for tracking task workflow progress. # List Task Statuses Source: https://developer.kodexa.ai/api-reference/task-statuses/get-task-statuses get /api/task-statuses Returns a paginated list of Task Statuses. Task statuses define named states for tracking task workflow progress. # Get TaskStatus by ID Source: https://developer.kodexa.ai/api-reference/task-statuses/get-task-statuses-id get /api/task-statuses/{id} Retrieves a single TaskStatus by its unique identifier. Task statuses define named states for tracking task workflow progress. # Get TaskStatus change sequence Source: https://developer.kodexa.ai/api-reference/task-statuses/get-task-statuses-id-sequence get /api/task-statuses/{id}/sequence Returns the current change sequence number for the specified TaskStatus. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get TaskStatus by ID Source: https://developer.kodexa.ai/api-reference/task-statuses/get-taskstatus-by-id /api-reference/api-docs.yaml get /api/task-statuses/{id} Retrieves a single TaskStatus by its unique identifier. Task statuses define named states for tracking task workflow progress. # Get TaskStatus change sequence Source: https://developer.kodexa.ai/api-reference/task-statuses/get-taskstatus-change-sequence /api-reference/api-docs.yaml get /api/task-statuses/{id}/sequence Returns the current change sequence number for the specified TaskStatus. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Task Statuses Source: https://developer.kodexa.ai/api-reference/task-statuses/list-task-statuses /api-reference/api-docs.yaml get /api/task-statuses Returns a paginated list of Task Statuses. Task statuses define named states for tracking task workflow progress. # List TaskStatus audit history Source: https://developer.kodexa.ai/api-reference/task-statuses/list-taskstatus-audit-history /api-reference/api-docs.yaml get /api/task-statuses/{id}/audit Returns the paginated audit history for a single TaskStatus. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # Create TaskStatus Source: https://developer.kodexa.ai/api-reference/task-statuses/post-task-statuses post /api/task-statuses Creates a new TaskStatus. Task statuses define named states for tracking task workflow progress. # Update TaskStatus Source: https://developer.kodexa.ai/api-reference/task-statuses/put-task-statuses-id put /api/task-statuses/{id} Updates an existing TaskStatus. Task statuses define named states for tracking task workflow progress. # Update TaskStatus Source: https://developer.kodexa.ai/api-reference/task-statuses/update-taskstatus /api-reference/api-docs.yaml put /api/task-statuses/{id} Updates an existing TaskStatus. Task statuses define named states for tracking task workflow progress. # Create TaskActivity Source: https://developer.kodexa.ai/api-reference/taskactivity/create-taskactivity /api-reference/api-docs.yaml post /api/task-activities Creates a new TaskActivity. Task activities record events in a task's history such as status changes. # Delete TaskActivity Source: https://developer.kodexa.ai/api-reference/taskactivity/delete-taskactivity /api-reference/api-docs.yaml delete /api/task-activities/{id} Permanently deletes the specified TaskActivity. Task activities record events in a task's history such as status changes. # Get TaskActivity by ID Source: https://developer.kodexa.ai/api-reference/taskactivity/get-taskactivity-by-id /api-reference/api-docs.yaml get /api/task-activities/{id} Retrieves a single TaskActivity by its unique identifier. Task activities record events in a task's history such as status changes. # Get TaskActivity change sequence Source: https://developer.kodexa.ai/api-reference/taskactivity/get-taskactivity-change-sequence /api-reference/api-docs.yaml get /api/task-activities/{id}/sequence Returns the current change sequence number for the specified TaskActivity. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List TaskActivity Source: https://developer.kodexa.ai/api-reference/taskactivity/list-taskactivity /api-reference/api-docs.yaml get /api/task-activities Returns a paginated list of TaskActivity. Task activities record events in a task's history such as status changes. # Update TaskActivity Source: https://developer.kodexa.ai/api-reference/taskactivity/update-taskactivity /api-reference/api-docs.yaml put /api/task-activities/{id} Updates an existing TaskActivity. Task activities record events in a task's history such as status changes. # Create TaskDocumentFamily Source: https://developer.kodexa.ai/api-reference/taskdocumentfamilies/create-taskdocumentfamily /api-reference/api-docs.yaml post /api/task-document-families Creates a new TaskDocumentFamily. Task document families associate document families with tasks for review. # Delete TaskDocumentFamily Source: https://developer.kodexa.ai/api-reference/taskdocumentfamilies/delete-taskdocumentfamily /api-reference/api-docs.yaml delete /api/task-document-families/{id} Permanently deletes the specified TaskDocumentFamily. Task document families associate document families with tasks for review. # Get TaskDocumentFamily by ID Source: https://developer.kodexa.ai/api-reference/taskdocumentfamilies/get-taskdocumentfamily-by-id /api-reference/api-docs.yaml get /api/task-document-families/{id} Retrieves a single TaskDocumentFamily by its unique identifier. Task document families associate document families with tasks for review. # Get TaskDocumentFamily change sequence Source: https://developer.kodexa.ai/api-reference/taskdocumentfamilies/get-taskdocumentfamily-change-sequence /api-reference/api-docs.yaml get /api/task-document-families/{id}/sequence Returns the current change sequence number for the specified TaskDocumentFamily. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List TaskDocumentFamilies Source: https://developer.kodexa.ai/api-reference/taskdocumentfamilies/list-taskdocumentfamilies /api-reference/api-docs.yaml get /api/task-document-families Returns a paginated list of TaskDocumentFamilies. Task document families associate document families with tasks for review. # Update TaskDocumentFamily Source: https://developer.kodexa.ai/api-reference/taskdocumentfamilies/update-taskdocumentfamily /api-reference/api-docs.yaml put /api/task-document-families/{id} Updates an existing TaskDocumentFamily. Task document families associate document families with tasks for review. # Create TaskGroupHistory Source: https://developer.kodexa.ai/api-reference/taskgrouphistory/create-taskgrouphistory /api-reference/api-docs.yaml post /api/task-group-history Creates a new TaskGroupHistory. Audit feed entries for task groups (created, assigned, status changed, completed, etc). # Delete TaskGroupHistory Source: https://developer.kodexa.ai/api-reference/taskgrouphistory/delete-taskgrouphistory /api-reference/api-docs.yaml delete /api/task-group-history/{id} Permanently deletes the specified TaskGroupHistory. Audit feed entries for task groups (created, assigned, status changed, completed, etc). # Get TaskGroupHistory by ID Source: https://developer.kodexa.ai/api-reference/taskgrouphistory/get-taskgrouphistory-by-id /api-reference/api-docs.yaml get /api/task-group-history/{id} Retrieves a single TaskGroupHistory by its unique identifier. Audit feed entries for task groups (created, assigned, status changed, completed, etc). # Get TaskGroupHistory change sequence Source: https://developer.kodexa.ai/api-reference/taskgrouphistory/get-taskgrouphistory-change-sequence /api-reference/api-docs.yaml get /api/task-group-history/{id}/sequence Returns the current change sequence number for the specified TaskGroupHistory. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List TaskGroupHistory Source: https://developer.kodexa.ai/api-reference/taskgrouphistory/list-taskgrouphistory /api-reference/api-docs.yaml get /api/task-group-history Returns a paginated list of TaskGroupHistory. Audit feed entries for task groups (created, assigned, status changed, completed, etc). # Update TaskGroupHistory Source: https://developer.kodexa.ai/api-reference/taskgrouphistory/update-taskgrouphistory /api-reference/api-docs.yaml put /api/task-group-history/{id} Updates an existing TaskGroupHistory. Audit feed entries for task groups (created, assigned, status changed, completed, etc). # Clear task assignee Source: https://developer.kodexa.ai/api-reference/tasks/clear-task-assignee /api-reference/api-docs.yaml delete /api/tasks/{id}/assignee Removes the assignee from the specified task. # Clear task status Source: https://developer.kodexa.ai/api-reference/tasks/clear-task-status /api-reference/api-docs.yaml delete /api/tasks/{id}/status Removes the status from the specified task, resetting it to unset. # Clear task team Source: https://developer.kodexa.ai/api-reference/tasks/clear-task-team /api-reference/api-docs.yaml delete /api/tasks/{id}/team Removes the team from the specified task. # Create Task Source: https://developer.kodexa.ai/api-reference/tasks/create-task /api-reference/api-docs.yaml post /api/tasks Creates a new Task. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Create task with request Source: https://developer.kodexa.ai/api-reference/tasks/create-task-with-request /api-reference/api-docs.yaml post /api/tasks/create-task-with-request Creates a new task with a full request body including document families, assignees, and other configuration. # Delete Task Source: https://developer.kodexa.ai/api-reference/tasks/delete-task /api-reference/api-docs.yaml delete /api/tasks/{id} Permanently deletes the specified Task. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Delete Task Source: https://developer.kodexa.ai/api-reference/tasks/delete-tasks-id delete /api/tasks/{id} Permanently deletes the specified Task. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Clear task assignee Source: https://developer.kodexa.ai/api-reference/tasks/delete-tasks-id-assignee delete /api/tasks/{id}/assignee Removes the assignee from the specified task. # Clear task status Source: https://developer.kodexa.ai/api-reference/tasks/delete-tasks-id-status delete /api/tasks/{id}/status Removes the status from the specified task, resetting it to unset. # Clear task team Source: https://developer.kodexa.ai/api-reference/tasks/delete-tasks-id-team delete /api/tasks/{id}/team Removes the team from the specified task. # Get Task by ID Source: https://developer.kodexa.ai/api-reference/tasks/get-task-by-id /api-reference/api-docs.yaml get /api/tasks/{id} Retrieves a single Task by its unique identifier. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Get Task change sequence Source: https://developer.kodexa.ai/api-reference/tasks/get-task-change-sequence /api-reference/api-docs.yaml get /api/tasks/{id}/sequence Returns the current change sequence number for the specified Task. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Tasks Source: https://developer.kodexa.ai/api-reference/tasks/get-tasks get /api/tasks Returns a paginated list of Tasks. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Get Task by ID Source: https://developer.kodexa.ai/api-reference/tasks/get-tasks-id get /api/tasks/{id} Retrieves a single Task by its unique identifier. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Get Task change sequence Source: https://developer.kodexa.ai/api-reference/tasks/get-tasks-id-sequence get /api/tasks/{id}/sequence Returns the current change sequence number for the specified Task. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Tasks Source: https://developer.kodexa.ai/api-reference/tasks/list-tasks /api-reference/api-docs.yaml get /api/tasks Returns a paginated list of Tasks. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Lock task Source: https://developer.kodexa.ai/api-reference/tasks/lock-task /api-reference/api-docs.yaml post /api/tasks/{id}/lock Locks a task to prevent editing or deletion. Also locks all associated document families. # Create Task Source: https://developer.kodexa.ai/api-reference/tasks/post-tasks post /api/tasks Creates a new Task. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Assign next available task Source: https://developer.kodexa.ai/api-reference/tasks/post-tasks-assign-next post /api/tasks/assign-next Atomically claims the next unassigned unit of work (task or group) in the given project for the current user. Both pools (groups + ungrouped tasks where status_type='OPEN') are merged and ranked by priority ASC then created_on ASC. The org's teamMatchingMode (strict|soft) and the caller's team memberships shape which candidates are eligible; platform admins are subject to the same routing as org members. projectId is required — cross-project / cross-org take-next is not supported. # Create task with request Source: https://developer.kodexa.ai/api-reference/tasks/post-tasks-create-task-with-request post /api/tasks/create-task-with-request Creates a new task with a full request body including document families, assignees, and other configuration. # Lock task Source: https://developer.kodexa.ai/api-reference/tasks/post-tasks-id-lock post /api/tasks/{id}/lock Locks a task to prevent editing or deletion. Also locks all associated document families. # Unlock task Source: https://developer.kodexa.ai/api-reference/tasks/post-tasks-id-unlock post /api/tasks/{id}/unlock Unlocks a task to allow editing and deletion. Also unlocks all associated document families. # Update Task Source: https://developer.kodexa.ai/api-reference/tasks/put-tasks-id put /api/tasks/{id} Updates an existing Task. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. ## Completing a task with a discarded save When the update changes `statusSlug` to a status of type `DONE`, the platform first checks the task's documents. If the most recent saved changes for one of them were discarded server-side and never redone, the update is refused with `409 Conflict` and no part of it is written. The message names the affected documents by path (or by ID when a document has no path): ```json theme={null} { "code": "CONFLICT", "message": "Cannot complete this task: the most recent saved changes for invoices/2026-08/inv-1042.pdf were not applied to the stored document (the save failed server-side). Reopen the document, redo the changes, and save again before completing." } ``` To clear the block, reopen each document the message names, redo the changes, and save again. A successful re-save — or any full-document save — releases it and the update succeeds. Only the latest save per document counts, so a failure that has already been redone and saved does not block completion. Updates that leave `statusSlug` unchanged, or move it to a status of any other type, are unaffected. The same check applies to [`PUT /api/tasks/{id}/status`](/api-reference/tasks/put-tasks-id-status) and to task updates sent through `POST /api/batch-update`. This endpoint also returns `409 Conflict` for a stale `changeSequence`; the two cases share the `CONFLICT` code and are distinguished by the message. # Update task assignee Source: https://developer.kodexa.ai/api-reference/tasks/put-tasks-id-assignee put /api/tasks/{id}/assignee Assigns a user to the specified task. # Update task status Source: https://developer.kodexa.ai/api-reference/tasks/put-tasks-id-status put /api/tasks/{id}/status Updates the status of the specified task. ## Completing a task with a discarded save When the requested `statusSlug` differs from the task's current status and resolves to a status of type `DONE`, the platform first checks the task's documents. If the most recent saved changes for one of them were discarded server-side and never redone, the transition is refused with `409 Conflict` and nothing is written. The message names the affected documents by path (or by ID when a document has no path): ```json theme={null} { "code": "CONFLICT", "message": "Cannot complete this task: the most recent saved changes for invoices/2026-08/inv-1042.pdf were not applied to the stored document (the save failed server-side). Reopen the document, redo the changes, and save again before completing." } ``` To clear the block, reopen each document the message names, redo the changes, and save again. A successful re-save — or any full-document save — releases it and the task completes normally. Only the latest save per document counts, so a failure that has already been redone and saved does not block completion. Transitions to statuses of any other type are unaffected. This endpoint also returns `409 Conflict` for a stale `changeSequence`; the two cases share the `CONFLICT` code and are distinguished by the message. # Update task team Source: https://developer.kodexa.ai/api-reference/tasks/put-tasks-id-team put /api/tasks/{id}/team Assigns a team to the specified task. # Take next available work in a project Source: https://developer.kodexa.ai/api-reference/tasks/take-next-available-work-in-a-project /api-reference/api-docs.yaml post /api/tasks/assign-next Atomically claims the next unassigned unit of work (task or group) in the given project for the current user. Both pools (groups + ungrouped tasks where status_type='OPEN') are merged and ranked by priority ASC then created_on ASC. The org's teamMatchingMode (strict|soft) and the caller's team memberships shape which candidates are eligible; platform admins are subject to the same routing as org members. projectId is required — cross-project / cross-org take-next is not supported. # Unlock task Source: https://developer.kodexa.ai/api-reference/tasks/unlock-task /api-reference/api-docs.yaml post /api/tasks/{id}/unlock Unlocks a task to allow editing and deletion. Also unlocks all associated document families. # Update Task Source: https://developer.kodexa.ai/api-reference/tasks/update-task /api-reference/api-docs.yaml put /api/tasks/{id} Updates an existing Task. Tasks represent units of work in document review workflows, with assignees, statuses, and due dates. # Update task assignee Source: https://developer.kodexa.ai/api-reference/tasks/update-task-assignee /api-reference/api-docs.yaml put /api/tasks/{id}/assignee Assigns a user to the specified task. # Update task status Source: https://developer.kodexa.ai/api-reference/tasks/update-task-status /api-reference/api-docs.yaml put /api/tasks/{id}/status Updates the status of the specified task. # Update task team Source: https://developer.kodexa.ai/api-reference/tasks/update-task-team /api-reference/api-docs.yaml put /api/tasks/{id}/team Assigns a team to the specified task. # Create TaskTemplate Source: https://developer.kodexa.ai/api-reference/tasktemplates/create-tasktemplate /api-reference/api-docs.yaml post /api/task-templates Creates a new TaskTemplate. Task templates define reusable task configurations with default values. # Delete TaskTemplate Source: https://developer.kodexa.ai/api-reference/tasktemplates/delete-tasktemplate /api-reference/api-docs.yaml delete /api/task-templates/{id} Permanently deletes the specified TaskTemplate. Task templates define reusable task configurations with default values. # Get TaskTemplate by ID Source: https://developer.kodexa.ai/api-reference/tasktemplates/get-tasktemplate-by-id /api-reference/api-docs.yaml get /api/task-templates/{id} Retrieves a single TaskTemplate by its unique identifier. Task templates define reusable task configurations with default values. # Get TaskTemplate change sequence Source: https://developer.kodexa.ai/api-reference/tasktemplates/get-tasktemplate-change-sequence /api-reference/api-docs.yaml get /api/task-templates/{id}/sequence Returns the current change sequence number for the specified TaskTemplate. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List TaskTemplate audit history Source: https://developer.kodexa.ai/api-reference/tasktemplates/list-tasktemplate-audit-history /api-reference/api-docs.yaml get /api/task-templates/{id}/audit Returns the paginated audit history for a single TaskTemplate. Snapshots (`beforeSnapshot`, `afterSnapshot`) are excluded by default — opt in with `?include=beforeSnapshot,afterSnapshot`. Standard query DSL applies (filter, sort, page, pageSize). The default sort is `createdOn:desc`. # List TaskTemplates Source: https://developer.kodexa.ai/api-reference/tasktemplates/list-tasktemplates /api-reference/api-docs.yaml get /api/task-templates Returns a paginated list of TaskTemplates. Task templates define reusable task configurations with default values. # Update TaskTemplate Source: https://developer.kodexa.ai/api-reference/tasktemplates/update-tasktemplate /api-reference/api-docs.yaml put /api/task-templates/{id} Updates an existing TaskTemplate. Task templates define reusable task configurations with default values. # Create TeamMember Source: https://developer.kodexa.ai/api-reference/team-members/create-teammember /api-reference/api-docs.yaml post /api/team-members Creates a new TeamMember. Team members represent users assigned to teams. # Delete TeamMember Source: https://developer.kodexa.ai/api-reference/team-members/delete-team-members-id delete /api/team-members/{id} Permanently deletes the specified TeamMember. Team members represent users assigned to teams. # Delete TeamMember Source: https://developer.kodexa.ai/api-reference/team-members/delete-teammember /api-reference/api-docs.yaml delete /api/team-members/{id} Permanently deletes the specified TeamMember. Team members represent users assigned to teams. # List Team Members Source: https://developer.kodexa.ai/api-reference/team-members/get-team-members get /api/team-members Returns a paginated list of Team Members. Team members represent users assigned to teams. # Get TeamMember by ID Source: https://developer.kodexa.ai/api-reference/team-members/get-team-members-id get /api/team-members/{id} Retrieves a single TeamMember by its unique identifier. Team members represent users assigned to teams. # Get TeamMember change sequence Source: https://developer.kodexa.ai/api-reference/team-members/get-team-members-id-sequence get /api/team-members/{id}/sequence Returns the current change sequence number for the specified TeamMember. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get TeamMember by ID Source: https://developer.kodexa.ai/api-reference/team-members/get-teammember-by-id /api-reference/api-docs.yaml get /api/team-members/{id} Retrieves a single TeamMember by its unique identifier. Team members represent users assigned to teams. # Get TeamMember change sequence Source: https://developer.kodexa.ai/api-reference/team-members/get-teammember-change-sequence /api-reference/api-docs.yaml get /api/team-members/{id}/sequence Returns the current change sequence number for the specified TeamMember. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Team Members Source: https://developer.kodexa.ai/api-reference/team-members/list-team-members /api-reference/api-docs.yaml get /api/team-members Returns a paginated list of Team Members. Team members represent users assigned to teams. # Create TeamMember Source: https://developer.kodexa.ai/api-reference/team-members/post-team-members post /api/team-members Creates a new TeamMember. Team members represent users assigned to teams. # Update TeamMember Source: https://developer.kodexa.ai/api-reference/team-members/put-team-members-id put /api/team-members/{id} Updates an existing TeamMember. Team members represent users assigned to teams. # Update TeamMember Source: https://developer.kodexa.ai/api-reference/team-members/update-teammember /api-reference/api-docs.yaml put /api/team-members/{id} Updates an existing TeamMember. Team members represent users assigned to teams. # Create TeamOrgAssignment Source: https://developer.kodexa.ai/api-reference/team-org-assignments/create-teamorgassignment /api-reference/api-docs.yaml post /api/team-org-assignments Creates a new TeamOrgAssignment. Team org assignments grant a team a role within an organization. # Delete TeamOrgAssignment Source: https://developer.kodexa.ai/api-reference/team-org-assignments/delete-team-org-assignments-id delete /api/team-org-assignments/{id} Permanently deletes the specified TeamOrgAssignment. Team org assignments grant a team a role within an organization. # Delete TeamOrgAssignment Source: https://developer.kodexa.ai/api-reference/team-org-assignments/delete-teamorgassignment /api-reference/api-docs.yaml delete /api/team-org-assignments/{id} Permanently deletes the specified TeamOrgAssignment. Team org assignments grant a team a role within an organization. # List Team Org Assignments Source: https://developer.kodexa.ai/api-reference/team-org-assignments/get-team-org-assignments get /api/team-org-assignments Returns a paginated list of Team Org Assignments. Team org assignments grant a team a role within an organization. # Get TeamOrgAssignment by ID Source: https://developer.kodexa.ai/api-reference/team-org-assignments/get-team-org-assignments-id get /api/team-org-assignments/{id} Retrieves a single TeamOrgAssignment by its unique identifier. Team org assignments grant a team a role within an organization. # Get TeamOrgAssignment change sequence Source: https://developer.kodexa.ai/api-reference/team-org-assignments/get-team-org-assignments-id-sequence get /api/team-org-assignments/{id}/sequence Returns the current change sequence number for the specified TeamOrgAssignment. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get TeamOrgAssignment by ID Source: https://developer.kodexa.ai/api-reference/team-org-assignments/get-teamorgassignment-by-id /api-reference/api-docs.yaml get /api/team-org-assignments/{id} Retrieves a single TeamOrgAssignment by its unique identifier. Team org assignments grant a team a role within an organization. # Get TeamOrgAssignment change sequence Source: https://developer.kodexa.ai/api-reference/team-org-assignments/get-teamorgassignment-change-sequence /api-reference/api-docs.yaml get /api/team-org-assignments/{id}/sequence Returns the current change sequence number for the specified TeamOrgAssignment. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Team Org Assignments Source: https://developer.kodexa.ai/api-reference/team-org-assignments/list-team-org-assignments /api-reference/api-docs.yaml get /api/team-org-assignments Returns a paginated list of Team Org Assignments. Team org assignments grant a team a role within an organization. # Create TeamOrgAssignment Source: https://developer.kodexa.ai/api-reference/team-org-assignments/post-team-org-assignments post /api/team-org-assignments Creates a new TeamOrgAssignment. Team org assignments grant a team a role within an organization. # Update TeamOrgAssignment Source: https://developer.kodexa.ai/api-reference/team-org-assignments/put-team-org-assignments-id put /api/team-org-assignments/{id} Updates an existing TeamOrgAssignment. Team org assignments grant a team a role within an organization. # Update TeamOrgAssignment Source: https://developer.kodexa.ai/api-reference/team-org-assignments/update-teamorgassignment /api-reference/api-docs.yaml put /api/team-org-assignments/{id} Updates an existing TeamOrgAssignment. Team org assignments grant a team a role within an organization. # Create TeamProjectAssignment Source: https://developer.kodexa.ai/api-reference/team-project-assignments/create-teamprojectassignment /api-reference/api-docs.yaml post /api/team-project-assignments Creates a new TeamProjectAssignment. Team project assignments grant a team a role within a project. # Delete TeamProjectAssignment Source: https://developer.kodexa.ai/api-reference/team-project-assignments/delete-team-project-assignments-id delete /api/team-project-assignments/{id} Permanently deletes the specified TeamProjectAssignment. Team project assignments grant a team a role within a project. # Delete TeamProjectAssignment Source: https://developer.kodexa.ai/api-reference/team-project-assignments/delete-teamprojectassignment /api-reference/api-docs.yaml delete /api/team-project-assignments/{id} Permanently deletes the specified TeamProjectAssignment. Team project assignments grant a team a role within a project. # List Team Project Assignments Source: https://developer.kodexa.ai/api-reference/team-project-assignments/get-team-project-assignments get /api/team-project-assignments Returns a paginated list of Team Project Assignments. Team project assignments grant a team a role within a project. # Get TeamProjectAssignment by ID Source: https://developer.kodexa.ai/api-reference/team-project-assignments/get-team-project-assignments-id get /api/team-project-assignments/{id} Retrieves a single TeamProjectAssignment by its unique identifier. Team project assignments grant a team a role within a project. # Get TeamProjectAssignment change sequence Source: https://developer.kodexa.ai/api-reference/team-project-assignments/get-team-project-assignments-id-sequence get /api/team-project-assignments/{id}/sequence Returns the current change sequence number for the specified TeamProjectAssignment. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get TeamProjectAssignment by ID Source: https://developer.kodexa.ai/api-reference/team-project-assignments/get-teamprojectassignment-by-id /api-reference/api-docs.yaml get /api/team-project-assignments/{id} Retrieves a single TeamProjectAssignment by its unique identifier. Team project assignments grant a team a role within a project. # Get TeamProjectAssignment change sequence Source: https://developer.kodexa.ai/api-reference/team-project-assignments/get-teamprojectassignment-change-sequence /api-reference/api-docs.yaml get /api/team-project-assignments/{id}/sequence Returns the current change sequence number for the specified TeamProjectAssignment. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Team Project Assignments Source: https://developer.kodexa.ai/api-reference/team-project-assignments/list-team-project-assignments /api-reference/api-docs.yaml get /api/team-project-assignments Returns a paginated list of Team Project Assignments. Team project assignments grant a team a role within a project. # Create TeamProjectAssignment Source: https://developer.kodexa.ai/api-reference/team-project-assignments/post-team-project-assignments post /api/team-project-assignments Creates a new TeamProjectAssignment. Team project assignments grant a team a role within a project. # Update TeamProjectAssignment Source: https://developer.kodexa.ai/api-reference/team-project-assignments/put-team-project-assignments-id put /api/team-project-assignments/{id} Updates an existing TeamProjectAssignment. Team project assignments grant a team a role within a project. # Update TeamProjectAssignment Source: https://developer.kodexa.ai/api-reference/team-project-assignments/update-teamprojectassignment /api-reference/api-docs.yaml put /api/team-project-assignments/{id} Updates an existing TeamProjectAssignment. Team project assignments grant a team a role within a project. # Create Team Source: https://developer.kodexa.ai/api-reference/teams/create-team /api-reference/api-docs.yaml post /api/teams Creates a new Team. Teams group users for collaborative access control within projects. # Delete Team Source: https://developer.kodexa.ai/api-reference/teams/delete-team /api-reference/api-docs.yaml delete /api/teams/{id} Permanently deletes the specified Team. Teams group users for collaborative access control within projects. # Delete Team Source: https://developer.kodexa.ai/api-reference/teams/delete-teams-id delete /api/teams/{id} Permanently deletes the specified Team. Teams group users for collaborative access control within projects. # Get Team by ID Source: https://developer.kodexa.ai/api-reference/teams/get-team-by-id /api-reference/api-docs.yaml get /api/teams/{id} Retrieves a single Team by its unique identifier. Teams group users for collaborative access control within projects. # Get Team change sequence Source: https://developer.kodexa.ai/api-reference/teams/get-team-change-sequence /api-reference/api-docs.yaml get /api/teams/{id}/sequence Returns the current change sequence number for the specified Team. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Teams Source: https://developer.kodexa.ai/api-reference/teams/get-teams get /api/teams Returns a paginated list of Teams. Teams group users for collaborative access control within projects. # Get Team by ID Source: https://developer.kodexa.ai/api-reference/teams/get-teams-id get /api/teams/{id} Retrieves a single Team by its unique identifier. Teams group users for collaborative access control within projects. # Get Team change sequence Source: https://developer.kodexa.ai/api-reference/teams/get-teams-id-sequence get /api/teams/{id}/sequence Returns the current change sequence number for the specified Team. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Invite a user to a team Source: https://developer.kodexa.ai/api-reference/teams/invite-a-user-to-a-team /api-reference/api-docs.yaml post /api/teams/{id}/invite Adds the given user to the team. If no user exists with the supplied email, a stub user is created so the invitation can complete later. # List Teams Source: https://developer.kodexa.ai/api-reference/teams/list-teams /api-reference/api-docs.yaml get /api/teams Returns a paginated list of Teams. Teams group users for collaborative access control within projects. # Create Team Source: https://developer.kodexa.ai/api-reference/teams/post-teams post /api/teams Creates a new Team. Teams group users for collaborative access control within projects. # Update Team Source: https://developer.kodexa.ai/api-reference/teams/put-teams-id put /api/teams/{id} Updates an existing Team. Teams group users for collaborative access control within projects. # Update Team Source: https://developer.kodexa.ai/api-reference/teams/update-team /api-reference/api-docs.yaml put /api/teams/{id} Updates an existing Team. Teams group users for collaborative access control within projects. # Create Trigger Source: https://developer.kodexa.ai/api-reference/triggers/create-trigger /api-reference/api-docs.yaml post /api/triggers Creates a new Trigger. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Delete Trigger Source: https://developer.kodexa.ai/api-reference/triggers/delete-trigger /api-reference/api-docs.yaml delete /api/triggers/{id} Permanently deletes the specified Trigger. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Delete Trigger Source: https://developer.kodexa.ai/api-reference/triggers/delete-triggers-id delete /api/triggers/{id} Permanently deletes the specified Trigger. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Disable a trigger Source: https://developer.kodexa.ai/api-reference/triggers/disable-a-trigger /api-reference/api-docs.yaml post /api/triggers/{id}/disable # Enable a trigger Source: https://developer.kodexa.ai/api-reference/triggers/enable-a-trigger /api-reference/api-docs.yaml post /api/triggers/{id}/enable # Get Trigger by ID Source: https://developer.kodexa.ai/api-reference/triggers/get-trigger-by-id /api-reference/api-docs.yaml get /api/triggers/{id} Retrieves a single Trigger by its unique identifier. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Get Trigger change sequence Source: https://developer.kodexa.ai/api-reference/triggers/get-trigger-change-sequence /api-reference/api-docs.yaml get /api/triggers/{id}/sequence Returns the current change sequence number for the specified Trigger. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Triggers Source: https://developer.kodexa.ai/api-reference/triggers/get-triggers get /api/triggers Returns a paginated list of Triggers. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Get Trigger by ID Source: https://developer.kodexa.ai/api-reference/triggers/get-triggers-id get /api/triggers/{id} Retrieves a single Trigger by its unique identifier. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Get Trigger change sequence Source: https://developer.kodexa.ai/api-reference/triggers/get-triggers-id-sequence get /api/triggers/{id}/sequence Returns the current change sequence number for the specified Trigger. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Triggers Source: https://developer.kodexa.ai/api-reference/triggers/list-triggers /api-reference/api-docs.yaml get /api/triggers Returns a paginated list of Triggers. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Create Trigger Source: https://developer.kodexa.ai/api-reference/triggers/post-triggers post /api/triggers Creates a new Trigger. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Disable a trigger Source: https://developer.kodexa.ai/api-reference/triggers/post-triggers-id-disable post /api/triggers/{id}/disable # Enable a trigger Source: https://developer.kodexa.ai/api-reference/triggers/post-triggers-id-enable post /api/triggers/{id}/enable # Update Trigger Source: https://developer.kodexa.ai/api-reference/triggers/put-triggers-id put /api/triggers/{id} Updates an existing Trigger. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Update Trigger Source: https://developer.kodexa.ai/api-reference/triggers/update-trigger /api-reference/api-docs.yaml put /api/triggers/{id} Updates an existing Trigger. Triggers are project-scoped automation rules that start an ActivityPlan when a matching event fires. # Create PlatformUser Source: https://developer.kodexa.ai/api-reference/users/create-platformuser /api-reference/api-docs.yaml post /api/users Creates a new PlatformUser. Platform users represent user accounts in Kodexa. # Delete PlatformUser Source: https://developer.kodexa.ai/api-reference/users/delete-platformuser /api-reference/api-docs.yaml delete /api/users/{id} Permanently deletes the specified PlatformUser. Platform users represent user accounts in Kodexa. # Delete PlatformUser Source: https://developer.kodexa.ai/api-reference/users/delete-users-id delete /api/users/{id} Permanently deletes the specified PlatformUser. Platform users represent user accounts in Kodexa. # Get PlatformUser by ID Source: https://developer.kodexa.ai/api-reference/users/get-platformuser-by-id /api-reference/api-docs.yaml get /api/users/{id} Retrieves a single PlatformUser by its unique identifier. Platform users represent user accounts in Kodexa. # Get PlatformUser change sequence Source: https://developer.kodexa.ai/api-reference/users/get-platformuser-change-sequence /api-reference/api-docs.yaml get /api/users/{id}/sequence Returns the current change sequence number for the specified PlatformUser. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get user organization access Source: https://developer.kodexa.ai/api-reference/users/get-user-organization-access /api-reference/api-docs.yaml get /api/users/{id}/teams Returns all team-based organization access for the specified user. # List Users Source: https://developer.kodexa.ai/api-reference/users/get-users get /api/users Returns a paginated list of Users. Platform users represent user accounts in Kodexa. # Get PlatformUser by ID Source: https://developer.kodexa.ai/api-reference/users/get-users-id get /api/users/{id} Retrieves a single PlatformUser by its unique identifier. Platform users represent user accounts in Kodexa. # Get PlatformUser change sequence Source: https://developer.kodexa.ai/api-reference/users/get-users-id-sequence get /api/users/{id}/sequence Returns the current change sequence number for the specified PlatformUser. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # Get user organization access Source: https://developer.kodexa.ai/api-reference/users/get-users-id-teams get /api/users/{id}/teams Returns all team-based organization access for the specified user. # List Users Source: https://developer.kodexa.ai/api-reference/users/list-users /api-reference/api-docs.yaml get /api/users Returns a paginated list of Users. Platform users represent user accounts in Kodexa. # Create PlatformUser Source: https://developer.kodexa.ai/api-reference/users/post-users post /api/users Creates a new PlatformUser. Platform users represent user accounts in Kodexa. # Update PlatformUser Source: https://developer.kodexa.ai/api-reference/users/put-users-id put /api/users/{id} Updates an existing PlatformUser. Platform users represent user accounts in Kodexa. # Update PlatformUser Source: https://developer.kodexa.ai/api-reference/users/update-platformuser /api-reference/api-docs.yaml put /api/users/{id} Updates an existing PlatformUser. Platform users represent user accounts in Kodexa. # Create Workspace Source: https://developer.kodexa.ai/api-reference/workspaces/create-workspace /api-reference/api-docs.yaml post /api/workspaces Creates a new Workspace. Workspaces organize project resources and provide a working context. # Delete Workspace Source: https://developer.kodexa.ai/api-reference/workspaces/delete-workspace /api-reference/api-docs.yaml delete /api/workspaces/{id} Permanently deletes the specified Workspace. Workspaces organize project resources and provide a working context. # Delete Workspace Source: https://developer.kodexa.ai/api-reference/workspaces/delete-workspaces-id delete /api/workspaces/{id} Permanently deletes the specified Workspace. Workspaces organize project resources and provide a working context. # Get Workspace by ID Source: https://developer.kodexa.ai/api-reference/workspaces/get-workspace-by-id /api-reference/api-docs.yaml get /api/workspaces/{id} Retrieves a single Workspace by its unique identifier. Workspaces organize project resources and provide a working context. # Get Workspace change sequence Source: https://developer.kodexa.ai/api-reference/workspaces/get-workspace-change-sequence /api-reference/api-docs.yaml get /api/workspaces/{id}/sequence Returns the current change sequence number for the specified Workspace. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Workspaces Source: https://developer.kodexa.ai/api-reference/workspaces/get-workspaces get /api/workspaces Returns a paginated list of Workspaces. Workspaces organize project resources and provide a working context. # Get Workspace by ID Source: https://developer.kodexa.ai/api-reference/workspaces/get-workspaces-id get /api/workspaces/{id} Retrieves a single Workspace by its unique identifier. Workspaces organize project resources and provide a working context. # Get Workspace change sequence Source: https://developer.kodexa.ai/api-reference/workspaces/get-workspaces-id-sequence get /api/workspaces/{id}/sequence Returns the current change sequence number for the specified Workspace. Use this for optimistic concurrency control — compare against the changeSequence field before updating. # List Workspaces Source: https://developer.kodexa.ai/api-reference/workspaces/list-workspaces /api-reference/api-docs.yaml get /api/workspaces Returns a paginated list of Workspaces. Workspaces organize project resources and provide a working context. # Create Workspace Source: https://developer.kodexa.ai/api-reference/workspaces/post-workspaces post /api/workspaces Creates a new Workspace. Workspaces organize project resources and provide a working context. # Update Workspace Source: https://developer.kodexa.ai/api-reference/workspaces/put-workspaces-id put /api/workspaces/{id} Updates an existing Workspace. Workspaces organize project resources and provide a working context. # Update Workspace Source: https://developer.kodexa.ai/api-reference/workspaces/update-workspace /api-reference/api-docs.yaml put /api/workspaces/{id} Updates an existing Workspace. Workspaces organize project resources and provide a working context. # Change Log Source: https://developer.kodexa.ai/change-log/index Release notes and platform updates for the Kodexa AI Platform, including new features, breaking changes, fixes, and migration guidance for each release. ### Release 2026.10 Rollup of every customer-facing change in the 2026.10 GA release. The headline items: the OpenAPI specification now describes every request and response precisely — separate create and update schemas, required and nullable fields, named enums, unique operation IDs — with the Python SDK regenerated to match; activity-plan agents and module executions can call the service bridges your organization has configured, and every bridge call is now fenced to public hosts; and the document engine closes a long list of gaps in validation exceptions, formula recalculation, selectors, and multi-view editing. `kdx sync push` now converges deletions inside the regions your files own, and module runtimes deliver finished work through brief orchestrator unavailability. Four changes are breaking — the OpenAPI contract, the regenerated Python SDK, the service-bridge egress fence, and `documentStatus` on document families — see the upgrade notes under API, SDK, and Service Bridges. **API:** * **The OpenAPI specification now describes every body precisely — separate create and update request schemas, required and nullable fields, named enums, and unique operation IDs (breaking).** The specification served at `GET /v3/api-docs` (and everything generated from it) now yields three schemas per resource: `` for responses, with `required` listing the fields the server always returns and `nullable` marking those that can be `null`; `CreateRequest` for `POST` bodies, with server-generated fields (`id`, `uuid`, `createdOn`, `updatedOn`, `changeSequence`) removed and `required` naming only what the server insists on (for example `TaskCreateRequest` requires just `projectId`); and `UpdateRequest` for `PUT` bodies, which requires nothing, keeps `changeSequence` for optimistic locking, and omits the ownership fields an update never writes. Enumerations are emitted as named components referenced by `$ref` — `TaskStatusType`, `ExecutionStatus`, `ExecutionStatusMessageType`, `SortDirection`, `AuditAction`, `ChatMessageRole`, `WebLlmModelSize`, and others — so every generator produces one stable type per enum instead of inventing names like `Type1` or `StatusType3`. Nullable object references are expressed as `allOf` + `nullable: true`, so generated types carry both facts. Fields that hold free-form JSON (for example the storage on `Workspace`, prompt metadata, and platform-event payloads) are typed as JSON objects rather than base64 strings. Operation IDs are unique: `PUT /api/tasks/{id}/status` is `setTaskStatus` and `GET /api/task-groups/{id}/history` is `listHistoryForTaskGroup`, which lets `updateTaskStatus` (`PUT /api/task-statuses/{id}`) and `listTaskGroupHistory` (`GET /api/task-group-history`) appear in generated clients — both were previously dropped by the name collision; `cancelExecution` is defined once, at `/api/executions/{executionId}/cancel`. The retired project-scoped task status type (`TODO`/`IN_PROGRESS`/`DONE`) is gone: `TaskStatusType` is `OPEN | IN_PROGRESS | DONE | BLOCKED | PENDING`, and project templates that still declare a task status with `statusType: TODO` are accepted and stored as `OPEN`. Request and response JSON on the wire is unchanged; only the specification and the clients generated from it change. **Upgrade note.** If you generate a client from `/v3/api-docs`, regenerate it against a 2026.10 server and expect compile-time changes: create calls take `CreateRequest` and update calls take `UpdateRequest` instead of the response type; response fields that were all optional are now typed as required where the server always sends them; enum types take their component names; JSON-blob fields become objects instead of strings; and two operations are renamed — `updateTaskStatus` → `setTaskStatus` for `PUT /api/tasks/{id}/status` and `listTaskGroupHistory` → `listHistoryForTaskGroup` for `GET /api/task-groups/{id}/history` (the old names now refer to the task-status and task-group-history entity endpoints). Hand-written HTTP integrations need no change. * **`documentStatus` on document families is now a full status object (breaking).** Document family reads — `GET /api/document-families`, `GET /api/document-families/{id}`, and the responses of the status `PUT`/`DELETE` endpoints — return `documentStatus` as the `DocumentStatus` object (`id`, `status`, and its other fields) instead of always `null`, and saved filters such as `documentStatus.status:'Reviewed'` resolve against it. In the Studio document grids the Status column now shows each document's status and redraws as soon as you pick a new one. The generic `PUT /api/document-families/{id}` also accepts `documentStatus` as an object under the standard update permission. **Upgrade note.** In the OpenAPI schema, `documentStatus` on `DocumentFamily` and on its create/update request bodies changes from `type: string` to a reference to the `DocumentStatus` schema. Regenerate typed API clients and update any code that declared the field as a string; read the status through `documentStatus.status` (or `documentStatus.id`) rather than treating the field itself as the value. * **Filter task groups by their member tasks.** `GET /api/task-groups` now accepts a `memberTask` relationship in `filter`, so you can select groups by properties of the tasks they contain — for example `memberTask.statusSlug in ['reviewed']`, or `not (memberTask.updatedOn > '2026-08-01T00:00:00Z')` to find groups with no recent task activity. A group matches when at least one of its non-deleted tasks satisfies the condition. This is additive and opt-in; existing filters and responses are unchanged. * **`POST /api/batch-update` reports the real outcome of every write.** A batch save completes when only optional bookkeeping fails, and when a write that matters fails — the task update, task-group maintenance, labels, document-family deletes, or the status auto-lock — the response carries that write's own error and reason rather than a generic commit failure. A save that reports success has been stored. * **`taskId` removed from the Activity schema.** The `Activity`, `ActivityCreateRequest`, and `ActivityUpdateRequest` schemas no longer include `taskId`; the field was never populated by the server, so no data is lost, and the generated Python SDK and TypeScript models drop it accordingly. The link between a task and the activity that created it is the task's `createdByActivityId`. **SDK:** * **Python SDK models regenerated against the new contract — request models, spec-named enums, and a slimmer `Task` (breaking).** `kodexa_document.model._generated` now includes a `CreateRequest` and `UpdateRequest` model for every resource (154 new models) alongside the response models, so create and update payloads can be built from a typed model that contains only the fields the server accepts. Enumeration classes carry the names from the API specification instead of generator-invented ones: `Status` → `ExecutionStatus` (also the top-level export, `from kodexa_document.model import ExecutionStatus`), `StatusType4` → `TaskStatusType`, `Type6` → `TaskActivityType`, `Direction` → `SortDirection`, `Action` → `AuditAction`, `ActorType` → `DocumentActorType`, `Cardinality` → `TaxonCardinality`, `ValuePath` → `TaxonValuePath`, `MetadataValue` → `TaxonMetadataValue`, `AnalyticsDatasetFieldType` → `AnalyticsFieldType`, `ModelType` → `WebLlmModelSize`, `Outcome` → `TaskSignalOutcome`, `State` → `SessionState`, `StepType` → `PipelineStepType`; the retired `StatusType3` (`TODO`/`IN_PROGRESS`/`DONE`) and the duplicate `TaskstatusTaskStatus` model are removed. `Task` now carries `status_slug` only — resolve the label, color, and `status_type` through the organization's task statuses (`TaskStatus`) rather than an embedded object. **Upgrade note.** Update imports that reference the old enum names listed above (`kodexa_document.model.Status` is now `ExecutionStatus`). Code that read `task.status` or `task.status_id` must switch to `task.status_slug` and look the status up in the organization's task statuses; `TaskEndpoint.create_with_request()` ignores an embedded status object and sends `statusSlug`. Values on the wire are unchanged — only the Python type names and the `Task` model's fields differ. * **Python SDK attribute writes are complete and type-safe.** Setting a date attribute from the Python SDK or an agent tool now persists — previously the value was dropped while the call reported success — and date strings are parsed flexibly: `2023-06-30`, month-name and US numeric forms as well as RFC 3339. Updating a boolean or date attribute no longer raises `DocumentError` after the value was written, and only the field that actually changed triggers recalculation. `Document.batch_transaction()` now persists `value`, `tagId`, `dataFeatures`, confidence and timestamps on attributes it creates, keeps `selectionOptions` on data objects it creates, and recalculates formulas for attributes it creates or updates — the same behavior as the direct accessors. Closing a document also releases its formula, validation, and conditional-format caches, so long-running processes that open many documents no longer grow memory per document. **Service Bridges:** * **Activity-plan `AGENT` steps and module executions can call service bridges.** An agent running as an `AGENT` step gets two tools, `list_service_bridges` and `call_service_bridge` (`bridge` and `endpoint_name` required, optional `query` and `body`; returns `{status, ok, body, truncated}`), so any external HTTP API your organization has configured as a service bridge — a search provider, an enrichment service, an internal system — becomes something the agent can use, with the credential injected server-side and never entering the agent's container. Access is opt-in and fails closed: a bridge is callable by agents only when its new `agentCallable` flag is on (the **Callable by agents** toggle on the bridge's General tab, or `agentCallable: true` in the bridge YAML) **and** the bridge is bound to the agent's project; agents see only bridges that satisfy both. The same two checks admit module and task executions running as a project's assistant, so a module can call the bridges bound to its own project through the bridge proxy; any call that cannot be resolved to a project is denied. Agents also receive document tools for the document families they were dispatched for, and any document an agent creates during the activity appears on the review task the activity ends in. * **Activity plans containing `AGENT` steps deploy through `kdx sync push`.** `AGENT` is accepted by server-side plan validation, so a plan carrying an agent step syncs like any other plan instead of being rejected as an unknown step type. * **Service bridge calls are fenced to public hosts and refuse credential configuration that cannot work (breaking).** Service bridge calls made through the bridge proxy — from data forms, `BRIDGE_CALL` steps and agents — go through the platform's egress guard: targets that resolve to loopback, private, link-local, or cloud-metadata addresses are refused, and each redirect hop is re-checked rather than followed blindly. A refused call from a data form or the bridge proxy returns `400` with `service bridge egress denied` and the reason; a refused `BRIDGE_CALL` step fails with the same reason. A header configured with `secretRef` is now rejected with a `400` naming the header instead of being sent upstream with an empty value; `${secrets.NAME}` interpolation in the header `value` on `defaultHeaders` is the supported way to inject a secret. A call naming an endpoint that does not exist on a bridge now fails with an error listing the bridge's available endpoint names, instead of a bare "not found" — and a script or step that names a wrong endpoint no longer falls through to the bridge's first endpoint. An omitted endpoint name still selects the sole endpoint of a single-endpoint bridge. **Upgrade note.** Audit bridges whose `baseUrl` points at a private or in-cluster host — those calls now fail with `service bridge egress denied`. Self-hosted deployments that must reach an internal mock or test service can set `BRIDGE_EGRESS_PRIVATE_HOST_ALLOWLIST` (a comma-separated list of exact hostnames) in the environment of both the API and the orchestrator; matching is exact and case-insensitive with no wildcards or CIDR ranges, DNS pinning and redirect checks still apply, and an active allowlist is logged as a warning at startup — it is intended for development and test environments only. Move any `headers[].secretRef` to `value: "${secrets.NAME}"` under `defaultHeaders`. Confirm that scripts and forms reference an endpoint `name` that exists on the bridge — a misspelled name is now an error rather than a call to the first endpoint. * **Service bridge required-field checks now recognize parameters sent in the query string.** When an endpoint declares `required` fields in its `requestSchema`, the proxy (`/api/service-bridges/{id}/proxy/{endpointName}`) returns an empty `[]` result without calling upstream while any of them is missing — the behaviour that keeps a dependent dropdown showing no options until its prerequisite field is filled. That check now treats a required field as supplied when it appears in either the JSON body or the query string, so GET-style endpoints that carry their parameters in the URL — the usual shape for search and lookup APIs — reach the upstream service instead of quietly returning an empty result. Presence is what counts on both sides: an empty string, `0`, or `false` still satisfies the field. * **Edits in the service bridge editor's YAML tab now save.** Changes typed in the YAML tab reach the bridge on save — including when you switch tabs or close the editor mid-edit — and the toolbar spells out that editing here replaces the whole bridge definition, including fields on the other tabs. YAML that does not parse, or parses to something other than a bridge object, is held back with an explanation instead of committing. If the bridge changes underneath you while you have uncommitted YAML (for example, an agent updates a draft), your text is kept and a banner tells you it is now based on the older version. **Document:** * **Validation rules can read parent values with `{../…}`.** A validation rule or condition that references a parent or grandparent attribute — `{../invoice_number}`, `{../../customer_name}` — is evaluated during extraction, during recalculation after an edit, and when a data definition is saved. A reference that keeps walking up past the top of the data — a second `..` applied to an object that has no parent — is reported as an evaluation error rather than resolving to nothing. Documents whose data carries a circular parent link open and save normally. * **On Studio-built data definitions, a corrected value closes its validation exception on the first correction.** Where an element's internal name differs from its external name — the shape Studio produces — correcting a value that raised a validation exception closes that exception on the first correction, whichever evaluation path opened it. * **Exception flags from validation rules stay open until the rule passes.** A validation rule authored with an empty `exceptionId` is identified by its taxon path and rule name, so every rule on a taxon carries its own flag: a flagged document stays flagged across data-definition saves and document refreshes until the underlying value is fixed. Deleting a flagged field in the review form removes that field's exception along with it. Documents carrying flags from such rules are corrected on their next refresh — no reprocessing needed. * **Overridable validation rules can be overridden in the review workspace, and exception details persist through every write.** A validation rule with `overridable: true` in the data definition now produces exceptions that carry the Override action in the browser, on documents already processed — no reprocessing or redeploy needed — and a rule's `supportArticleId` is carried onto its exceptions the same way. Overriding an exception keeps it on screen in a muted "Overridden by user" state, and the override holds across later validation passes while the rule still fails; only exceptions the platform itself closed are reopened when their rule fails again. Exceptions written by extraction, recalculation, or the browser now retain their `overridable` flag, linked support article, and evaluation-errored status, and previously processed documents pick these values up silently the first time they are opened. Separately, editing or moving a data object or attribute now preserves its original creation timestamp instead of blanking it, so creation times read from exported document databases are reliable. * **Data exceptions keep their configuration-error state through re-validation and save.** When a validation rule has a configuration error, its open exceptions carry that state — the error banner, whether the exception can be overridden, its details, and its support-article link. Re-saving the data definition now refreshes that state on the existing exceptions (a rule whose error is fixed clears the banner and details), and the state is now part of the document's saved change history, so it survives reload and shows identically for every user rather than only in the session that produced it. Existing documents pick up the new fields automatically; no action required. * **Removing a validation rule now takes effect when a document is reloaded.** Reloading a document applies the current data definition in full: added and changed rules are evaluated on load, and open exceptions raised by a rule that has since been removed from its taxon are now closed on reload, with the close recorded in the document's change history — no reprocessing needed. Removing an entire taxonomy, or removing every rule from a taxon, still requires reprocessing to clear those exceptions. * **Saving a data definition only closes the validation-rule flags it owns.** When a data definition is created, updated, or deleted, the engine re-evaluates validation-rule exceptions and closes only the rule-based flags that no longer apply. Data-type conversion errors, formula and content errors, extraction-time flags, and selection-validation flags stay open until their own source clears them or a user closes them — a data-type conversion error clears when the value is re-extracted or a reviewer closes it, not on a definition save. * **Deleting a data object removes its validation exceptions with it.** When you delete a row, the exceptions raised on it are removed in the same change, so they no longer linger on the document or appear in the save as flags pointing at an object that no longer exists. * **Edits made in a popped-out document window are saved.** When a document is open in a popped-out sidecar alongside the main workspace, adding, editing, moving, copying, or deleting data objects, values, and notes from the popped-out window is written to the document that saves, so the change survives reload and appears in the change history, and the main workspace catches up within a moment. A write the platform refuses is reported once with a clear notification (for example "The value could not be deleted — it has not been removed") rather than appearing to succeed, and a partially applied multi-row move or copy says which part applied. Deleting a row succeeds even when re-validation raised exceptions on it in the same change, deleting a value by its identifier reaches the stored document on every path, and tag highlights load reliably when a document opens. * **A save containing changes that can never apply keeps everything else.** When some operations in a save reference a parent or object the document no longer has, the remaining operations are applied and the delta is recorded with the new `APPLIED_PARTIAL` state on `GET /api/linked-deltas`, with `errorMessage` summarizing what was dropped. A save in which nothing could apply is still marked `FAILED`. * **A failed server-side save leaves the document exactly as it was.** When a processing step's write to a document is abandoned partway — an error during a large rewrite, for example — the document reverts cleanly to its previous contents and passes integrity checks, with no reprocessing needed. Recovery from a hard crash in the middle of a write is unchanged: reprocess the document. * **Highlight adjustments save with their content links intact.** Moving or resizing a tag's highlight during a review removes and re-adds the link between the tag and its text; the saved change set now records the final state of each tag-to-content link, so a tag re-linked to the same text keeps its highlight after save and a removed link stays removed — including when another tag was removed in the same session. Tags removed in a session no longer leave dangling references in the saved document. * **Save enables for label removals and task status or priority changes.** Clearing selected labels on a document, and changing Status or Priority in the Task Metadata panel, mark the document as having unsaved changes, so Save is available and the change is included in the next save. * **Formula values corrected when a document opens are saved back to the document.** Opening a document whose formula fields are stale — inputs changed since the value was last calculated, or a value stored under the wrong type marker — recalculates them for display and writes those corrections to the stored document in the background, so exports, downstream steps, and other viewers see the same values without anyone pressing save. Corrections are written only from the browser tab that owns the document. A locked task or a locked document family is corrected too — only the calculated values are written and the task stays locked — so a signed-off task's export matches what reviewers see. Corrections are recorded as automatic corrections rather than reviewer edits, and anything you type while the document is opening is saved as your own edit. If the background save does not go through you see a notification, and the corrections are recomputed the next time the document opens. * **Recalculate All, data-definition saves, and document refreshes recompute formulas on Studio-built data definitions.** The batch formula passes — Recalculate All, saving a data definition, and document refresh — now recompute every formula field, including on data definitions created in Studio, where an element's internal name differs from its external name. Expect the first definition save on a document that holds stale formula values to update them. * **Bulk move and copy of data objects are faster, and bulk copy is all-or-nothing.** Copying many data objects at once from a grid's bulk actions either lands every object or none of them, and a failure is reported instead of leaving a partial result that reported success. Large bulk moves and copies complete in a fraction of the time they used to — a 48-object copy finishes in well under a second — and the grid shows the moved or copied objects as soon as the engine finishes instead of re-downloading the whole document. A second bulk action started right after one completes now runs instead of being ignored. * **Selector edge cases evaluate as written.** A positional predicate selects the nth match (`//line[0]` is the first — indexes are 0-based); every condition in an `and` is applied; a comparison against `false` such as `[hasTag('x') = false]` matches nodes that do not carry the tag; and `hasFeatureValue(type, name, value)` compares against the feature's stored value. Predicates apply in the order written, after the node-type test. Because `hasFeatureValue` matches, documents processed with the AWS Textract model (`kodexa/aws-textract-model-v3`) outside lightweight mode now gain the `FormAssistant/Label` and `FormAssistant/Value` form-label tags. Re-check any selector using these shapes that you had worked around. * **Dates written with month names, dashes, or a UTC offset convert to the value you meant.** `2026-Mar-01` and `1-Jan-2025` convert to 1 March 2026 and 1 January 2025, and RFC 1123 timestamps convert correctly on every weekday. A space-separated date-time carrying a numeric UTC offset (`2026-03-01 12:00:00 -0500`) is stored at the instant the offset states — 12:00 at `-05:00` is 17:00 UTC. Purely numeric dash dates (`04-30-25`, `1-2-2026`, `03-1-26`) are unchanged. Values written with an offset before this release may be stored at a different instant, or a different date, than they are now; re-extract them if you compare against them. * **`addFeature` reports a value it cannot store, and leaves the attribute unchanged.** In event-subscription scripts and script steps, `addFeature` on a data attribute normalizes its value at the script boundary: a cyclic or otherwise non-serializable value raises a catchable script error and the attribute is left untouched. Typed arrays are stored as base64 and integer values as floats immediately — the same shape a reload produces. * **Explain Plan phases close with a status and duration, and the detail pane opens at full width.** Every top-level phase in a document's Explain Plan — the extraction run, knowledge application, and preprocessing — is persisted with a completion status and a measured duration, so the plan and processing-step analytics show how long each phase took. A phase that recorded its own outcome, such as an error or a skip, keeps it, as does a duration the phase measured for itself. Clicking a node opens the detail pane at its configured width, flush to the edge of the panel. * **PDF Parser: more accurate scan detection, with new opt-in tuning options.** The `PDF Parser` module (`fast-pdf-model`) now handles three cases that could misroute a healthy text PDF: metadata containing stray control characters, a `pdftotext` warning exit that still produced usable output, and a `min_scan_pages` of `0` or a non-numeric value (which falls back to `1` with a warning). All three are extracted normally rather than routed to OCR. The advanced per-page detector gains four options, all off by default: `text_margin_ratio` (ignore text within this fraction of the page edge — scanning stamps, Bates numbers, burned-on headers — when judging whether a page has a usable text layer; also applies to the standard detector), `use_summed_image_coverage` (measure the union of all images on a page, catching scans tiled into strips), `check_font_encoding` (treat text that extracts as mojibake as a scan), and `advanced_full_page_scan` (examine every page rather than stopping once `min_scan_pages` pages are confirmed, so the recorded page list is complete). A new `scan_detection_shadow` option records per-page scan results on the document and its page nodes without changing the verdict or routing, and when the General Parser hands a document to OCR, the scan-detection reason is preserved on the OCR output under a `fast_pdf_scan_detection` metadata entry. * **Advanced scan detection labels only the pages it examined.** With `use_advanced_scan_detection` or `scan_detection_shadow` enabled on the PDF Parser module, page nodes carry `fast_pdf:is_scan` (true/false) and, on scanned pages, a `fast_pdf:scanned` presence marker — written for the pages the detector examined and no others. A page the parser could not read carries a `fast_pdf:scan_error` marker instead of an `is_scan` verdict, and pages the walk never reached carry no label at all, so an absent feature means "not measured", never "not a scan". Select scanned pages with `//page[hasFeature('fast_pdf','scanned')]`. Document metadata describes the walk itself: `scan_pages_examined` is how many pages were examined, `scan_detection_mode` is `enforce` or `shadow`, and `scan_detection_complete` says whether the walk covered the whole document — it is `false` whenever the walk stopped short, which includes the ordinary case of stopping as soon as `min_scan_pages` scanned pages were confirmed, so read it as "this page list is partial" rather than as a failure flag. Pages already confirmed as scans always count toward `min_scan_pages`. For a complete page list, use shadow mode or `advanced_full_page_scan`. Both detection options remain off by default. * **Document Preprocessor: pages are rasterized only when they really contain vector artwork.** The preprocessor's vector-graphics check now matches PDF drawing operators as whole tokens, so ordinary page text no longer counts as drawing (for example the letters `re` inside a word, or `m` and `c` inside a `cm` transform). In testing the share of pages flagged as vector graphics fell from about 94% to 74%, so far fewer text-only pages are rasterized at 300 DPI — faster preprocessing, with unchanged output for pages that truly carry vector graphics. Clip-only paths (`W`/`W*`) are still excluded. **Data Forms:** * **A cleared field is treated as empty everywhere in the review form.** When a reviewer clears a value, the form treats that field as empty from then on: re-extracting refills the cleared field instead of adding a duplicate, clearing the last value in a grid row lets the now-empty row be removed rather than left behind, copy actions and formula explanations read the current value, and data-form script triggers fire with the cleared (empty) value rather than the original extracted text. No action required. * **Clearing a date, date-time, currency or number value now clears it — on screen, in the saved data, and after reload.** Using a form's clear-value shortcut or emptying the field blanks the stored value and its displayed text together, the field renders empty immediately, and it stays empty when the document is saved and reopened. Clicking into and out of an untouched date field no longer registers as an edit, so it does not dirty the document or block a clear that follows. Form-declared data-entry shortcuts keep acting on the field you were just in, so a modifier that blurs the field on the way into a key chord (an Alt-based combination, for example) no longer stops the shortcut from working. * **Edits to numeric, date, and boolean fields persist exactly as entered.** Typing a new value into a typed field — number, currency, percentage, decimal, integer, date, date-time, or boolean — updates that field's value and leaves the originally extracted text in place as provenance, so what you entered is what the saved document holds after reload; clearing such a field still clears it outright. Re-tagging a field from a new selection in the document records the newly selected text as that field's value and provenance; on a numeric or date field the typed value is not re-derived from the new text, so enter it directly if it should change too. No action required. * **`setAttribute` in the form script bridge stores values by the attribute's type.** `kodexa.data.setAttribute(dataObjectUuid, path, value)` on an existing attribute now writes into the column that matches the field's type: a numeric string (grouping commas allowed) or a JS number becomes the numeric value, a date becomes the date value — a date-only literal such as `"2026-01-01"` is accepted and stored as midnight — and booleans set the boolean value. The write goes through the same audited update path as a reviewer's edit, so it is saved, survives reload, and appears in the change history; the attribute's original extracted text is left unchanged. If your scripts pre-formatted values to work around dates or numbers not saving, that is no longer necessary. * **Copy rules keep the original extracted value on copied data, and multi-select copies follow the rule.** A copy made through a copy rule with attribute mappings now carries the source attribute's original extracted value and confidence onto the copy, and the copy records its source as provenance — so a correction a reviewer made before copying remains detectable on the copy, and object history shows the object as a copy. Selecting several data objects and copying them under a copy rule now produces exactly the result of copying them one at a time: attribute mappings, `copyAttributes: false`, and `stampAttributes` are applied the same way regardless of how many rows are selected. * **Audit/Notes opens on the value you picked, from any workspace layout.** Choosing **Audit/Notes** on a data value opens the Audit panel with that value's thread already showing, including when the side panel was collapsed. Closing the side panel with the toggle and reopening it returns you to the tab you were last using rather than to Chats. In layouts where no audit view is available for the current task, the Audit/Notes menu item is hidden instead of being shown but inactive. * **The Formula Execution Trace shows the correct sign on every component.** When you open the execution trace for a formula value, subtracted operands are shown as subtractions and the tree view picks the correct top-level operator in mixed expressions such as `(A + B) - C`, so the trace reads the way the formula is written rather than listing every component as an addition. **Tasks:** * **A task's documents now open in a deterministic order, with the first document as the primary.** Task document links and activity document links carry a new integer `ordinal` field (`TaskDocumentFamily`, `ActivityDocumentFamily` and their create/update requests in the API, Python SDK and CLI models). The ordinal is stamped from each document's position in the `documentFamilyIds` list the activity was started with, copied onto the task's documents when the task is created, and used by the workspace to order a multi-document task — so the task opens on the first document you submitted rather than an arbitrary one, and you choose the primary document by listing it first. Ordinal `0` is the primary document. A `POST /api/task-document-families` or `POST /api/activity-document-families` that omits `ordinal` (or sends `0`) appends the document after the existing ones; an explicit non-zero ordinal is stored as given. Existing multi-document tasks and activities were backfilled with a stable order. * **Task completion is guarded when a document's latest changes are not yet in the stored document.** Marking a task done — from the status dropdown, `PUT /api/tasks/{id}`, `PUT /api/tasks/{id}/status`, or a batch update — returns `409 Conflict` naming the affected documents when one of the task's documents still has unapplied changes from its most recent save. Reopen the document, redo those changes, and save; a successful save clears the block and the task completes normally. Only the latest save per document is checked, so an older failure that has since been re-saved does not block. Error dialogs now show the server's own explanation for any `4xx` response, so the reason is visible immediately instead of a generic "Request failed" line. * **Task actions' `attributes` block now persists writes to existing values.** When an action's `attributes` block stamps a field that already has a value — for example `accepted_by` / `accepted_at` on Accept, or clearing them on Re-open — the write now goes through the same audited update path as a reviewer edit, so it is saved with the document, survives reload, and appears in the change history. The stamped value is stored according to the target field's type (number, date, boolean, or text), and the attribute's original extracted text is left unchanged. Creating a new attribute via `attributes` behaves as before. * **Task status names and colors display consistently wherever a task appears.** The home dashboard, related-task popovers, kanban cards, the workflow task navigator, the parent-task chip, and sub-task timelines now resolve a task's status through the organization's task statuses, so they show the configured status name and color (for example "In Review") rather than the raw slug or no status at all. * **Task Metadata status keeps its value after a batch save.** The task object returned by `POST /api/batch-update` now carries the status under `statusSlug`, matching the Task model everywhere else, so the Status dropdown in the Task Metadata panel shows the saved status the moment the save confirmation appears instead of resetting to its placeholder until a reload. Integrations reading the task from the batch response should read `statusSlug`; the legacy `statusId` key is no longer emitted there. * **Task group queues load the full working set.** The reviewer queue on a task group's landing page and workspace loads up to 100 member tasks, so a group of that size shows every open task in the queue — ordered by priority, then creation date — and always offers the next one to pick up, even when the highest-priority tasks are already complete. * **The task template editor opens every template.** Templates created outside the editor — with `kdx`, in project-template YAML, or through the API — that have no `metadata.properties` set now open in the Project → Task Templates editor with defaults filled in, instead of an empty pane. **Orchestrator:** * **Scripted LLM calls carry model and cost attribution in the Explain Plan.** Every `llm.invoke` and `llm.invokeWithPromptRef` call made from a script step records the model id, provider (`anthropic_bedrock`, `cohere_bedrock`, or `bedrock`), input and output token counts, duration, and cost onto the Explain Plan step the script is currently inside — no script changes needed. When the call is not inside a step, the platform adds a completed `llm_call` step on the first document the script loaded, named from the call's `note` when you supply one, from the prompt reference for `invokeWithPromptRef`, and `LLM call` otherwise, so every scripted call is visible and attributable in the plan and in processing-step analytics. Cost is priced at call time from the AI Gateway model catalog — the same prices shown on the model list — and recorded once, so later catalog price changes never reprice history; where the orchestrator has no `AI_GATEWAY_URL` configured, or the model is not in the catalog, cost records as `0` and the call still succeeds. * **`recordLLMCall` records a usage entry only when it carries usage.** The step-authoring helper in the TypeScript, Python (`record_llm_call`), and Go step authors adds a model-usage entry when at least one usage field is set — model, provider, token counts, cost, duration, finish reason, request id, or in the Go author a sampling parameter such as `temperature` — and records the prompt and response bodies either way. A wrapper that only narrates the prompt and response text therefore leaves the platform's own model and cost record as the step's usage. * **`loadDocument()` returns one handle per document family.** Calling `loadDocument(familyId)` more than once in a script step for the same family now returns the same document handle, so data changes and processing steps written through different helpers all land on one document and publish as a single new version. Repeat loads of an already-loaded family do not count toward the script's maximum document-load budget. Scripts already written as though repeated loads return the same document need no changes. * **The orchestrator's built-in LLM model is configurable and works in every region.** AI task naming and the script-step `llm.invoke` binding call Amazon Bedrock with the model set by the new `LLM_SMALL_MODEL_ID` environment variable, which defaults to the `global.anthropic.claude-haiku-4-5-20251001-v1:0` cross-region inference profile. The global profile is accepted from any supported AWS region, so environments outside the US get working AI naming and scripted LLM calls with no configuration. Set the variable to pin a different Bedrock model id. * **Reprocessing from a step re-runs that step and everything downstream.** Choosing Reprocess on a completed step runs it again, including per-document work that had already finished, and discards the document versions the later steps produced, so the re-run starts from the same input the original run had; review tasks created downstream are removed and recreated when those steps run again. Retrying an activity's failures keeps the failed step's own completed per-document work but re-runs everything downstream, since its input may change. Documents that a routing step sends down another branch are shown as not processed on this branch, with no leftover results or errors from a run that no longer applies, and a run that a reprocess supersedes is marked Reprocessed rather than left looking as though it is still going. * **Finished module work survives a brief platform hiccup.** When a module runtime finishes a piece of work, it retries delivering the result with backoff instead of discarding it, so a transient `5xx`, `429`, or connection error no longer costs you the completed work; a rejection the platform will not accept (a non-`429` `4xx`) still stops immediately. Tune the retry window with `KDXA_RESULT_RETRY_MAX_SECONDS` (default `780` seconds, and never longer than the time left in the invocation) and the longest wait between attempts with `KDXA_RESULT_RETRY_BACKOFF_CAP` (default `30`); scheduler callbacks use the shorter `KDXA_CALLBACK_RETRY_MAX_SECONDS` (default `60`). Keep-alive heartbeats retry transient server errors too. The orchestrator also stays in service through a load spike, slowing down rather than dropping out, and still reports unavailable within a second when its database is genuinely unreachable. * **Module runtimes reuse a model package across steps.** A module runtime downloads and extracts each model package once and reuses it for every later step, so a long-running runtime keeps processing for its full lifetime instead of exhausting its temporary storage. Because the package is cached for the life of the runtime, a model implementation redeployed while a runtime is already running is picked up the next time that runtime restarts — a deploy that cycles the runtimes is unaffected. **CLI:** * **`kdx sync push` applies deletions inside the regions your files own.** Removing a key from a task template's `metadata.properties`, or removing a taxon — or any key inside one, such as a taxon's only `validationRules` entry — from a data definition's `taxons`, and pushing again brings the server in line with your file: the resource is reported as changed and the deleted content is removed. Only content your file actually states is compared, so a file that omits `metadata.properties` entirely leaves properties set in Studio untouched, while an explicit `metadata: {}` or `properties: {}` clears them, and a fully commented-out `taxons:` key is treated as unmanaged rather than as a deletion. Fields your file never mentions — `id`, `changeSequence`, `ref` and other server-maintained values — are ignored as before. Deletions converge when pushing to a current Kodexa API server; pushes to legacy servers keep the previous behaviour. * **`${org}` placeholders are resolved in module deployments, and any leftover placeholder stops the push.** Module payloads deployed by `kdx sync push` — including references embedded in a module's script, such as `${org}/my-taxonomy` — now have `${org}` substituted with the target organization slug, the same as every other resource type. After substitution, both the module and generic push paths check the payload: if any string still contains `${org}` (for example a malformed token such as `${org}-name` instead of `${org}/`), the push aborts with an error naming the resource and the field paths, instead of deploying the literal text and failing later at runtime. **Analytics:** * **Analytics task metrics now count by status type, not status name.** In the task, task-link and assignment analytics datasets, `completedTaskCount` / `completedTaskLinkCount` / `completedAssignmentCount` count tasks whose status is DONE-typed (or that carry a completed date), whatever the status is called — statuses named `reviewed`, `rejected` or `canceled` count as completed just like `done`. `openTaskCount` / `openTaskLinkCount` / `openAssignmentCount` count OPEN-typed statuses only, matching the task grid's **Open** quick filter and the task-group `openGroupCount`, and the `overdue*` metrics count any not-done task past its due date. Because IN\_PROGRESS and BLOCKED tasks are neither open nor completed, Open + Completed no longer necessarily equals Total — read each metric directly rather than deriving one from the others. Expect Completed to rise and Open to fall on existing dashboards. * **Extraction, classification, and chunking LLM calls are recorded as typed model usage.** Steps that record their usage as freeform `llm_usage` metadata now also carry structured `model_use` entries — model id, token counts, duration, and the provider where the model id identifies one — whenever the document is read, so an activity step's token totals, the step detail pane in the Explain Plan, and the model usage projected to analytics account for these calls alongside script-step calls. Existing documents pick this up on their next load or publish, with no reprocessing, and the freeform metadata stays in place for older readers. Cost is not yet attributed to these entries. **Chat:** * **Collapse the chat list to a rail in the Chat panel.** The list of chats beside the conversation can now be collapsed with the hide/show control in its header. Collapsed, it becomes a narrow rail that keeps **New chat** and your existing chats (shown as initials, with the active chat highlighted) one click away, and the conversation takes the reclaimed width — so a narrow panel dock still leaves room to read and reply. The collapsed/expanded choice is remembered per task or project workspace in your browser. * **Files offered by an agent appear as a labelled attachment card.** When an agent message offers a file to download, the message now shows an attachment card — file icon, file name (full name on hover), and a **Download** button — instead of a bare download-arrow emoji. Agents supply the file name through the new optional `name` attribute on the `` tag; a tag without `name` renders as a labelled **Download file** chip, so existing messages keep working. The control is a real button: keyboard-focusable and announced by screen readers with the file name. Copying a message as text or rich text keeps the file name in the copied content. If you author agents that emit this tag, add `name` so users can see which file they are about to download. **Activities:** * **The New Activity wizard enforces document group upload limits.** When a plan's document group sets `maxSize`, `maxPages`, or `hardMaxPages`, each file added in the New Activity wizard — by drag-and-drop or the file picker — is checked before it uploads. A file over `maxSize` or a PDF over `maxPages` prompts with **Include Anyway** / **Exclude Document**; a PDF over `hardMaxPages` is rejected with a **Document Too Large** notice and cannot be overridden. Page checks apply to PDFs only, every file in a batch is judged on its own, and a PDF whose page count cannot be read is allowed through. This brings the wizard in line with the New Task dialog, which already enforced these limits. Set the limits on the document groups in your activity plans to have them applied. **Manage:** * **Manage → Intakes lists only the current organization's intakes.** The Intakes grid is scoped by the organization in the URL, so it shows the right organization's intakes on a hard refresh, a direct link, or the first page after sign-in, and no longer carries a previously viewed organization's filter across sessions. Browsers holding an older saved grid filter are corrected automatically the next time the page opens. **Platform:** * **Sort the Projects list by Status, Owner, and Organization.** Clicking those column headers in Organization → Projects now sorts the list in either direction (a saved sort on one of these columns previously emptied the grid on every visit; affected users self-heal on next load). For API clients, `GET /api/projects` accepts `sort=status.status`, `sort=owner.firstName`, and `sort=organization.name` alongside the existing column sorts, and text search and filters keep working while one of these sorts is active. * **Signing in goes straight through.** Opening the app while signed out takes you to the sign-in page once and returns you to the page you asked for, with no repeated redirects and no stalled "Initializing Kodexa Platform" screen — including on slower or managed browsers. * **Notifications show their full message.** Success and error notifications across the workspace — a failed export or download, a copy-to-clipboard result, an activity step action, a channel stop — now display their explanatory text beneath the heading, including the specific reason an API call failed, instead of a bare "Error" or "Success" heading. ### Release 2026.9 Rollup of every customer-facing change in the 2026.9 GA release. The headline items: knowledge-set priority is now enforced and project-scoped sets finally apply, with feature links and item edits that reliably save; DeepSeek, Qwen 3, and Z.ai GLM models join the AI Gateway; and chat gains pre-built prompts with context-aware starter tiles. Create and update endpoints across the API now persist exactly what you send — explicit `false` and `0` included — with sparse updates and optimistic locking enforced consistently; this is the release's one breaking change — see the upgrade note under Platform. **Knowledge:** * **Project-scoped knowledge sets now apply to documents.** A knowledge set scoped to a project was silently never matched during document processing — the project was mis-derived from where the document was stored, so only organization-level sets ever applied. Matching now takes the project from the processing run itself, across every processing path, so a project-scoped set applies to documents processed in that project. Documents processed outside any project context are matched against organization-level sets only. * **Knowledge set priority is now enforced.** The 0–10 priority on a knowledge set (default 5) previously had no effect. Now, when several applied sets contribute knowledge to a document, their items are ordered by priority — highest first, with a stable tie-break — everywhere that order is visible: the knowledge context supplied to extraction, and the knowledge/instruction panel in the review form, which now shows the highest-priority set's instruction instead of whichever happened to load first. Priority only affects ordering; it does not change which sets match a document. * **Features linked to a knowledge set now actually save.** Linking features to a knowledge set — the set's feature palette — silently persisted nothing: after a reload the palette came back empty, and an expression condition that referenced one of the lost features rendered as an unresolvable picker. The `features` array on `POST`/`PUT /api/knowledge-sets` is now persisted: omitting the field (or sending null) leaves existing links untouched, an empty array clears them, and each entry may reference a feature by `id` or `slug` (validated against the set's organization — unknown references are rejected with a clear error). In the set editor, picking a feature through **Add Feature** in Advanced mode now also inserts it as a condition in the expression (duplicates are skipped), and Advanced mode shows a read-only **In Palette** chip list so palette membership is visible outside Simple mode. * **Bulk-loading knowledge items no longer drops items.** Creating, updating, or deleting many items of the same knowledge set concurrently — for example a CLI sync applying a set's items with parallel workers — could hit database deadlocks that failed most of the writes, leaving only a few items saved. Item writes to the same set are now serialized on the server, so parallel loads complete with every item intact. * **Reordering and deleting knowledge items now sticks.** In the knowledge set editor, items can be reordered by drag-and-drop, and the dropped order both renders correctly and is saved — so the order you arrange is the order the items are applied in. Deleting an item now persists too: previously the row disappeared from the editor but was never removed on the server, so it returned on the next reload. An item added and then removed in the same editing session is simply discarded without an error. * **Negated matching expressions no longer grow an extra "All of" group on every save.** Saving a knowledge set whose matching expression has NOT at the top level used to wrap it in a new "All of" group each time the editor reloaded it, nesting one level deeper on every save round-trip. NOT-rooted expressions are now kept as-is through save and reload, and they evaluate exactly as before. **Data Forms:** * **Notes on data values now save and stay in sync.** A note added to a data value through the Audit/Notes thread only ever lived in the current browser session — a save containing nothing but notes was silently discarded, so notes vanished on reload and never reached anyone else. Notes and their replies are now persisted with the document's change history: they survive reload, travel with the document, and appear for other people viewing the same document without a refresh. In the task workspace, the **Audit/Notes** context-menu item on a value now opens the Audit panel as expected — previously it did nothing in that layout. **Chat:** * **The AI Gateway now serves DeepSeek, Qwen, and Z.ai GLM models on Amazon Bedrock.** Modules and prompts that call LLMs through the Kodexa AI Gateway can now target non-Anthropic Bedrock model families — DeepSeek (including R1), Qwen 3 (including the vision-capable qwen3-vl family), and Z.ai GLM — through the same unified request, tool-calling, and streaming interface as every other provider. Per-family limitations are validated up front with a clear error (for example, a tool-use request against DeepSeek R1, which doesn't support tools, fails immediately instead of surfacing a raw provider failure mid-request). The gateway's model catalog can also declare per-model capability flags — `supports_tools`, `supports_vision`, `supports_documents`, and `reasoning_output` — surfaced on the model list so callers can pick a model by what it actually supports instead of parsing description prose. Streaming failures now arrive as a structured error event using the same error taxonomy as non-streaming calls, instead of error text spliced into the model's reply. * **Start a chat from a pre-built prompt, with starter tiles that match your context.** The chat panel's starter tiles now differ by context — task chats suggest task-shaped questions (summarize this task, explain the exceptions), project chats suggest project-level ones. Alongside them, a new **From prompt** button opens a picker of your organization's Prompt resources, so teams can publish curated, reusable prompts for common workflows. A prompt's title comes from its `name`, and its `metadata` carries `context` (`"task"` or `"project"` — which screen offers it), `category` (how the picker groups it), and `prompt` (the message body). Picking one starts a new chat named after the prompt with the body prefilled into the input — nothing is sent until you press send, so you can tailor it first. The button appears only when prompts exist for the current context; prompts are managed through the standard `/api/prompts` resource API. * **Copy an entire chat transcript from the chat header.** A copy dropdown in the chat header exports the whole conversation in your choice of Markdown, plain text, or rich text (formatted HTML with a plain-text fallback, so pasting into Word, Google Docs, or an email keeps the formatting). The transcript is chronological, with author and timestamp headers on each message, and excludes internal system traffic so it reads as the conversation you actually saw. It covers the messages currently loaded in the panel (the most recent 50); a copy that fails or produces nothing shows a notification instead of silently doing nothing. * **Platform administrators can see every user's chats on a task.** Chat lists in task views now show a platform administrator all users' conversations on that task — useful for supervision, QA, and support — while project-workspace chat lists remain scoped to each user's own conversations for everyone. The widened view affects visibility only: when an administrator starts a chat, or the platform opens one on their behalf, it is always their own — the broader view never causes a message to land in another user's chat. * **Chat agents hold their scope under adversarial prompting.** Chat agents are hardened against prompt-injection and jailbreak attempts: an agent now treats the capabilities enabled for its chat as its entire job, declines and redirects out-of-scope requests, resists instruction-override attempts (persona swaps, "developer mode", "ignore previous instructions"), and will not reveal its internal instructions or configuration. Content the agent reads from documents and tool results is treated as data, never as instructions. These boundaries are enforced by the platform rather than just requested of the model — a tool call outside the chat's enabled capabilities, or a file access outside the agent's working scope, is blocked before it executes. * **Renaming a chat now saves reliably and confirms the result.** A rename could previously fail with a spurious permission error, or overwrite changes someone else had just made to the same chat (such as its sharing settings) — and a failure gave no visible feedback at all. The rename now updates only the chat's name and shows a success or failure notification; on failure the rename stays open so you can correct and retry. **Platform:** * **Create and update endpoints now persist exactly what you send — explicit `false`, `0`, and other zero values included (breaking).** Previously a generic `PUT` silently dropped any field whose value was `false`, `0`, or empty: the request returned 200 OK but the value never changed — so deactivating a user, switching a boolean option off, or zeroing a numeric setting could be a silent no-op. `POST` had the twin problem: an explicit `false` or `0` on a field with a server-side default was overwritten by the default. Both paths now track which keys your request body actually contained and write exactly those fields: explicit zeros persist, omitted fields are left untouched (reliable sparse updates), sending `null` clears a nullable field, and echoing back a `GET` body is a safe no-op. The write path is also hardened: system-managed and ownership fields (`id`, `uuid`, `createdOn`, `createdByUserId`, `organizationId`, `projectId`, and soft-delete state) are ignored if sent in an update body, and `changeSequence` is never written directly — it serves only as the optimistic-lock token; a request body that is not a JSON object, or that repeats the same field under case-variant keys, is rejected with `400`; uniqueness and reference violations now return clean `400`/`409` responses instead of `500`s; and optimistic locking is enforced whenever the body carries a non-null `changeSequence` — including `0` on a never-updated resource — so a stale save returns `409` instead of silently overwriting newer data. **Upgrade note.** Audit integrations that `PUT` full or hand-built objects: fields carrying `false`/`0` are now written rather than ignored, and calls that used to return `200` can now return `400` (non-object body, duplicate case-variant keys) or `409` (stale `changeSequence`, uniqueness conflict). To leave a field unchanged, omit it from the body; to clear a nullable field, send `null`. Values sent for `createdByUserId`, `organizationId`, or `projectId` on update are ignored. On the project-assistant endpoints, an update that changes `name` without sending `slug` now re-derives the slug from the new name. * **Document-store and data-store bodies are now complete, stable, and safe to round-trip.** Fetching a store could previously return different bodies for the same resource — sometimes missing inner metadata keys such as `indexed` — and saving a store back (an edit in the UI, a full-object `PUT`, or a `kdx apply`) could silently wipe inner content-metadata settings such as `indexed`, `documentProperties`, and `labelExpressions`. Store bodies now carry the nested store metadata intact under a new `contentMetadata` key (the existing flattened keys remain for compatibility), reads return the same body every time, and round-trip saves preserve every setting. * **Project slugs are now guaranteed unique within an organization.** Creating or renaming a project whose slug would collide with another project in the same organization now automatically appends a numeric suffix (`-1`, `-2`, …) instead of allowing a duplicate — renames previously had no protection, so two projects could end up sharing a slug and slug-based references became ambiguous. Uniqueness is now also enforced at the database level. A `slug` you supply on create is honored (and suffixed only on collision) instead of being silently replaced with a derived one, and re-saving a project under its own slug stays idempotent. Creating a project from the CLI now leaves slug derivation to the server, so project names with spaces or punctuation no longer fail slug validation. * **Deleting an organization no longer fails when it has an audit history.** Hard-deleting an organization previously hit a server error once audit records existed for it. The organization's audit-trail records are now removed as part of the delete, so the delete completes cleanly. **Projects:** * **Triggers gain two new event kinds and can ship inside project templates.** `document_locked` (a reviewer locks a finished document) and `knowledge_set_updated` (a knowledge set changes) join `task_created`, `task_status_changed`, `activity_completed`, and `manual` as accepted trigger event kinds — these are what let a project start an activity plan in response to review activity. Project templates can now also declare a `triggers` array (`slug`, `name`, `eventKind`, optional `eventFilter` and `inputMapping`, `activityPlanRef`, `enabled`); the triggers are created at project creation after the template's activity plans are bound, and re-applying a template skips triggers that already exist instead of failing. Separately, `PUT /api/triggers/{id}` now fully validates the update — an unrecognized event kind, malformed activity-plan reference, or malformed filter is rejected instead of silently persisted. * **Required and pattern validation on project-template options is now enforced at project creation.** A template option (in `options` or `dataOptions`) marked `required: true` must be filled in before the New Project dialog allows Create — previously the required asterisk was cosmetic and the project was created with the option blank. Options can also declare `properties.pattern` (a regular expression the value must match) with an optional `properties.patternMessage` shown inline when the value doesn't match; validation errors appear under the field and above the action buttons, and the dialog switches to the first tab with a problem. An option with a defined `default` counts as satisfied, and developer options are only validated in Studio, where they are shown. * **Surface a template option on the first tab with `showOnPopup`.** A project-template option flagged `showOnPopup: true` now renders on the New Project dialog's **Details** tab, directly beneath the project name and description, instead of behind the Developer Options or Data Options tab — so a required option is visible before you can hit Create. After the project exists, the same flag places the option on the **General** tab of Project Settings, and a Settings tab whose options are all flagged is dropped entirely. Options without the flag stay on their usual tabs (in the New Project dialog the original tabs remain, minus the flagged options). * **Values entered for template options when creating a project now save reliably.** Typing a value into a template option in the New Project dialog could show nothing or silently drop the edit, and even when a value was submitted the server could discard it while applying the template's options — so a project could end up created without the values you entered on the form. Option values now display as you type, validate, and persist onto the created project exactly as entered; the create dialog also no longer modifies the shared template definition. * **Project Settings changes no longer appear to revert after a page reload.** Saving project settings — name, description, or template option values — always persisted on the server, but reloading the page could show the old values for up to a day because the browser kept serving a stale locally cached copy of the project. The local cache is now updated on save, so saved changes survive a reload immediately. **Orchestrator:** * **CREATE\_TASK steps can name and stamp tasks with runtime placeholders.** The `taskData` of a CREATE\_TASK step now resolves placeholders at the moment the task is created: `${activity.title}` (the activity's title at that point, after any automatic or manual rename), `${project.name}`, `${project.id}`, and `${project.options.dataProperties.}` (a value entered in the project's data properties, as defined by the project template). Placeholders work in the task's `title` and `description` and in top-level string values under `taskData.properties` — so one plan shared across many projects can give each child task a context-specific name (e.g. `"title": "Review ${activity.title}"`) or copy a project-scoped identifier onto every task it creates for downstream filtering. If `${activity.title}` or `${project.name}` cannot be resolved, the text is left unchanged; a `dataProperties` placeholder always resolves — to an empty string when the key is unset or not a simple value — so unresolved tokens never leak into task properties. * **The activity Logs tab now shows the right document's logs.** When an activity step runs once per document, the Logs tab in the activity status dialog could display logs belonging to a different document than the one you were focused on — reopening the dialog on another document, or clearing the document focus, could leave it pinned to the previous document, and background refreshes reset the execution you had picked in the dropdown. The Logs tab now follows the focused document as it changes, preserves your dropdown selection across refreshes, and shows a clear empty state instead of another document's logs when the focused document has no execution for that step. * **Task operations no longer fail when a history entry can't be recorded.** Actions such as locking or unlocking a task, or changing its team, also record an entry in the task's activity history. Previously, if that history write failed, the whole operation failed with a server error — task locking could break entirely. The history record is now best-effort: the operation itself completes normally and a failed history write is logged server-side instead of failing the request. **Tasks:** * **The explanation entered when unlocking a task is now recorded.** Unlocking a locked task prompts for an explanation, but the text was being discarded — the unlock went through with nothing attached. The explanation is now saved on the task's unlock activity: the activity text reads `Task unlocked: ` and the reason is available on the activity record through the task-activities API, so audits of who unlocked a task and why are complete. (The `reason` field on the unlock endpoint's request body is optional; a blank explanation still unlocks.) **Activities:** * **Creating an activity from inside a project no longer asks you to pick the project.** The New Activity wizard opened from a project's Activities tab now starts with that project already selected, skipping the redundant project-selection step — matching how the wizard already behaved when launched from a document view. * **The My Projects filter in the New Activity and New Task wizards no longer flips on its own.** The filter used to default to on and then silently uncheck itself whenever the project list came back empty, so it appeared randomly checked or unchecked from one open to the next — and could quietly widen your project list without you asking. It now defaults to off and only changes when you change it. When the filter is on and you don't own any of the listed projects, the wizard now says so and suggests unticking **My Projects** to see all projects, instead of clearing the filter behind your back. **Document:** * **Edits made right after saving a document are no longer discarded.** After a save in the review workspace, the platform's periodic change check could mistake your own save for an outside change and force-reload the document, silently throwing away anything you had entered since the save. Your own saves are now recognized and the document is left alone. When a document genuinely is updated outside your session it still reloads to the latest version, but you now get a prominent warning whenever that reload discarded unsaved edits, instead of a quiet refresh. For API clients, each result in the batch-update response now includes a `contentObject` object carrying the saved content object's `id` and `changeSequence`, so a custom client polling for changes can distinguish its own save from an external one the same way. **SDK:** * **Python SDK objects stay current after writes.** Calling `create()`, `update()`, or `deploy()` on an SDK entity now refreshes the object in place from the server's response — previously the local object kept its pre-write state (including a stale `changeSequence`), so a second `update()` on the same object failed with a `409` conflict. Task status and assignee updates made through the SDK also now send the task's own change sequence, so they participate correctly in optimistic locking. ### Release 2026.8 Rollup of every customer-facing change in the 2026.8 GA release. The headline items reach across the platform: a new `POST /api/analytics/embed-token` endpoint for embedded, per-tenant analytics, a dedicated task-lock feed on the CDC data lake, and keyboard shortcuts you can bind to task-template actions. Scripts can now stamp `taskProperties` onto the tasks they create, and the Knowledge expression builder gets a broad overhaul — self-healing matching expressions, reversible negation, and safer Simple/Advanced mode switching. The CLI hardens up with real server-side `--dry-run` validation, credential redaction in debug output, and pipe-safe `-o json`. Alongside those, this release lands a wide set of Data Forms, Document review, and Tasks fixes. One breaking change lands on the task-status API; see the upgrade note. **Analytics:** * **New `POST /api/analytics/embed-token` endpoint for embedded, per-tenant analytics.** Authenticated callers can exchange their Kodexa credential for a short-lived, tenant-scoped RS256 JWT and query the standalone analytics service directly. The token's organization scope is derived server-side from the caller's team/organization assignments (never client-supplied), and every dataset it grants is row-filtered to those organizations, so a token can only ever read its own tenants' rows. The response returns `{ token, expiresAt, models, orgs }`, with token lifetime capped at 15 minutes. The minter is opt-in via the `analyticsEmbed` config block (`enabled`, `issuer`, `audience`, `keyId`, `tokenTtlSeconds`, `models`, `objects`, and `rowFilterField` — defaults to `org_slug`), while the RS256 signing key is supplied out-of-band through the `ANALYTICS_EMBED_SIGNING_KEY_PEM` environment variable. When the minter is disabled or the key is missing/unparseable, the endpoint returns `503` and platform boot is unaffected. * **Task locks now stream to the data lake.** Whenever a task is locked, the platform now mirrors a task-lock record to your CDC data lake under a new `task-locks/` prefix — a dedicated feed, kept separate from the existing work-session telemetry — so you can report on when tasks were locked and by whom. Each record includes `taskId`, `lockedAt`, `lockedById`, `lockedByEmail`, `taskStatus`, `projectId`, the task's `documentFamilyIds`, and the task `properties` object, and records the user who performed the lock (omitted for system or API-key callers). The write is best-effort and uses your existing data-lake configuration. * **More accurate Work Sessions durations, immune to client clock skew.** The **Work Sessions** dataset now measures each session's wall-clock time from the browser's own elapsed-time reading rather than the difference between a client and a server timestamp, so durations stay accurate even when a machine's clock is offset from the server. Active/engaged time is now reported raw instead of being silently capped to the wall-clock value, and two data-quality measures — **Skew-Invalid Wall Count** and **Avg Client Clock Skew (ms)** — surface sessions whose wall-clock reading was thrown off by client clock skew, so distorted readings are visible rather than hidden. **Focused Ratio (%)** is computed only from sessions that have both a valid wall-clock and an active measurement, and each session is counted once (intermediate saves within a session are collapsed to the final save) rather than double-counted. Sessions recorded before active-time capture existed are excluded from the active metrics instead of being averaged in as 0% engaged, and the **Total Active (ms)** / **Avg Active (ms)** measures are renamed **Total Focused (ms)** / **Avg Focused (ms)**. **Tasks:** * **The Task API now reports when and by whom a task was locked.** The Task object returned by the tasks API now includes `lockedAt` (the lock timestamp) and `lockedById` (the user who locked it) alongside the existing `locked` flag. * **The bulk "Lock" action on the Tasks grids now actually locks.** Selecting tasks and choosing Bulk Actions → Lock — in both the project and organization Tasks views — previously did nothing (the grid just refreshed as though it had worked). It now prompts for confirmation and locks each selected task. Separately, opening a locked task while "Assign on open" is enabled no longer pops a spurious "Unexpected Exception" error: the auto-assign is skipped for a locked task (which the server rejects) and the task simply opens. * **Setting a task's status from the UI now saves reliably.** Changing a task's status — through the bulk **Set status** action, the task details panel, or the status dropdown in the tasks grid — now persists correctly. Previously a bulk status change could clear the task's status (leaving the badge blank), a status change made in the task details form could be silently dropped, and two consecutive status changes on the same task could fail with a conflict error. * **Task actions now wait for a form's bridge lookups to finish before they can be triggered.** A task action such as Approve or Reject could previously fire while a form's service-bridge lookups were still resolving and writing back an auto-populated value, letting a task complete with a value that had not yet been saved. Approve, Reject, and other task actions are now disabled while any of the form's bridge lookups are still in flight, so an action only runs once the form's auto-populated values have settled. * **Task, task-group, and activity lists no longer show "nothing here" while a stale, invisible filter hides pending work.** In both organization and project views, a list could report nothing to do while work was actually pending, with no filter visibly selected — the restored query kept filtering by a stale clause while the toolbar controls reset to blank. Each list now persists its toolbar inputs (quick filter, facets, project selector, date/running filters) and rebuilds the query from them on load, so the visible filters and the results always agree; pre-existing stale state self-heals on first load. * **Task-status API field renamed `statusId` → `statusSlug` (breaking).** The request body for `PUT /api/tasks/{id}/status` now takes `statusSlug` — the org-scoped task status slug — instead of `statusId`. The value was already a status slug rather than a UUID; only the field name changed, so callers stop mistakenly sending a status UUID that could never be resolved. **Upgrade note.** Update any integration calling `PUT /api/tasks/{id}/status` to send the status under `statusSlug` (a slug such as `in-review`), not `statusId`. Requests still sending `statusId` are rejected with `statusSlug is required`. **Task Groups:** * **Task groups now record how their assignee was set.** Task-group responses — including the list endpoint and the take-next claim response — carry a new `assignmentType` field: `TAKE_NEXT` (claimed from the kiosk take-next widget), `SELF_CLAIM` (a user claimed the group themselves), or `MANUAL` (assigned by someone else). It is set on every assignment path, cleared when the assignee is cleared, filterable on the list endpoint, and included in the `task_group.assigned` event payload and the group's history. Self-claiming or self-releasing a group is now also correctly checked against the `claim` / `release` permission instead of always requiring `assign`. * **Clear filters on Task Groups.** The project and organization **Task Groups** tabs now show an active-filter summary row with a **Clear filters** button (matching the Activities tab), so you can see at a glance which filters are applied and reset them all — search, quick filter, and facets — in one click. * **Clearing a task group's team now works.** Removing the team from a task group via `PUT /api/task-groups/{id}` (sending an empty `teamId`) now clears the team instead of failing with a server error. Omitting `teamId` still leaves the team unchanged. **Task Templates:** * **Bind keyboard shortcuts to task-template actions.** A task-template action can declare a `shortcut` (and an optional `shortcutAltKey` alternate combination) in its `properties`, and the template itself can declare `saveShortcut` / `cancelShortcut` (with `saveShortcutAltKey` / `cancelShortcutAltKey`) for its default Save and Cancel buttons. Shortcuts are purely declarative — there are no built-in default keys — and they register per task and clear automatically when the task closes. The keyboard-shortcuts help dialog (`Ctrl` / `⌘` + `/`) now lists these alongside data-form shortcuts in an aligned Windows / Mac layout, and a registered shortcut that uses a modifier now fires even while a text field is focused. **Orchestrator:** * **Scripts can stamp properties onto the tasks they create.** A SCRIPT step's return value may now include a `taskProperties` object; its keys are merged into the `properties` of any CREATE\_TASK step that directly depends on that script, at creation time — so the task carries them from the moment it exists. The gating script is the last code to run with document context before the task exists, making it the natural place to compute per-document grouping or routing keys. The merged properties appear on every task API response and are queryable with the standard filter DSL (e.g. `filter=properties.invoiceRegion: 'EMEA'`). Static `taskData.properties` declared on the CREATE\_TASK step apply first and script-supplied keys win on conflict; if several upstream scripts supply `taskProperties` they merge in completion order (latest completion wins per key). Only steps that directly depend on the script receive the properties — there is no transitive propagation. Returning a non-object `taskProperties` from a script fails that script step with a clear error; the merge step itself is failure-soft — a merged properties blob over 256KB is dropped with a warning and never fails task creation. This is the creation-time counterpart to `tasks.setProperties`, which you still use to update a task that already exists. **Knowledge:** * **Knowledge sets recover from corrupt matching expressions.** A knowledge set's feature-matching expression could accumulate broken conditions — a blank condition, a feature condition with no feature chosen, or a leftover condition after un-negating a group. Any one of them evaluated as false and pulled down the whole **All of** group it sat in, so the set silently stopped matching every document. Broken conditions are now dropped automatically when the expression is evaluated, so an affected set starts matching again on the next processed document with no need to re-open and re-save it. The expression builder no longer creates these conditions, and any leftover unrecognised condition now appears as a clearly-labelled, removable **Invalid condition** row instead of an empty box you can't delete. * **Deleting a knowledge set now stops it matching documents.** Previously a deleted knowledge set kept matching every newly processed document indefinitely, because the eligibility lookup did not exclude deleted sets. Deletion now removes the set from document matching as expected. * **Expression builder handles negation and mode switching predictably.** A NOT block now carries the same **All / Any / NOT** control as any other group, so negation can always be turned back off — previously choosing NOT hid the control and left the block stuck. Negating an already-negated condition now removes the negation instead of double-wrapping it. The **Simple / Advanced** toggle follows the expression itself: switching from Advanced to Simple on an expression that uses NOT or nested groups asks for confirmation first (Simple mode keeps only a flat list of features), and adding a feature to the palette no longer bounces you into Advanced mode and traps you there. * **Inactive features stay visible in the feature palette.** Features linked to a knowledge set but deactivated are now shown in a distinct **Inactive features** group — struck through and removable — instead of being hidden. Hiding them could make a palette look empty right after adding a feature, with no way to see or clear the dead entry. The quick-create / feature picker also no longer offers deactivated features, since selecting one appeared to succeed but then matched nothing. * **Project-scoped knowledge sets are labelled correctly.** A knowledge set scoped to a project no longer mislabels itself as **Organization Level** in the sets list and detail panel when the project object hasn't been loaded — it now recognises the project scope and shows **Project-scoped** (with the project name when available). The knowledge-set API also returns the associated project, so the correct name is shown. Previously the false 'Organization Level' label could hide a mis-scoped set from its author. * **Deleting a knowledge set no longer shows a spurious error.** Deleting a knowledge set used to send the delete request twice, so it reported both a success and an error at once — the second request hit the already-deleted set. Deletion now runs exactly once and reports a single result. **Data Forms:** * **Transposed grid rollup cards gained toolbar and header polish.** An **expand/collapse-all** button toggles every rollup parent row at once and re-applies after the grid rebuilds; an **auto-filter** toggle (on by default) populates the description/value filters from whatever you click in the document viewer, with numeric content driving the value filter and text driving the description filter; and column headers now show the **full document name**, truncated responsively with the full name and summary in a hover tooltip, and the lock and knowledge icons kept snug beside it. Row numbers also stay stable while a search or filter is active, and columns open at a uniform default width regardless of document-name length. * **Adding to and editing a transposed grid rollup now behaves correctly around locked documents and on save.** The add (+) button only appears on columns whose document matches the active selection's source document, so you can no longer add into the wrong document's column; inline description edits are blocked on rows whose contributors all live in locked documents and skip locked columns when saving mixed-lock rows; and showing/hiding columns is still allowed for locked documents (locking blocks data edits, not view-only preferences). Separately, a newly added rollup group is no longer silently merged into an existing group, and a row added via the popover no longer vanishes, when the document is saved. * **Opening a grid's column menu no longer spuriously opens the first row's dropdown.** On editable data-object grids, opening or dismissing a column-header menu or the Choose-Columns panel could pop open the first row's SELECTION dropdown. That no longer happens — the first row's dropdown opens only on a real click or keyboard focus, not when the grid shifts focus internally. * **A stray dropdown no longer floats in the top-left corner when a form loads.** On load, a form auto-activates its first document-sourced field so it is ready for copy-from-document. When that field was a SELECTION dropdown on an inactive, hidden `v2:tabs` panel, activating it could leave a stray dropdown floating in the top-left corner. Activation now skips fields that aren't currently visible, so the stray dropdown no longer appears. * **Read-only SELECTION fields now display the option label instead of the stored code.** An attribute editor with `readonly: true` bound to a SELECTION taxon rendered the raw stored code rather than the chosen option's label. Read-only SELECTION fields now resolve and show the option label, falling back to the raw value only when no option matches or the options have not loaded yet. **Data Definitions:** * **Show a field's full value on hover in grids.** A new per-taxon display flag, `typeFeatures.showFullOnHover: true`, makes a grid column show its full cell value in a hover tooltip — useful for prose-length fields like AI explanations, notes, or comments without widening the column. Enable it from the taxon editor ("Show full text on hover") or set it in the data-definition YAML. Empty cells show no tooltip. * **`changed:dataAttribute` subscriptions fire when a value is first entered, not only on later edits.** An event-subscription script — and any derive logic it drives — now runs the moment a reviewer sets a value on a previously-blank attribute, matching the documented "fired when a data attribute changes" contract. Previously the subscription fired only on subsequent edits, so a reviewer's first pick into a blank field could leave dependent fields un-derived until the value was cleared and re-entered. **Formulas:** * **Formula-typed fields are computed and stored during server-side document processing.** When a document is opened outside the browser review UI — in server-side extraction and transform steps — FORMULA taxon values are now re-derived against current inputs and persisted, including creating formula attributes that don't yet exist, before validation, conditional-formatting, and selection-formula passes run. This brings server-side processing in line with the browser, so downstream consumers such as validations, exports, and analytics see correct formula values without a reviewer first opening the document. * **Extraction-time validations and formulas now resolve group references correctly.** When validations and formulas run as part of model processing — before the extracted data has been persisted — group references now resolve against the in-memory data tree instead of relying on database IDs that aren't assigned yet. Previously a bare group reference could resolve to nothing at extraction time, so a rule such as `isnull({group})` fired a false failure on clean data, and a validation exception on a nested group or attribute was anchored to the top-level object instead of the row it applied to. Exceptions now attach to the correct nested data object and attribute, and group-scoped formulas evaluate correctly. **Document:** * **Document-level validations added in one processing step now evaluate in later steps.** When a step adds document-level taxon validations — for example a transformer calling `set_validations` — and extraction later runs in a separate process, those validations are now read back and evaluated, so the expected data exceptions are raised. Previously they were held only in memory, so a freshly opened document saw an empty set and raised nothing. Such validations are now also included in JSON export. * **Grouped tags no longer corrupted when converted to and from features.** Converting tags to and from their feature representation — for example exporting a document to JSON and re-importing it, or adding tags through the feature API — now preserves the full tag payload, including the UUID-based grouping identifiers, so grouped and owned tags are no longer rewritten as ungrouped, ownerless rows that extraction would fragment into spurious data objects. Camel-case, snake-case, and legacy field-name variants of the payload are all accepted. Removing a tag from a node now removes every instance of that tag name (not just the first), deletes the underlying tag once nothing else references it, and is a silent no-op when the node doesn't carry the tag (it previously raised an error) — so loops that strip a node's tags one by one run cleanly. * **Copied text reads in reading order on rotated pages.** Selecting text on a rotated page — most visibly a page turned 180° — and using **Copy value from document** (or a native Ctrl/Cmd+C copy) now returns the text in visual left-to-right order instead of reversed. The highlight/overlay layer also stays aligned to the page across every rotate path — toolbar button, keyboard shortcut, and form-shortcut bridge. * **The document viewer no longer gets stuck on a permanent loading spinner for dense pages.** A page that takes a couple of seconds to render — for example a full broadsheet with thousands of words — could previously leave the viewer stuck on an endless loading spinner instead of displaying. These pages now settle and render correctly. * **`adopt_children(replace=True)` no longer discards nodes nested under the children it replaces.** When the nodes being adopted were descendants of the children being replaced — for example re-flattening a line whose words sit under intermediate column nodes — the replace step used to cascade-delete them, leaving empty phantom children that broke later processing. Adoptees are now re-parented before the replace-removal, so they survive intact. * **Document-engine updates apply cleanly on reload.** When a new engine version ships, the in-app "Update Available" reload now reliably fetches the fresh engine instead of re-serving a stale cached copy — which could loop the update prompt or force a manual "Empty Cache and Hard Reload." A version mismatch detected after boot now self-heals by re-fetching the engine once and rebooting, with no user action required. * **Rich-text editor toolbar gains tooltips and accessible labels.** Every button on the markdown editor toolbar — bold, italic, link, heading, ordered/bullet lists, undo/redo, and the table controls — now shows a hover tooltip and carries an accessible label, so the icon-only controls are clearer on hover and readable to screen readers. **CLI:** * **`kdx sync push --dry-run` now validates against the live server.** A dry-run push previously computed its diff entirely client-side and never exercised the platform's write path, so metadata that would fail server validation still passed a dry run and only failed on the real push. Against a 2026.8 or newer server, `--dry-run` now submits each would-be create/update with the new `?validate=only` request parameter: the server runs the full write path — validators at enforced strength plus slug, foreign-key, and common-rule checks — and persists nothing (returning `200 {"valid": true}`, or an RFC 9457 problem+json `errors[]` body on rejection). Error-severity findings are reported per resource and make the command exit non-zero, so a CI job can gate metadata-repository pull requests on `kdx sync push --dry-run`. Against older servers the dry run automatically falls back to the previous client-side diff. The `?validate=only` parameter can also be used directly against any write endpoint to validate a payload without persisting it. * **Credentials are redacted from CLI diagnostic output.** Error messages and `--debug` request/response logs now mask credential-bearing values and headers — API keys, tokens, secrets, passwords, passphrases, private keys, and connection strings — before they are printed, so a failed apply or a debug session no longer writes live credentials into terminal or CI logs. Redaction is display-only; the actual request payloads sent over the wire are unchanged. * **`-o json` output is now complete and pipe-safe.** List commands run with `-o json` now return every page of results instead of only the server's first page (about 20 rows), and all informational and debug logging is written to stderr. As a result `kdx get … -o json | jq` pipelines are no longer silently truncated or corrupted by log lines mixed into stdout. Pinning an explicit `page` parameter keeps the single-page behavior. * **API discovery cache is keyed per server.** The CLI's cached API discovery spec is now stored per server URL with a 24-hour freshness window. Switching between profiles that point at different servers no longer runs commands (run, describe, validation, resource operations) against another server's cached API shape. **Chat:** * **Copy chat messages in the format you need.** The copy button on a chat message is now a dropdown with three choices: **Copy as Markdown** (the raw message source, the previous behavior), **Copy as Text** (the rendered message as plain text), and **Copy as Rich Text** (formatted HTML with a plain-text fallback, so pasting into Word, Google Docs, or an email keeps the formatting). Rich-text copy falls back to a plain-text copy where the browser clipboard API isn't available. **Activities:** * **AI-suggested activity names no longer surface error text as the title.** The activity naming assistant is now instructed never to place an error message or explanation in the title field, so a naming hiccup no longer produces an activity titled with raw error text. **Platform:** * **Search users in the admin Users view.** The admin **Users** view now has a free-text search box that matches across a user's email, first and last name, job title, and business group, resolved server-side via `GET /api/users?query=`. Existing users are searchable by the newly searchable title and business-group fields immediately. ### Release 2026.7 Rollup of every customer-facing change in the 2026.7 GA release. The headline items span the form-building surface: a new `ifBlank()` formula function and steadier formula recalculation, new data-form controls for AI explanation text and selection hints, and keyboard-driven detach / dock / zoom of the document viewer. Alongside those it brings a queryable **Work Sessions** analytics dataset, a faster task-open path, a wave of **Activities** polish (plan provenance, live progress, a smoother New Activity wizard), and data-modeling safeguards around taxon external names. One breaking change lands in intake — post-upload scripts can no longer overwrite reserved document fields; see the upgrade note. **Data Forms:** * **Render an attribute as an AI explanation callout.** A new `editorOptions.displayAs: "explanation"` renders an attribute as a wrapping AI callout that preserves line breaks and grows to fit its content — for AI rationale text that used to clip inside the fixed-height value box. * **Selected-option hint shows beneath a closed SELECTION dropdown.** A `SELECTION` editor now shows the chosen option's hint (plain text or markdown) below the closed dropdown, so the guidance for the current choice stays visible. It is on by default and opt-out via `editorOptions.showSelectedHint: false`, and is suppressed inside grid cells. * **Drive the document viewer from form shortcuts: detach, dock, zoom.** New `bridge.viewer` methods let declarative form shortcuts control the document viewer the way the toolbar buttons do: `viewer.detach()` pops the viewer out into its own window, `viewer.dock()` re-docks it, and `viewer.zoom("in")` / `viewer.zoom("out")` change the zoom level. **Formulas:** * **New `ifBlank()` formula function.** `ifBlank(a, b, …)` is a blank-aware coalesce: it returns the first argument that isn't blank, and an empty string when every argument is blank. Unlike `ifNull`, it treats an empty or whitespace-only string as blank — so a chain of references falls through to the next candidate — while `0` and `false` pass through unchanged. * **Aggregates over a missing reference settle to zero instead of erroring.** A multi-part reference inside a formula that can't be resolved now yields an empty list rather than failing the calculation, so an expression like `ifnull(sumifs(…), 0)` settles cleanly to `0`. This removes the transient red "formula calculation failed" recalc toast that used to appear while the referenced group didn't yet exist. * **Parent-relative selection formulas resolve on create.** A selection formula that reads a parent value with a `{../Field}` reference now resolves to the real value the moment a row is created, instead of an empty list. Previously a service-bridge-backed selection could fire with an empty argument on a brand-new row; the parent value is now available on first evaluation. **Activities:** * **New Activity wizard loads the full plan before the details step.** When you pick a plan, the wizard now fetches that plan's full body before rendering the details step, so the document-upload panel appears right away — no more going Back and re-selecting the plan once a background refetch lands. * **Activity-plan provenance on tasks and activities.** The task details sidebar now shows the Activity Plan that spawned the task (hidden when a task wasn't spawned from a plan), and the activity dialog header shows a "Plan" chip naming the plan. * **Activities grid progress reflects live step status.** The Activities grid's progress column and counters are computed from live step statuses (completed / running / failed) instead of lagging summary fields, so progress tracks what's actually happening. * **Spawn-time validation errors are surfaced.** When an activity plan fails validation as it's spawned (for example, from a script step or a completed-activity trigger), the specific validation error is now surfaced and recorded instead of the spawn silently rolling back. **Analytics:** * **New "Work Sessions" analytics dataset.** A queryable Work Sessions dataset reports wall-clock time versus actual active/engaged time per task, including an active-ratio percentage — so you can see how long work really takes against how long a task was open. **Performance:** * **Opening a task workspace is faster.** A task's fetches are now parallelized and de-duplicated, the next task in a group is warmed in the background, and the open reuses data that is already loaded — so tasks open noticeably faster, especially when advancing through a queue. **Intake:** * **Post-upload intake scripts can no longer overwrite reserved document fields (breaking).** A post-upload intake script that returns a reserved structural metadata key — `source`, `uuid`, `version`, `labels`, `mixins`, or `statusId` — now fails the upload and rolls back the transaction, instead of silently overwriting the document's structural fields. These keys back dedicated document fields (for example, `source` holds the document's original filename). **Upgrade note.** If an intake's post-upload script sets any of `source`, `uuid`, `version`, `labels`, `mixins`, or `statusId` in its returned metadata, those uploads will now fail rather than silently overwrite. Rename the offending field to a non-reserved key (for example, `documentSource`) before upgrading. **Taxonomy & extraction:** * **Blank taxon external names default to PascalCase over the taxonomy MCP tool.** When an agent creates a taxon with a blank external name via the taxonomy MCP tool, the external name now defaults to PascalCase of the internal name (for example, `invoice_date` becomes `InvoiceDate`). An external name the agent supplies is never overwritten. * **Clear error when a taxon has no external name on export.** Data-object (JSON) export now fails loudly and names the offending taxon when a taxon has no external name, instead of exporting under an empty key or a silent fallback; XML export enforces the same. * **`skipExtraction` taxons are excluded from chunking again.** Taxons flagged `skipExtraction` are once more excluded from document chunking and extraction, restoring the intended contract. ### Release 2026.6 Rollup of every customer-facing change in the 2026.6 GA release. The headline items: a new top-level **Manage** administration area that gathers every org-admin surface in one place, and a **Kodexa Workflow MCP connector** that exposes activities, tasks, and task-groups to claude.ai as a remote OAuth connector. Alongside those, this release brings a wave of review-experience performance work, a broad set of Data Forms V2 shortcut and grid fixes, task-group polish, opt-in user presence & activity tracking, and analytics and orchestration fixes. **Manage:** * **A dedicated Manage area.** Organization administration now lives in its own top-level **Manage** area, alongside Studio, Workflow, and Knowledge, and is visible only to administrators (the `MANAGE` or `PLATFORM_ADMIN` role). It gathers the organization-admin surfaces in one place — Organization Profile, Teams, Document Tags, Secrets, Intakes, Custom Modules, Subscriptions, Model Library, and Concurrency — which previously lived scattered through Studio. * **Organization Profile → Features.** Organization Profile now has a **Features** tab with two org-level toggles: **Strict team matching** (when on, take-next assigns only candidates matched to a reviewer's team; by default it also offers work that isn't assigned to any team when nothing matches) and **Presence tracking** (opt-in per-user presence and activity, off by default — see Platform below). **Workflow:** * **Kodexa Workflow MCP connector.** The platform now exposes its workflow surface — activities, tasks, and task groups — as an [MCP connector](/guides/mcp-connector/index) you add to claude.ai as a remote OAuth connector. It provides 19 tools spanning reads (identity, and listing/reading organizations, projects, teams, members, activities, tasks, task groups, and task statuses) and writes (assign and unassign tasks and task groups, update task status, and add or remove tasks from a group). Enable it with the `mcp.enabled` and `mcp.publicUrl` settings; claude.ai runs the OAuth flow automatically, and an `X-API-Key` fallback covers programmatic clients. Every call is scoped to the calling user's access — the connector grants no broader visibility than the REST API. Activities are read-only over MCP, and a grouped task's assignee is managed through its group. **Performance:** * **Review pages and task groups load faster.** Review tasks open faster, and the project data behind a review session is cached longer so a kiosk reviewer isn't re-fetching it on every task. The task and activity-plan list endpoints gained an opt-in `?view=summary` projection that returns a much lighter payload for high-volume listing. Task groups also prefetch the next task's document in the background, so advancing through a queue opens the next document instantly. **Data Forms:** * **Full document-viewer keyboard shortcuts on data forms.** Data forms can now drive the document viewer entirely from the keyboard — page-step and viewer-scroll alongside rotate — declared through the [declarative form-shortcuts](/guides/data-forms/shortcuts) system. New Bridge methods back this: `navigation.previousPage()` / `navigation.nextPage()` step the viewer a page at a time (clamped at the document edges), and a new `viewer.scroll(direction)` nudges the viewport up/down/left/right. `viewer.scroll` requires a new `viewer` bridge permission; the page-step methods use the existing `navigation` permission. * **Keyboard shortcuts fire reliably on Mac + Chrome.** Declarative form shortcuts had stopped firing for Mac users on recent Chrome; they now trigger reliably again, with no form changes required. * **Grid sort no longer reorders rows mid-entry.** A grid's declared `sort` is now applied once when it first loads a task, then locked — so a newly added row appends in place instead of jumping while an operator is typing. Manual column-header sorting still works, and the resulting row order is remembered per task across refresh and for other reviewers viewing the same task. * **Promote and Copy mark the target as edited.** Promoting or copying a value into a field now shows the same edited-value indicator as a manual edit, and click-to-source navigation still works on the copied field. * **Fewer redundant service-bridge lookups.** Form lookups backed by a service bridge no longer refetch when an unrelated field on the form changes — only a genuine change to the lookup's inputs re-runs it — cutting flicker and load. * **Large service-bridge responses no longer hang a document.** A very large service-bridge response (for example, a long list of selection options) used to stall a document on open; large responses are now handled cleanly. **Document:** * **Detached viewer behaves, and form shortcuts reach it.** Popping the document viewer out into its own window no longer freezes the page when you focus an attribute or tag in the main workspace, and declarative form shortcuts (rotate page, viewer scroll) now take effect in the popped-out window. **Task Groups:** * **Assignee picker populates again.** The assignee picker on a task group could come back empty; it now lists the organization's members and matches on email as well as name. * **Search matches partial terms.** Task-group search on the organization and project lists now matches partial terms anywhere in the name, instead of requiring an exact match. **Analytics:** * **Nested line-item detail reaches the data lake.** The data-lake projection previously populated only top-level data objects, so nested child groups came through empty. Nested detail now projects correctly at any depth, in the same order reviewers see it; top-level projection is unchanged. It populates on newly processed documents — reprocess if you need historical coverage. **Platform:** * **User presence & activity tracking (opt-in, off by default).** Organizations can opt into per-user presence and activity signals from **Manage → Organization Profile → Features → Presence tracking**. It is off by default, and a legal/privacy notice is shown before enabling it. When enabled, the UI reports only derived signals — whether a user is active vs. idle, tab visibility, and how long tasks take to open — sampled roughly every couple of minutes. **No raw input is ever captured**: no keystrokes, no mouse coordinates, no scroll positions. Collection is enforced on the server, so nothing is gathered for an organization that hasn't opted in. **Orchestrator:** * **Orchestrated activity hand-offs authenticate everywhere.** When one activity spawns a follow-up (from a script step or a trigger), the hand-off now authenticates correctly in every environment, so chained activities launch reliably. ### Faster document loads, correct totals on open, and reliable saves This release focuses on speed and dependability across the review experience. Documents open and recalculate dramatically faster, conditional-format highlights and formula totals are now correct the moment a document opens — not only after a reviewer's first edit — and reviewer edits are reliably captured on **Approve**. It also adds a wave of new **Data Forms V2** components alongside a broad set of grid, formula, knowledge, and orchestration improvements. **Faster document loads and edits:** * **Editing large documents is dramatically faster** — Recalculating a document used to issue a separate database query for each attribute, which multiplied quickly — a single edit could cascade into thousands of queries. The same work now takes a handful (one measured edit dropped from roughly 12,800 queries to 2), so edits and their downstream recalculations stay snappy even on large documents. * **Documents recalculate up to \~88× faster** — Documents now refresh incrementally by default — 4.4s down to 46ms in one measured case — with an automatic fallback for unusually complex dependency graphs. * **Tasks open progressively** — The first data form appears as soon as its own document is ready, rather than waiting for every document on the task. Off-screen views defer presentation work — text indexing, summaries, page tags — until you open them, and redundant loading and artificial delays were removed from the open path. * **Switching tasks keeps sessions light** — Moving to a new task releases the documents the new task no longer needs, so long review sessions stay responsive, while a short history cache keeps back-navigation instant. Cleanup is always held off while a save, edit, or popped-out sidecar is still active. * **Rollup totals always match their formulas** — Rollup cards now compute through the same engine — with the same `sum()` semantics — as the formulas they summarize, and refresh together with them, so a rollup and its underlying formula can no longer disagree. * **Leaner edit updates** — Editing an attribute now sends just that attribute to the browser instead of its entire containing group, cutting overhead on documents with large tables. * **Faster, more reliable startup** — The document engine now loads entirely from Kodexa with no third-party CDN dependency at startup, and moves document data across the browser more efficiently. **Correct the moment a document opens:** * **Conditional formatting shows on open — no edit required** (ENGG-5296) — A mismatch highlight (for example, a billed total that doesn't match the summed line items) used to stay hidden until a reviewer touched the document. Highlights now render correctly on first paint, against the stored values as they are. * **Formula totals are correct on open** — Formula values (sums, weights, charge totals) now show their correct computed value the instant a document opens, instead of a stale zero that only corrected itself after the first edit. Documents that are already correct open clean — with no false "unsaved changes" — and any missing formula values are computed and filled in automatically. * **Conditional formatting updates when you delete a row** (ENGG-5265) — Deleting a line item now re-checks any total that depends on it, so a balance that moves into or out of tolerance on a delete reflects immediately. **Reliable saves:** * **Approve always captures your last edit** (ENGG-5291) — Selecting a value and immediately clicking Approve could previously miss that value if its write hadn't finished landing. Save and Approve now wait for in-flight edits to settle first — and show a clear error rather than silently continuing if an edit is stuck. * **Form values no longer revert after Approve** (ENGG-5269) — Editing a form and approving could take two clicks and briefly appear to revert. Edits are now applied to the document before any follow-up step runs, so the first Approve takes effect. * **No edits lost during a save** (ENGG-5292) — An edit made while a save is already in progress is now included in the next save instead of being dropped or needlessly re-sent. **Data Forms V2:** * **`v2:routeTimeline`** (new) — Renders a group taxon's rows as a vertical timeline of numbered stop cards for ordered lists such as a multi-stop shipment route. Stops drag-reorder (the sequence attribute is rewritten to the new position on drop), each card has an inline-editable detail panel, and per-row delete is built in. Additional props: **+ Add stop** and **AI extract stop** (single-record extraction anchored on highlighted document text, with one level of nested sub-object such as an address), a **find-in-document** button that scrolls the viewer to a stop's source span, `show: 'firstLast'` to collapse the middle of a long route behind a "…+N" toggle, `readonly` and `orientation` (vertical/horizontal) props, and an optional location-code badge. The group taxon, sequence tag, type tag, and sub-object path are all configurable. * **`v2:attributeCopyAction`** (new) — A scalar sibling-copy button for form layouts (distinct from the grid-cell copy components): it evaluates the source value through the formula engine and writes it to a sibling target. Like the other copy components it now writes `decimalValue`, so number-typed targets re-render immediately. * **`v2:attributeRowPromote`** (new) — Replaces the per-target chevron columns on candidate grids with a single **Promote to…** dropdown per row, with an optional per-target source-tag override. Promotes are idempotent and now write `decimalValue` so number targets update without tabbing away (ENGG-5217). * **`v2:grid` pagination, sort, and sortable custom columns** (ENGG-5267) — `v2:grid` gained opt-in `pagination` and `sort` props (reviewer grids still default to showing all rows), and custom columns can opt into sorting with a header-matched sort spec. `SELECTION` cells now close their popover on pick instead of staying open until Tab/Escape. * **Form Completeness Gate** (new) — A per-form primitive that lets a task action stay disabled until reviewers have actually looked at the data they should. `v2:tabs` gains `mustView` (flags unvisited tabs with an amber dot and a "N tabs to review" banner), `v2:panel` gains `mustExpand` / `mustScroll`, and outstanding workspace exceptions fold into the same list. Actions opt in with `gatedByCompleteness: true` and surface an info-popover listing what's left; existing forms and actions are unchanged. * **`v2:panel` polish** — New `description` subtitle prop and an `iconColor` tile palette (avatar-style tinted icon) matching the rest of the app, plus header icons on tabs and panels. * **Rotate-page keyboard shortcut** (ENGG-5217) — `alt+R` / `alt+shift+R` (⌥R / ⌥⇧R on Mac) rotate the current page in the document viewer right / left, wired through the [declarative form-shortcuts](/guides/data-forms/shortcuts) system. The server-side `DataForm` schema gained the `shortcuts` field, so forms declaring a `shortcuts:` block now round-trip correctly (the block was previously dropped on save). * **Vertical radio layout** — Attribute editors accept `radioOrientation: 'vertical'` so a `displayAsRadio` field can stack one option per line instead of wrapping across the row (default stays horizontal). **Document formulas & extraction:** * **`sumifs` / `countifs` accept single-row groups** — A formula like `sumifs({Group/Value}, {Group/Use}, true)` previously errored ("first argument must be an array") whenever the source group resolved to exactly one row, surfacing as a transient "Formula calculation failed" toast that vanished once a second row was added. Scalar range / criteria arguments are now coerced to a one-element range, so one-row and many-row groups take the same path. * **String cleaning patterns applied on attribute create** — Taxon `stringExtract` / `stringReplace` cleaning patterns were only applied when an attribute was updated, not when it was first created — so extraction tagging, programmatic adds, the API, and script-driven `setAttribute` all bypassed the cleaning that the same attribute would get on a later edit. The patterns now apply at create as well, and the redundant UI-side normalizer was removed. * **Direct copy/extract value appears immediately** — Adding a value via direct copy now patches the cache optimistically and emits the change event, so the value shows at once instead of only after tabbing out of the field (which used to trigger an expensive full-cache refresh). * **Preprocessor keeps rotation-corrected images** — When auto-orientation rotated a page, the rebuilt processed PDF was falling back to the original (un-rotated) source page and silently discarding the corrected image. The rebuild now embeds the rotated image bytes while preserving the viewer's "already corrected, don't CSS-rotate" signal. **Studio grids & activities:** * **Column-header clicks no longer steal focus into the first cell** (ENGG-5268) — Clicking a column header in an editable data-object grid was moving focus into row 0's first cell and opening its editor. A real user header click now suppresses that redirect while still preserving focus-from-outside and post-add-row refocus behaviour. * **Activities filter inputs persist across remount** (ENGG-5215) — The Activities grid's toolbar inputs (document-family filter, feature facets, quick filter, date range) and the applied filter could fall out of sync on tab switches, breadcrumb navigation, or reload — the inputs looked empty while the list stayed filtered. The toolbar state now persists and is realigned on remount. * **Bulk "remove by source" count stays accurate** (ENGG-5264) — The grid's per-source remove buttons could show a stale row after a bulk delete when overlapping async recounts resolved out of order; a sequence token now discards superseded runs. * **Grid search hydrates from the saved query** — The grid search input now repopulates from the persisted query on load, so a saved search shows its text instead of an empty box over a filtered list. **Knowledge:** * **Escaped image markdown normalized on write** — Image references pasted from Word or HTML into the rich-text editor were serialized as escaped literal text (`!\[\](attachment://…)`) and rendered as plain text instead of the image. The editor now normalizes on edit, and the API normalizes on every create/update — scoped to markdown-typed fields so intentional text escapes (e.g. `\[see codes below\]`) are preserved. * **Readonly-taxon panel shows the extracted value** (ENGG-5253) — The knowledge readonly-taxon panel rendered "—" instead of the extracted value when the stored dependency used the taxon's external-name chain while extraction keys attributes by taxon path — two different namespaces that never matched. The taxon picker now persists the taxon path so the reader matches directly. (Existing knowledge sets need the dependency re-selected and saved to migrate.) **Orchestrator:** * **Activity SCRIPT steps can read `inputs`** — A SCRIPT step body can now read the activity's materialized inputs via the `inputs` JS global (previously only reachable inside `BRIDGE_CALL` request templates — referencing `inputs.X` in a SCRIPT threw a ReferenceError). It always defaults to `{}`, so no `typeof` guard is needed. * **Script-step timeout raised to 60s; failed-step logs retained** — Long enrichment scripts on multi-document inputs were being interrupted at the old 15s limit; the `SCRIPT` step timeout is now 60s. A failed step's log pointer is also no longer discarded by the per-item rollback, so the step-logs view keeps showing the logs for a step that failed — covering `SCRIPT`, `BRIDGE_CALL`, and `AI_PROMPT` steps, which share the log path. ### Post-2026.4.1 patches — copy rules, source badges, multi-instance attribute paths The week after the 2026.4.1 cut shipped a batch of reviewer-workflow polish, new V2 data-form components for promoting candidate values into canonical slots, and the under-the-hood refactor that finally derives `DataAttribute.path` from `parent + tag` everywhere (closing out ENGG-5214). A handful of script-API and formula fixes ride along. **Studio reviewer workflow:** * **Open Task is a primary button that opens a new tab** — The activity-status dialog's "Open Task" affordance was a small text link that closed the dialog on click. It's now a primary `open-in-new` button, opens the task route in a new browser tab via `window.open`, and leaves the activity dialog mounted so reviewers keep their context. * **Activity dialog auto-zooms to the active step** — Opening the plan tab no longer fits the whole graph; it focuses the running step (or the last terminal step on completed plans), falling back to `fitView` only when there's no anchor. A one-shot watcher covers the race where the dialog mounts before the layout finishes. * **Attribute source badge: per-document-type instance numbering** — The badge now shows "Bill of Lading #1, #2, #3..." independent of how many Invoices or other classified pages interleave between them, instead of the preprocessor's global group sequence (which made every BoL in a doc read as the same number). Also fixes an off-by-one between `node.getPage()` (0-based) and the resolver's 1-based classification map that was making attributes anchored to page 2 read as page 1. * **Filter in ag-grid column kebab menu** — Column header menus on all ag-grid surfaces now expose ag-grid's `columnFilter` item alongside sort/pin/etc, gated on `column.isFilterAllowed()`. * **Page-size selector no longer steals focus into row 0** (ENGG-5252) — Clicking the grid's page-size selector previously opened the first row's first-column dropdown on top of the page-size popup. The grid's focus-redirect logic now ignores clicks landing in the pagination chrome. **Data Forms V2 — promotion and provenance:** Several new components and form-level features that work together to support the "promote a candidate value into a canonical scalar/grid slot, with provenance" workflow: * **`v2:attributeCopyButton.relatedCopies`** (new) — The copy button now accepts an optional `relatedCopies: [{sourceTagPath, targetTagPath}, ...]` prop. After the primary copy lands, each related pair runs through the same copy logic, so promoting a weight from a candidate-weights grid into `shipments/shipweight` can carry the matching UOM into `shipments/shipweightuom` in the same click. * **`v2:attributeRowDeleteButton`** + **`v2:gridDeleteBySource`** (new) — Per-row inline delete cell + a sibling toolbar component that surfaces "Remove all `` rows" buttons (one per detected source document) with a confirm dialog before bulk delete. Both share the same `(document_type, group)` resolver the source badge uses. * **`v2:attributeSourceBadge` on `v2:grid`** (new) — `v2:grid` gained a `columns` prop that mounts arbitrary V2 components as per-row cell renderers, alongside the existing taxon-driven columns. The new `v2:attributeSourceBadge` renderer shows one colored pill per distinct `(document_type, group)` tuple of source attributes; click dispatches `workspace.focusTag` for in-viewer navigation. * **`v2:grid` `height` prop honored even with parent data object** — Previously `v2:grid`'s `height` was silently dropped on any grid running under a parent scope (i.e. every form grid), forcing a row-count-based auto-calculation. An explicit `height` now always wins. * **Form-level `copyRules` on DataFormV2** — `DataFormV2` now accepts a top-level `copyRules?: TaxonCopyRule[]`. Cards merge form-level rules with their own per-card rules (card-level wins on `sourceTaxon` conflict). Replaces having to repeat the same copy block on every source panel in forms with many source-document instances. * **Formula-driven `stampAttributes` on copy rules** — `CopyBehaviorOptions` gained `stampAttributes?: Record` for stamping derived audit/provenance attributes onto the copy destination. Values can be literal strings or `${source.idString}` / `${source.parent.blnumber}` / `${source.parent.uuid}` templates. Distinct from `copyAttributes` (which clones existing source attributes with content-tag preservation), `stampAttributes` injects new derived fields with no content backing — useful for stamping `sourceDocumentRef`, `sourceBolNumber`, `sourceDocumentType` onto rows promoted into a canonical grid. **Scripting:** * **`setAttribute` routes numeric taxon types to `DecimalValue`** — `setAttribute` was only matching `DECIMAL`, so writes against `NUMBER`, `INTEGER`, `CURRENCY`, and `PERCENTAGE` taxons fell through and stored the value in `StringValue`. The form's numeric editor then read the typed slot and rendered empty even though `.value` showed the right string. The switch now covers every numeric taxon type the data model acknowledges, and routes `SELECTION` / `URL` / `EMAIL` / `PHONE` / `SECTION` / `DERIVED` into the `STRING` case explicitly. * **`addAttribute` auto-resolves type from the taxon** — `addAttribute`'s `TypeAtCreation` resolution now follows the same precedence as `setAttribute`: `opts.type` wins, then runtime `TaxonResolver`, then the document's cached taxonomies, then inference from the supplied typed-value field. Previously scripts calling `addAttribute({tag:"chargecode", stringValue:"DSC"})` logged "filled" but the resulting attribute couldn't bind to form `SELECTION` dropdowns, the formula evaluator, or conditional-format rules. Choice between `addAttribute` and `setAttribute` is now about find-or-create semantics, not type safety. * **`path` opt on `addAttribute` is ignored with a warning** — Paths have been derived from `parent + tag` since ENGG-5214; the `path` opt was silently dropped before, now `log.warn`s so callers can see they should drop it. `SPEC.go` no longer lists `path` as a valid option. * **Script dirty tracking across nested data objects** — `ScriptDataObject` traversal now propagates `parentDoc` so attribute changes made on a nested object correctly mark the document dirty for downstream persistence. **Document formulas & extraction:** * **Empty group refs return an empty list** (ENGG-5227) — Formulas like `sum({Accessorials/ChargeAmount})` evaluated against a parent whose child group had no instances (e.g. all accessorial rows deleted) previously returned a `"reference could not be found"` error, and the recalculator skipped the write — leaving the previously computed Sum of Line Items on screen instead of zeroing it. The path resolver now returns `[]` for the empty-group case (matching the sibling branch's existing semantics that `sum/min/max/avg` rely on), and the formula explain panel stops surfacing "Reference X/Y could not be found" for empty groups. * **Missing-reference errors null out the stored value** (ENGG-5227) — When `EvaluateFormula` returns a new `MissingReferenceError` (distinct from syntax/runtime errors), the recalculator now nulls the attribute's stored value fields and persists. Transient evaluation failures still preserve the previous value so they don't blank legitimate output. * **`DataAttribute.path` is derived everywhere** (ENGG-5214 close-out) — The `path` column has been dropped from `kddb_data_attributes`; every read now routes through `GetPath()` which composes `parent.path + "/" + tag` on the fly. Extraction, move, copy, formula reactivity, and the WASM serializer have all been migrated. External consumers reading attribute path from the JSON envelope are unaffected — the field is still emitted with the same value, just computed instead of stored. (The Go `DataAttribute.Path` field and `pathOverride` argument on `CopyDataAttribute` have been removed.) **Knowledge:** * **Knowledge feature search hits `extendedProperties` and numeric values** — The "Filter by feature" popup's `?query=` search runs against the `search_text` column, which previously only indexed slug + string-valued `Properties`. Human-readable labels stored in `ExtendedProperties.name` were never matched, and numeric scalars (e.g. `shipperCode: 1540`) were silently dropped — typing "JSP" returned zero matches even though chips render the full "JSP International - Legacy" name. `BuildSearchText` now walks both `Properties` and `ExtendedProperties` recursively, stringifying every string/number/bool scalar; a companion migration rebuilds `search_text` for every existing row so environments don't have to re-save each feature. **Orchestrator:** * **`loadTaxonomy` reads structured metadata, not stale `yaml_source`** — The script-engine adapter was reading `kdxa_taxonomies.yaml_source` and re-parsing YAML. `yaml_source` is a round-trip snapshot that drifts behind the structured `metadata` column whenever a client (`kdx sync push`, platform PUT) updates the taxonomy without rewriting the YAML. Scripts then validated taxon paths against the stale text, surfacing as `"taxon path X does not exist in taxonomy Y"` in plans that referenced recently added taxons. The adapter now reads `metadata::text` and parses the JSON via `yaml.v3` (which handles JSON as YAML 1.2). ### Release 2026.4.1 Rollup of every customer-facing change between 2026.4 and 2026.4.1. The largest items: a new **CDC Data Lake** that mirrors every metadata change and document delta into S3, a **Page Groups** picker in the document viewer that lets reviewers jump straight to each classified section of a multi-document PDF, **declarative keyboard shortcuts** in V2 data forms, and a reshaped **Take Next** API that finally distinguishes "nothing to do" from "filtered out by team" from "lost the race." A long tail of validation, formula, and extraction fixes ride along. Already documented separately: [activity-plan scripts can spawn follow-up Activities](#activity-plan-scripts-can-spawn-follow-up-activities) (the 2026-05-26 entry — it's also part of this release). **Studio reviewer workflow:** * **Page Groups picker on the document viewer** — A new `file-multiple-outline` button in the spatial toolbar opens a popover listing each classified physical document on the open file (e.g. `Invoice — Pages 1–3`, `Delivery Receipt — Pages 4–6`), with a taxon-colored swatch and click-to-navigate to the start of each group. Built on top of the preprocessor's per-page classifications + group UUIDs (below) and the existing `tagMetadataMap` so the labels and colors match what the rest of the UI shows. * **Spatial toolbar cleanup** — The Show Advanced (`wrench`), Developer Info (`i`), and Page Groups buttons now sit before the find-text input so narrow document panels don't wrap them onto a second line. * **Kiosk: step-out confirm before fetching next work** — Reviewers leaving a kiosk task now get a confirm prompt before the next task is auto-claimed, preventing the accidental "I just finished but the next one already opened" race. * **Require a comment on task-template actions** (new) — Task-template actions can now declare `requireComment: true` (with an optional `commentPrompt` string). When the reviewer clicks the action, a shadcn dialog opens for a mandatory comment **before** the action runs; cancelling aborts the action with no partial state. The comment rides the existing `/api/batch-update` payload as `task.completedActionComment` and is persisted server-side as a `COMMENT` task activity tagged with the action's UUID so the timeline can link the comment back to the action that produced it. See [Requiring comments on actions](/guides/task-templates/requiring-comments-on-actions) for the full recipe. * **Take Next API: EMPTY envelope replaces 204** (ENGG-5208) — `POST /api/tasks/assign-next` previously returned a bare 204 in three distinct situations: nothing queued, filtered out by team, or claim race. The endpoint now always returns 200 with a typed envelope and a reason code (`EMPTY`, `FILTERED_OUT`, `CLAIM_LOST`) so the UI and external integrations can react appropriately. The `projectId` parameter is also now required and the team filter is enforced for platform admins (ENGG-5209). **Integrators that relied on the old 204 will need to update.** * **Task lock decoupled from document family lock** — A task's lock no longer takes out the entire document family. Multiple reviewers can now work different tasks against the same document concurrently when the task-status policy permits. Existing locking behaviour is preserved for status types that explicitly opt into `lockDocumentFamily`. * **Faceted filtering across grids** — Tasks, Task Groups, Activities, and other primary grids now support faceted filtering by document-family feature + the family itself, surfaced through a shared `KodexaGridFacetBar` component. Saved filter state survives navigation. * **Activities grid: file-name filter clears on task completion** — Completing a task no longer leaves a stale file-name filter on the activities grid. * **Team slug surfaced in grids and forms** (ENGG-5185) — Team slugs are now visible in team listings and editable in create/edit forms, matching the slug-everywhere convention used by other resources. **Data Forms V2:** * **Declarative keyboard shortcuts** (ENGG-5244, new) — V2 forms can now declare a `shortcuts:` array at the top level. Each entry binds a key combination to a named script and is registered under a per-form scope, so a form **resets all of its shortcuts every time it mounts** with no manual cleanup. See [Keyboard Shortcuts](/guides/data-forms/shortcuts) for the schema, lifecycle semantics, and worked examples. ```yaml theme={null} shortcuts: - key: "control+1" description: "Jump to Invoice" scriptRef: gotoInvoice - key: "control+t" description: "Focus invoice total" scriptRef: focusTotal ``` * **Bridge navigation actions** (ENGG-5244) — `kodexa.navigation` (the bridge namespace shortcut scripts call) now has real `setPage(page, documentFamilyId?)`, `getCurrentPage(...)`, and `getPageCount(...)` methods alongside the existing `focusAttribute(...)`. Spatial methods are 1-based externally and route to the document viewer for the form's first document family by default. See [Bridge API & External Services → kodexa.navigation](/guides/data-forms/bridge-api#kodexa-navigation). * **Markdown editor scroll restored** — Internal scroll is back on the markdown editor, capped at 1.5× `--editor-height` so the editor no longer takes over the page on long content. **Document preprocessing & extraction:** * **Preprocessor: canonical taxon-path tagging** (ENGG-5240) — When the preprocessor is configured with a `taxonomy:` option, the LLM-returned `document_type` labels (e.g. "Bill of Lading") are translated to canonical taxon paths (`billoflading`) before pages are tagged. This makes the preprocessor's page tags match what downstream `kodexa/llm-taxonomy-model` writes, eliminating the duplicate `Invoice` + `invoice` tag instances that were appearing in reviewer-facing UI. The original LLM-returned label is still kept as a `preprocessor.document_type` feature. Existing plans without `taxonomy:` set are unchanged. * **Preprocessor: multi-page group on the tag** — Page tags now carry `groupUUID` (deterministic per `source_page.group`) and `value` (the sequence within the group), so consumers reading tags alone can reconstruct which pages belong to the same physical document. The existing `preprocessor.group` and `preprocessor.sequence` features are kept for backward compatibility. * **Spatial copy: cluster by line before sort** (ENGG-5205) — Multi-line copies from the spatial viewer were occasionally returning words in the wrong order when lines overlapped on the Y axis. The spatial sort now clusters by line first so copied text reads in natural order. * **Default copied attribute path** (ENGG-5214) — Copying an attribute to a different parent now defaults the new path / tag to the destination object's own path, instead of carrying the source's path forward. * **Formula reactivity scoped to lineage owners** (ENGG-5195) — Conditional-format and formula reactivity now routes to the lineage-scoped owner instead of fanning out across the document, which removes the perceived "everything recomputes" lag on edits in deep tag hierarchies. * **Conditional formats batched per owner** — `EvaluateAllConditionalFormatsBatch` is now gated on the conditional-format owner set, fixing intermittent integration-test timeouts and reducing wasted work in kodexa-ui. * **Reactive validation group rules + auto-derived exception path** — Group-level validation rules now re-evaluate reactively when their inputs change, and exception paths are derived automatically so authors don't have to maintain them by hand. * **Knowledge-feature taxon refs use canonical ExternalName chain** (ENGG-5221, ENGG-5222, ENGG-5223) — Several long-standing inconsistencies between the path used to look up a taxon and the path stored on dependency graphs / init-paths / refresh validation have been aligned to the canonical `ExternalName` chain. Symptoms that should now stop: stale validation results after a tag refresh, formulas resolving to the wrong taxon when org slugs were nested, init paths failing to find selection options after an external-name change. The WASM attribute-value bridge also now returns type-aware values (numeric, boolean, date) instead of always-string. * **Absolute formula refs require leading slash** (ENGG-5221) — Formula references intended as absolute (across taxonomies) must now start with `/`. Existing absolute refs that already had the leading slash are unchanged; ambiguous refs that worked accidentally before will now be treated as relative. **CDC Data Lake (new service):** Kodexa now mirrors every metadata change and document delta into an S3-backed data lake, structured for direct query by analytics tools. The lake is on by default for new deployments; existing deployments can opt in via the standard `STORAGE_LAKE_BUCKET` configuration. * **Envelopes** under `entities/` mirror every `kdxa_metadata_audit` write (an audit row is created for every create/update/delete of AbstractMetadata-managed resources — task templates, knowledge sets, activity plans, etc.). * **Activity envelopes** under `activities/` mirror every activity status transition, with rolled-up step errors and deferred publish until the originating database transaction commits (ENGG-5231) so no half-applied state leaks to the lake. * **Step / step-document envelopes** carry full `errorDetails` (ENGG-5235), so analytics dashboards can surface the actual exception message and stack from a failed step without re-querying the source database. * **Content-objects** under `content-objects/` are populated from the KDDB projection on `CONTENT_CREATED`, giving the lake the post-Apply view of every data object the orchestrator persisted. * **Batch context** ships task-template ref + work-session context on every batch envelope (ENGG-5202 phases 1–3), so audit / analytics consumers can correlate every change back to the user session that produced it. * The schema is intentionally append-only; rebuild scripts and a `CHANGELOG.md` under `kodexa-cdc-lake/` document the supported event shapes and projection logic. **Audit log + metadata refactor:** * **AbstractMetadata audit log** (ENGG-5150) — A `kdxa_metadata_audit` table now records every create/update/delete on AbstractMetadata-managed resources (task templates, knowledge sets, knowledge items, knowledge item types, knowledge feature types, knowledge features, activity plans, prompts, data forms, data definitions). Each row carries the actor, source IP, work-session ID, and a JSON snapshot of the change. Append-only enforced at the DB level. * **Generic slug auto-generation + uniqueness** — Any AbstractMetadata resource created without a slug now gets one auto-generated from its name, and a `UNIQUE (organization_id, slug)` index is enforced on ten audited tables. Nested org refs are resolved on every create/update so manifests can reference an org by ID, slug, or full URI. * **Knowledge create/update polish** — Knowledge Item creates now populate `slug` and `knowledge_set_slug` correctly. Duplicate-key responses now return a clear 409 with the conflicting field instead of a 500 (PX-10). `name` ↔ `set_name` and `type` ↔ `set_type` are mirrored in `BeforeCreate` / `BeforeUpdate` so legacy clients writing one form see consistent reads on the other. **Analytics datasets:** * **Document-family features in tasks / groups / links datasets** — The `tasks`, `task_groups`, and `task_group_links` datasets exposed via the analytics view now include each row's owning document family features (knowledge features attached to the family), so reports can group / pivot by feature without joining a separate feed. **Auth & user sessions:** * **Email extraction from non-standard claims** (ENGG-5203 phase A / A.1) — Login no longer fails for users whose ID token lacks a standard `email` claim. We now check standard OIDC claims, namespaced Auth0 claims, and finally fall back to `kdxa_users` lookup by sub. * **Work-session start time on /api/batch-update** (ENGG-5203 phase B) — The UI now sends `userWorkSession.startedAt` (the actual session start) instead of the dead `transactionStart` field that defaulted to the current time. Audit rows that previously showed every change as starting "now" now carry the correct session boundary. **kodexa-ui platform polish:** * **Pop-out keeps main tab as WASM owner** — Opening a document in a pop-out window no longer transfers WASM ownership; the main tab stays authoritative and the pop-out reads through. * **Sidecar heartbeat tightened** — Heartbeat now counts only actual ping responses, not noise from other event types, so disconnect detection is more reliable on slow networks. ### Activity-plan scripts can spawn follow-up Activities A SCRIPT step's return value can now include a `nextActivity` block that asks the platform to start another Activity Plan when the **current Activity** completes. Use this to chain related workflows — intake → extraction, classification → enrichment, validation → posting — without an external orchestrator. * **Return shape**: alongside `action` and the existing `features` array, scripts can now return: ```javascript theme={null} return { action: "approve", nextActivity: { activityPlanRef: "activity-plan://acme-finance/billing-extraction", inputs: { reviewedBy: org.userEmail }, documentFamilyIds: [families[0].id], features: [ { documentFamilyId: families[0].id, featureId: "fc_billing_ready" } ] } }; ``` * **Deferred spawn**: the new Activity starts when the current Activity reaches `COMPLETED`, not at the moment the script returns. Multiple SCRIPT steps in one plan may each emit their own `nextActivity` and they fan out at completion in step insertion order. * **Same-project only** for v1. The target plan must already be bound to the current project via `project_resources`. Cross-project spawns are rejected. * **Inheritance**: when `documentFamilyIds` is omitted, the spawned Activity inherits the source Activity's document families. Server-controlled `triggerMetadata` (`sourceActivityId`, `sourceStepId`, `sourceActionUuid`, `sourceProjectId`) is always merged in last so the audit trail can't be spoofed; the spawned Activity's `triggerKind` is `ACTIVITY_COMPLETED`. * **Soft failure**: spawn errors (missing plan, FGAC denial, input validation failure) leave the source Activity completed and record the reason in `script_result.nextActivityError`. On success, `script_result.nextActivityId` points back to the new Activity. * **Feature attachments**: `nextActivity.features` is applied to the named document families immediately, before the spawn fires, so the new plan's templates and scripts see them via the existing template context. See [Spawning a Follow-Up Activity](/guides/activity-plans/script-steps#spawning-a-follow-up-activity) for the full reference and the [Script Steps deep API reference](/guides/script-steps) for the runtime contract. ### Task Groups, the New Activity experience, and knowledge in scripts This release rounds out the Activity-centered workflow model. Reviewers can now batch related work into Task Groups; the New Activity surface replaces the old New Task entry points; activity plans get a richer editor; and activity-plan scripts gain a first-class `knowledge` global. **Task Groups (new):** * **Bundle tasks into a single assignment unit** — Select related tasks from any Tasks tab and use **Create task group** to wrap them under one name, description, priority, status, assignee, and team. Members work the group as a unit instead of picking off individual tasks. * **Groups tab and kiosk Take-Next** — Each project and organization now has an always-on **Groups** tab. Reviewers can claim the next eligible group from the kiosk widget without manual selection, and the workspace opens a guided drawer with auto-advance between member tasks and a completion summary. * **Slide-over detail panel** — Clicking a row opens a side panel that manages status, assignee, member tasks, history, and delete in one place. The Tasks grid now shows a clickable group chip on grouped rows so you can jump straight to the panel. * **Permission-gated** — The Groups tab and Take-Next kiosk action appear only when the viewer's role permits them. **New Activity experience:** * **New Activity is the primary CTA** — Workflow org-home and project-home Activities tabs now lead with **New Activity** (replacing the old "New Task" button). Project Home adds a split-button so you can start an Activity or kick off a Job Run from the same control. * **Two-step Activity wizard** — Pick a project, pick an activity plan, then fill in title, description, priority, and documents on the same form. Document upload is inline; AI naming proposes a title from document content when enabled. * **Activity Plan editor** — Visual editor with tabs, schema-driven properties panel, manual layout with persisted positions, slug shown under each step's name, action-qualified edges, badges auto-generated from module refs, and per-step **Cancel** and **Reprocess** actions. Plans get a delete action consistent with task templates. * **Document Families on Activities** — Activities own their document families directly. The new `GET /api/activities/{id}/steps/{stepId}/document-families` endpoint returns the documents touched by a step. CREATE\_TASK steps automatically copy the activity's document families onto the task they materialize, so review surfaces always have the right context. `PATCH /api/activities/{id}/steps/{stepId}` is also available for step updates. * **Activity steps in the API** — `GET /api/activities/{id}` now embeds the full step list in the response. **Knowledge in activity-plan scripts:** * **`knowledge` global in script runtime** — Activity-plan SCRIPT steps and routing scripts now expose a `knowledge` object scoped to the script's permitted document families: * `knowledge.getFeatures(familyId)` and `knowledge.getItems(familyId)` return the raw feature and item instances on a family * `knowledge.featuresByType(familyId, featureTypeRef)` and `knowledge.itemsByType(familyId, itemTypeRef)` filter by type * When the script operates on a single family, `knowledge.features` / `knowledge.items` return that family's data directly * Features and items are enriched with their full type definitions and frozen, so scripts can read everything they need without separate lookups and can't accidentally mutate the source data * See the [Scripting — Knowledge bindings](/guides/scripting/index) guide for usage patterns. **Knowledge resolution by org slug:** * Knowledge Sets, Knowledge Items, Knowledge Item Types, Knowledge Feature Types, and Knowledge Features can now be created, updated, and referenced by `orgSlug` + `slug` (or `itemTypeRef` / `featureTypeRef`) on every create and PUT path. Manifests no longer need internal UUIDs to round-trip. * The resource resolver supports a new `knowledge-item://` URI scheme. * `/api/knowledge-features?query=` now actually searches feature name, type name, and description (previously narrowed the result set incorrectly). **Document family activities:** * **Step dots on document family cards** — Document family cards now show a row of dots indicating each activity step's state for that document. Hover for the step name and status; click an activity to open it. The same display appears in document grids via the activity cell renderer. * Per-document activity data is batch-fetched, so even large document lists open without per-row API churn. **Studio and workflow navigation:** * **Workstreams tab removed** — Project navigation no longer shows the legacy Workstreams tab. * **Resources panel** — The Studio resources panel now surfaces task templates and activity plans alongside other project metadata. * **Org-level activities** — Activities filter by `lifecycleState` instead of the removed `status` field; the old org-home Activities tab variant has been retired in favor of the unified grid. **Knowledge UI improvements:** * Feature palette includes a search box and caps at 30 visible items at a time, so palettes with many feature types stay usable. * The Applications tab shows a document family + content object inspector so you can drill into the source content behind a knowledge feature. * Knowledge Features get a reworked card layout and detail overview, with feature properties and `extendedProperties` exposed to AI naming templates. **Data form and document grid polish:** * **Clearing a SELECT now nulls the value** — Clearing a selection-type attribute properly clears its underlying value (not just the display string), so dependent formulas, conditional formats, and validators react correctly. * **"Edited Value" indicator on clears** — When you clear an AI-extracted value, the form now marks the cell as user-edited so the next extraction pass won't silently overwrite it. * **Better grid editing feedback** — Invalid Number cells show a focus ring while focused; grid Add no longer freezes the row gate; autocomplete dropdowns match by substring (not just prefix); SELECT popovers stay open across ag-grid cell destroy/recreate. * Formulas re-evaluate when the data-object cache refreshes, so derived values follow upstream edits without a manual refresh. **Performance:** * **Faster project load** — Project resources are now fetched in bulk with deduplicated module wiring, and a no-op assistant-connections endpoint has been removed from the load path. **Agent runtimes:** * Channel-scoped workspace blob store keeps chat attachments and drafts per channel. * Conversations continue across sessions via SessionStore continuation, so picking a chat back up doesn't reset context. * Task-scoped chats now see the task's document store refs. * Module refs can use a `{org}` placeholder so plans are portable across organizations. **Notes for API consumers:** * Activity lifecycle now lives on `Activity.lifecycleState` (`DRAFT`, `RUNNING`, `PAUSED`, `COMPLETED`, `CANCELLED`, `FAILED`). The previous `Activity.Status` field has been retired; existing data is migrated automatically. Update integrations that read the old field. * Activity step `kind` is now `type` across the database, API, and UI (`EXECUTION`, `SCRIPT`, `BRIDGE_CALL`, `CREATE_TASK`, `APPROVAL`, `LLM`, `AGENT`). Existing rows are migrated; new activity-plan YAML should use `type`. * `/api/plans` has been retired now that the UI runs entirely on `/api/activities`. The OpenAPI spec has been regenerated with 20 previously-undocumented routes added — point external integrations at `/api/activities` and refresh generated SDKs. ### Assistant connections removed **Breaking change.** Assistant connections and the connection-driven event router in the orchestrator have been removed across the platform. * `/api/assistant-connections` REST endpoints (GET, POST, PUT, DELETE) have been removed. Clients calling these endpoints will receive 404. * The `AssistantConnection` and `ProjectAssistantConnection` types are gone from the Python SDK and the generated TypeScript models. * Domain events (document family, channel, batch, content) are still emitted by producers, but the orchestrator no longer routes them to assistants via connections — those events are now drained from the SQS queue without action. * Activity-related events (`PLAN_CREATED`, `TASK_UPDATED`, `REPROCESS`) continue to flow through the orchestrator's plan advancer, trigger evaluator, and reprocess handler unchanged. * The `kdxa_assistant_connections` database table is retained but emptied by migration; nothing reads or writes it. * In the CLI, `kodexa-cli pull` no longer writes `assistant-connections/` directories, and `kodexa-cli apply` warns and ignores any legacy on-disk `assistant-connections/` content. * The Studio data-flow editor has been removed; without connections the editor had no edges to render. The triggers model (introduced as part of the Activity refactor) is the planned replacement for event-driven assistant invocation. The trigger evaluator is already wired into the orchestrator for `task_status_changed`; broader trigger-based routing will follow in a subsequent release. ### Activity-centered workflows, reactive validation, and richer operations tooling This release continues Kodexa's move to an Activity-centered model for document-heavy business processes. Activities represent the business process run. Tasks represent the human review, correction, approval, or exception work that happens inside that run. **New Features and Improvements:** * **Activities as first-class workflow runs** — Activity Plans, Activity runs, and Activity steps are now the primary model for orchestrating automated work, human review, integrations, and audit history. Activity detail APIs now include step data so user interfaces and integrations can show the run and its materialized work together. * **First-class Service Bridge steps** — Activity Plans can call configured Service Bridges as workflow steps. Request method, URL, body, response body, and result details are captured with the step so teams can review and troubleshoot external system calls without leaving the workflow. * **Activity authoring improvements** — The flow editor now supports manual layout, persisted node positions, connection labels, context menus, compact step palettes, Service Bridge nodes, schema-driven configuration, and project/intake bindings. * **Per-document execution visibility** — Execution details now surface at the document-family and step level, including active or failed steps, logs, execution IDs, and error details for faster operational review. * **Reactive Data Definition validation** — Changes to data definitions, selection options, conditional formats, validation rules, and document data now trigger scoped revalidation. Matching exceptions are created, closed, or reopened as the document moves in and out of compliance. * **Richer exception review in Data Forms** — Review surfaces now show more complete exception detail, filter open exceptions consistently, support override metadata and support article references, and can scope actions to specific exception paths. * **Knowledge snapshot review** — Knowledge Sets now include snapshot panels, feature chips, and visual diffs so teams can review knowledge changes before and after updates. * **CLI and GitOps improvements** — `kdx sync` now preserves project-resource links more reliably, records sync state for task templates, annotates manifest project keys with readable names, sorts legacy associations deterministically, and supports pre-package `metadata.build` hooks. A new `kdx secret` command adds organization secret management from the CLI. * **Document handling improvements** — Preprocessing can correct document rotation and summarize documents. The document viewer now handles processed PDFs and rotated spatial overlays more consistently. * **Operational observability** — Platform errors, upload failures, subscription failures, and Activity/execution status changes now emit sanitized structured events for better monitoring without exposing sensitive request data. **Configuration Note:** Module runtime configuration is now standardized on `metadata.moduleRuntimeParameters`. Update any module YAML still using `modelRuntimeParameters`. ### Script API consolidation, document-resident taxonomies, and shared script helpers Three related changes ship together. The first is breaking; the rest are additive and unlock smaller, more maintainable scripts. **1. Script API consolidation (breaking change)** The JavaScript API for intake scripts, planner script steps, taxonomy event subscriptions (browser and Python contexts), and module scripts has been modernized onto a single canonical surface. **Scripts written against the legacy API need updating.** * **Method names are now camelCase.** `currentObject.GetFirstAttributeValue("foo")` → `currentObject.getFirstAttributeValue("foo")`. Same rule for every method on `currentObject`, `document`, attributes, and content nodes. Scripts using PascalCase fail with `TypeError: Object has no member 'GetFirstAttributeValue'`. * **`bridge.data.*` is removed.** `bridge.data.setAttribute(currentObject.GetID(), name, value)` collapses to `currentObject.setAttribute(name, value)`. `bridge.data.getAttribute(...)` collapses to `currentObject.getFirstAttributeValue(name)`. Calling the removed surface throws `bridge.data is undefined`. * **`log()` is now structured.** Replace `log("info", "msg: " + x)` with `log.info("msg:", x)`. `log.warn`, `log.error`, and `log.debug` follow the same variadic shape (args joined with spaces, like `console.log`). Calling `log()` positionally throws `log is not a function`. * **`getType()` on content nodes is renamed to `getNodeType()`.** Scripts iterating selector results and reading the node type need a one-word find/replace. * **`serviceBridge.list()` is removed.** Discovery now lives in the platform admin surface; scripts reference bridges by known `"orgSlug/bridgeSlug"` refs. New helpers that reduce boilerplate: * **`doc.getOrCreate(path)` / `obj.getOrCreateChild(path)`** — find-or-create idempotent. Replaces the `findFirst → if null create` pattern. * **`obj.setAttribute(name, value)`** — find-or-create on an attribute and write a typed value in one call. * **`obj.payload({ key: "attrName", ... })`** — extract a JS object suitable for `serviceBridge.call` payloads. Missing attributes default to `""`. * **`taxon.optionLabel("taxonName", value)`** — look up the human label for a selection-option value. Replaces hardcoded label maps in event scripts. * **Default `path` and `ownerUri`** — write methods (`addAttribute`, `copyAttributeFrom`, etc.) derive `path` from `parent.path + "/" + tag` and `ownerUri` from the runtime's script context. Specify only when overriding. Migration: every change is a mechanical find/replace. The full reference is in the [Scripting guide](/guides/scripting/index). Tenants with custom scripts stored in the platform (intake scripts, planner scripts, taxonomy event subscriptions edited via the UI) should update those scripts before upgrading. The browser WASM bundle is bumped to API version 2; the existing reload prompt detects mismatched bundles and asks users to reload. **2. Document-resident taxonomy resolution** `setAttribute(name, value)` now resolves the attribute's type from the document's cached taxonomies — scripts no longer need to declare `type: "SELECTION"` (or any other type) on writes. The resolution chain is: VM-supplied resolver → document's cached taxonomies (loaded by extraction) → fallback inferred from the JS value's runtime type. ```javascript theme={null} // Before — explicit type declaration needed shipment.addAttribute({ tag: "shippercode", value: "142600", stringValue: "142600", type: "SELECTION" }); // After — type resolved from the document's taxonomies shipment.setAttribute("shippercode", "142600"); ``` Scripts can also extend the document's in-scope taxonomies at runtime: ```javascript theme={null} document.addTaxonomy(myTaxonomy); // subsequent setAttribute calls see it ``` **3. Script sidecars on task templates** Task-template SCRIPT items can declare a list of module refs whose JS scripts are pre-loaded into the runtime before the main script body runs. This lets multiple templates share helper functions instead of inlining the same code in every block. ```yaml theme={null} - type: SCRIPT name: Initial Enrichment scriptSidecars: - acme-finance/invoice-helpers script: | // copyNewAttributes is provided by the sidecar — no inline definition needed copyNewAttributes(target, source, [{src: "amount"}, {src: "date"}]); ``` Refs use the standard `"orgSlug/moduleSlug"` form. Module declarations (loaded as functions/vars) land on the global scope and behave like any other helper in the main script. **4. CLI: `metadata.scriptPath` for module YAML** Module YAML can now reference an external `.js` file via `metadata.scriptPath`; `kdx sync push` reads the file and inlines it into `metadata.script` at deploy time: ```yaml theme={null} # models/invoice-helpers.yml orgSlug: acme-finance slug: invoice-helpers type: store storeType: MODEL metadata: scriptLanguage: javascript scriptPath: ../scripts/invoice-helpers.js # editor-friendly source of truth ``` Inline-script-only modules (the sidecar shape) don't need a `contents:` block — `kdx` skips the implementation zip-and-upload path entirely and logs `📜 Module {slug} is inline-script only — skipping implementation upload`. Existing modules with inline `metadata.script` or a populated `contents:` block are unaffected. ### Platform Updates — Mid April 2026 **New Features:** * **Markdown Image Paste** — Markdown editors now support Cmd/Ctrl+V image pasting and drag-and-drop. When editing knowledge items, pasted images are automatically uploaded as knowledge set attachments and referenced via portable `attachment://` URLs. In standalone contexts, images are base64-encoded inline. * **Selection Option Formulas** — Data definitions now support formula-driven selection options with a formula mode toggle and extended fields, enabling dynamic dropdown values computed from other attribute values in the document. * **Exception Override** — Data forms now support overriding validation exceptions directly from the workspace with WASM persistence and a form-scoped exception details panel. * **Tab Key Grid Navigation** — Tab key now navigates between input fields in grid cells for faster data entry. * **Filterable Knowledge Tables** — Markdown tables rendered in knowledge sections now include a search bar for filtering rows. * **Service Bridge Status Override** — `postReplyScript` can now override the HTTP response status code returned by a service bridge endpoint. * **Intake and Label URI Schemes** — The API resolver now supports `intake://` and `label://` URI schemes for resource resolution. **CLI Improvements:** * **YAML Round-Trip Preservation** — `kdx sync pull` now preserves YAML comments and formatting using a new `yamlpatch` engine. Pushed resources include the original YAML source for lossless round-trips. * **Smart Discover Merge** — `kdx sync pull --discover` intelligently merges newly discovered resources into existing YAML files, preserving comments and manual edits. * **Attachment Download** — `kdx sync pull` now downloads knowledge set attachments alongside metadata. * **Discover Directory Flag** — New `--discover-dir` flag sets the `metadata_dir` in the generated manifest during discovery. * **Conflict Detection** — Sync state tracking detects when remote resources have changed since the last pull, with a `--force` flag to override conflicts. * **Cross-Org Push** — `kdx sync push` rewrites organization slug references in YAML values when pushing to a different organization. * **Dependency-Aware Push** — Resources are pushed in dependency order to avoid reference errors during deployment. * **Parallel Pull/Push** — Sync operations now run in parallel for faster execution. * **Legacy Server Compatibility** — Improved compatibility with older Kodexa servers including paginated API responses, case-insensitive slug matching, and fallback resource fetching. **Platform Improvements:** * **Optimistic Locking** — Tasks, document families, and batch updates now use change-sequence-based optimistic locking to prevent concurrent modification conflicts. * **Service Bridge Observability** — Service bridge proxy calls now emit Datadog events with request and response body details. * **Selection Formula Scoping** — Selection formula evaluation is now scoped to ancestor data objects for more predictable results. * **Auto-Select Single Option** — Dropdown fields with a single available option are automatically selected when the field is empty. * **Attribute Editor Consistency** — Attribute editors now emit updates on blur rather than on every keystroke, reducing unnecessary saves. * **Readonly Field Styling** — Readonly form fields are now visually distinguished with a border and muted background. * **WASM Binary Size** — The WASM binary has been reduced by 20.8% by removing unused expression engine dependencies. * **Detached Sidecar Toolbar** — The detached sidecar window now includes the full document toolbar and page navigation. **Bug Fixes:** * Fixed service bridge calls not re-firing when dependency values changed. * Fixed selection options formula toggle not responding to clicks. * Fixed missing formula attribute references causing errors instead of resolving to nil. * Fixed document store table view checkbox selection and row click behavior. * Fixed chat session loading failing when reopening an existing conversation. * Fixed LLM JSON response preprocessing to handle truncated or malformed responses. * Fixed task locking and auto-lock behavior in plan advancement. * Fixed form freeze, dropdown UX glitches, and validation timing issues in data forms. * Fixed inline grid editing focus loss when attribute data updates arrived. * Fixed extracted selection values being accepted even when not in the dropdown options list. * Fixed GoJA script runtime not persisting `AddChild`, `SetTaxonomy`, `SetPath`, and attribute mutations to the document. ### Platform Updates — Early April 2026 **Improvements:** * **Scoped Document Reprocessing** — `POST /api/document-families/{id}/reprocess` now accepts an optional `assistantIds` request body. When omitted, the platform auto-detects prior assistant contributions and reprocesses the family asynchronously. * **Document Family Feature Filters** — Document families can now be filtered through attached knowledge-feature relationships such as `features.id=='...'` and `features.slug=='...'`. * **Module Package Selection** — Python runtimes now honor `metadata.moduleRuntimeParameters.module` when a module archive contains multiple packages, ensuring the intended package is imported before execution. * **Completion Event Chaining** — Applying an execution's `completeLabel` now emits a follow-up `CONTENT_CREATED` event so downstream subscriptions can react to the finalized content. ### Platform Updates — Late March 2026 **New Features:** * **Intake API Tokens** — Intakes now support scoped API tokens for machine-to-machine authentication. Create tokens on the new API Tokens tab; each token authenticates directly against a specific intake endpoint without requiring user credentials. Tokens are hashed at rest with SHA-256. * **Detachable Sidecar** — Pop out the sidecar document viewer into a separate browser window using the detach button. The inline sidecar collapses while the external window is open and automatically restores when the external tab is closed. * **Smart Grid Filters** — All grid views (tasks, projects, document families, etc.) now include a unified search bar with recent query history, structured filter mode with metadata-aware autocomplete, and AI-powered natural language filter generation. * **Script Step Log Viewer** — Script step logs are now captured in CloudWatch and viewable directly from Activity step details. Each script execution automatically records start/end entries and all `log()` calls. See [Script Steps](/guides/script-steps/index#viewing-script-logs). * **Knowledge Expression Trees** — Knowledge sets now support expression-based feature matching using AND, OR, and NOT operators, replacing the previous clause-based system. This enables more flexible conditional logic for knowledge assessment. See [Knowledge System](/concepts/knowledge_system#expression-based-matching). * **UNO Document Converter** — New module runtime for converting Office documents (Word, Excel, PowerPoint) via LibreOffice/UNO. Available as `kodexa/uno-runtime`. See [Module Runtimes](/concepts/module_runtimes#available-runtimes). * **LLM Model Manager** — New Python SDK `ModelManager` class provides unified access to all LLM models through the Kodexa AI Gateway. Supports text completion, function calling, streaming, thinking mode, and multimodal input. Replaces direct provider SDKs with a single gateway client. See [LLM & Model Manager](/sdk/python/llm). * **AI Grid Extraction** — Data forms using `v2:grid` layout now support AI-powered grid extraction with word-level node tagging for more accurate table data capture. See [Data Forms Extraction](/guides/data-forms/extraction#ai-extraction-on-grids). * **Direct Extract** — New `allowDirectExtract` option on data form attribute editors lets users copy values directly from the document text without an AI call. See [Data Forms Extraction](/guides/data-forms/extraction#direct-extract). **Improvements:** * **CLI Re-Authentication** — The CLI now automatically prompts for re-authentication when it encounters a 401 response, instead of failing. See [CLI Authentication](/guides/kdx-cli/authentication#automatic-re-authentication). * **Access Token Security** — API access tokens are now hashed at rest using SHA-256. The profile access tokens UI has been redesigned with confirmation dialogs before deletion and tokens scoped to the current user. * **Module Ref Rename** — The bridge script parameters `model_store` and `model_options` have been renamed to `module_ref` and `module_options` for consistency. See [Module Runtimes](/concepts/module_runtimes#magic-parameter-injection). * **Datadog Observability** — New instrumentation events for agent runtimes, agent instances, and LLM calls in the AI Gateway for Datadog monitoring. * **Execution Cancel** — The execution cancel button in the UI is now wired to the API endpoint. * **Taxonomy → Data Definition** — The UI label "Taxonomy" has been renamed to "Data Definition" across resource badges and labels for consistency with the platform terminology. ### Platform Updates — March 2026 **New Features:** * **Agentic Assistants** — A new assistant role that delegates processing to AI agents. Configure an agent runtime, module references, and a natural language prompt to let the agent autonomously decide how to process documents. See [Assistants](/concepts/assistants#agentic-assistant). * **Intake Enhancements** — Intakes now support JavaScript scripting for file validation and metadata enrichment, task template integration for auto-creating tasks on upload, multi-file uploads, knowledge feature assignment, and processing metadata. * **Activity Workflow Enhancements** — New SCRIPT and AGENT step types. SCRIPT steps run inline JavaScript for conditional routing. AGENT steps spawn AI agents in workflow execution. Action-qualified dependencies enable conditional branching (e.g., proceed only on "Approve"). Activity runs display as interactive DAG flow visualizations with automatic deadlock detection. * **AI Task Naming** — Task templates can configure LLM-powered naming so tasks receive descriptive titles based on document content. * **Notification Sounds** — Toggle audio notifications for error/warning toasts and new channel messages from your profile preferences. * **Document Native Download** — New `GET /api/document-families/{id}/native` endpoint returns the original uploaded file. See [Download original native file](/api-reference/documentfamilies/get-document-families-id-native). * **Execution Log Viewer** — Restyled with syntax highlighting, auto-scroll, copy and download buttons, and dark theme enforcement. * **Progress Toasts** — Consolidated progress notifications for document uploads and batch reprocessing operations. **Improvements:** * **Secrets API** — Secrets are now managed through organization-scoped endpoints (`/api/organizations/{orgId}/secrets`) with secure encrypted storage. * **Resource Resolver** — Three new project-scoped schemes: `task-status`, `task-template`, and `assistant`. See [Components and Structure](/concepts/components_and_structure#project-scoped-resources). * **CLI Resource Resolution** — `kdx apply` now resolves project-scoped resources (task statuses, task templates, assistants) with the `scheme://org/project/slug` URI format. * **AI Gateway** — Extended model metadata with pricing, description, and classification fields. Cloud models now proxy through the AI gateway. * **Store Reprocessing** — Reprocess documents within a store with assistant selection and filtering. * **Document Groups** — New `hardMaxPages` field to enforce a hard page count limit at upload time. ### KDX CLI v2026.3.0 (Pre-release) This version is currently available as a pre-release. Install via `brew install kodexa-ai/tap/kdx-dev` to try it out. Bug fixes, deployment reliability, and E2E test coverage: **Bug Fixes:** * **Apply Ordering Fix**: Fixed an issue where `kdx apply` for modules would overwrite metadata changes. Implementation uploads now happen before metadata PUT, ensuring inference options and other metadata updates are preserved. * **OpenAPI Resource Discovery**: Manual resource definitions now properly override OpenAPI-discovered resources, preventing bogus CRUD paths from being generated for document-stores and other hyphenated resource types. * **Hyphenated Resource Types**: Fixed resource discovery for `data-store`, `data-definition`, and `document-store` by normalizing hyphens and underscores in resource lookup. * **Deploy Failure Reporting**: Deployment failures are now properly surfaced with error counts and non-zero exit codes, instead of silently reporting success. * **ID Stripping on Create**: The CLI now strips `id` and `_id` fields from CREATE payloads since IDs are server-generated, preventing conflicts during resource creation. **Improvements:** * **E2E Test Suite**: Added comprehensive end-to-end tests covering document family reprocessing, knowledge-set CRUD and resolution, module upload/download, and CLI-to-API integration. * **Filter Syntax**: Updated to SpringFilter DSL syntax for resource filtering, with syntax reference added to `kdx get --help`. * **Sort Parameters**: Standardized sort parameter format across all commands. **Impact:** * Module metadata (inference options, configuration) is now reliably preserved during `kdx apply` operations * `kdx sync deploy` now correctly reports failures and returns non-zero exit codes for CI/CD pipelines * Resource operations for hyphenated types (data-store, document-store, data-definition) work reliably **Breaking Changes:** None - fully backward compatible with v2026.2.1 Available as pre-release via Homebrew: `brew install kodexa-ai/tap/kdx-dev` ### KDX CLI v2026.2.1 Bug fixes for project-scoped resource syncing and OpenAPI spec parsing: **Bug Fixes:** * **Project-Scoped Resource Sync**: Fixed an issue where syncing project-scoped resources (e.g., task templates) would fail with "TaskTemplate must have a project" errors. The PUT payload now correctly includes the project reference for project-scoped resources, mirroring the existing pattern for organization-scoped resources. * **OpenAPI Schema Parsing**: The CLI now gracefully handles missing `$ref` schema references in the server's OpenAPI specification. When broken references are encountered (e.g., a missing `ValidationFailedResponse`), the CLI patches in empty object stubs and retries parsing instead of failing. **Impact:** * Task template and other project-scoped resource syncing now works correctly with `kdx sync push` and `kdx sync deploy` * CLI operations no longer fail when the platform's OpenAPI spec contains missing schema definitions **Breaking Changes:** None - fully backward compatible with v2026.2.0 Available via Homebrew: `brew upgrade kdx` ### KDX CLI v2026.2.0 Document Command Overhaul, Knowledge Management & Module Downloads: **Version Scheme Change:** The CLI version now aligns with the platform release cycle (2026.2.x), replacing the previous 0.x/8.x numbering. **Document Command Rewrite:** All `kdx document` commands have been rewritten to use the native kodexa-document Go library via a new DocumentAdapter, providing significantly improved performance and richer output including node IDs and type information. **New Document Subcommands:** * **`kdx document stats`** - Document statistics summary * **`kdx document schema`** - Display document schema * **`kdx document tags`** - List and inspect tags * **`kdx document features`** - List and inspect features * **`kdx document node`** - Inspect individual nodes * **`kdx document text`** - Extract text content * **`kdx document page`** - Page-level operations * **`kdx document find`** - Multi-criteria search across nodes * **`kdx document spatial find`** - Spatial search by coordinates * **`kdx document spatial bbox`** - Bounding box queries * **`kdx document data objects`** - List data objects * **`kdx document data attributes`** - List data attributes * **`kdx document data exceptions`** - List data exceptions * **`kdx document audit`** - View audit trail entries **Knowledge Management:** * **`kdx knowledge attach`** - Attach files to knowledge sets * **`kdx knowledge download`** - Download knowledge set items * **`attachmentPath` support** in `kdx sync` for knowledge items **Module Downloads:** * **`kdx get module --download`** - Download module implementation packages directly **Sync Improvements:** * **Project-scoped resources** with auto-pull for task templates * Graceful handling of missing files in pull operations ```bash theme={null} # Document inspection with enriched output kdx document stats invoice.kddb kdx document find invoice.kddb --type paragraph --content "total" kdx document spatial bbox invoice.kddb --page 1 --x1 0 --y1 0 --x2 500 --y2 200 kdx document audit invoice.kddb # Knowledge management kdx knowledge attach my-org/knowledge-set:1.0.0 ./data.csv kdx knowledge download my-org/knowledge-set:1.0.0 item-id -o output.csv # Module download kdx get module my-org/my-model:1.0.0 --download ``` **Breaking Changes:** None - fully backward compatible with v8.0.0 Available via Homebrew: `brew upgrade kdx` ### KDX CLI v0.6.0 Content Object Access & Extended Store Commands: **New Features:** * **Document Family Content Commands**: New `kdx document-family content` subcommand for direct access to content objects (kddb files) within document families: * **`kdx document-family content list`** - List all content objects with IDs, timestamps, and labels * **`kdx document-family content download`** - Download kddb files directly, bypassing DFM export timeouts * **`--latest` flag** - Automatically select the most recent content object * **`--output` flag** - Specify custom output filename * **Store Upload & Watch Commands**: New commands for document upload and processing workflows: * **`kdx store upload`** - Upload files (PDF, images, documents) to document stores * **`kdx store watch`** - Monitor document processing progress with real-time status updates * **`--label` flag** - Wait for specific processing labels (PREPARED, FIRST-PASS, LABELED, PROCESSED) * **`--timeout` flag** - Configure wait timeout for long-running processing **Improvements:** * **Project Create Organization Lookup**: Fixed organization resolution when creating projects from templates * **Dynamic API Flags**: Improved handling of dynamic flags for `kdx run` operations * **Document Family Data Export**: Fixed data export to always use latest content object **Usage Examples:** ```bash theme={null} # List content objects in a document family kdx document-family content list 70b894f5-8d32-4584-b780-89f89210e078 # Download the latest kddb file kdx document-family content download 70b894f5-8d32-4584-b780-89f89210e078 --latest -o document.kddb # Upload and monitor processing kdx store upload satori/project-processing:1.0.0 ./report.pdf kdx store watch abc123 --label PROCESSED --timeout 600 ``` **Documentation:** * [Document Family Commands](/guides/kdx-cli/document-family-commands) * [Store Commands](/guides/kdx-cli/store-commands) **Breaking Changes:** None - fully backward compatible with v0.5.x Available via Homebrew: `brew upgrade kdx` ### v8 Documentation Preview Pre-release documentation for upcoming v8 CLI and SDK releases: **CLI v8 - Document Commands (Preview):** New `kdx document` command suite for working with local KDDB files without requiring a platform connection: * **`kdx document info`** - Display document summary with metadata and statistics * **`kdx document print`** - Pretty print document structure as an ASCII tree with depth limiting and feature display * **`kdx document select`** - Query nodes by type using selector syntax * **`kdx document natives list/extract`** - List and extract embedded files (PDFs, images, etc.) * **`kdx document external list/get/set/delete`** - Manage key-value external data store * **`kdx document metadata get/set`** - View and modify document metadata ```bash theme={null} # Quick document inspection kdx document info invoice.kddb kdx document print invoice.kddb --depth 3 kdx document select invoice.kddb "//paragraph" # Extract embedded PDF kdx document natives extract invoice.kddb original.pdf -o extracted.pdf ``` **SDK Documentation Updates:** * **Native Documents**: Store and retrieve binary files within KDDB documents * **External Data**: Flexible key-value store for custom data and processing results * **Metadata**: Document properties including UUID, version, and custom fields * **Content Nodes**: Hierarchical document structure with types and content * **Selectors**: XPath-like query syntax for finding nodes **Documentation Links:** * [CLI Document Commands](/guides/kdx-cli/document/overview) * [SDK Native Documents](/sdk/native-documents) * [SDK External Data](/sdk/external-data) * [SDK Metadata](/sdk/metadata) These features are in preview and will be included in the upcoming v8 release of the KDX CLI and Kodexa Document SDKs. ### Kodexa Document SDK v8.0.0 New SDK Documentation & Version 8 Libraries: **New Documentation:** * **SDK Documentation Tab**: Added a dedicated SDK section to the developer portal with comprehensive guides for Python and TypeScript * **Python Getting Started**: Complete guide covering installation, document creation, node manipulation, selectors, features, tags, and saving * **TypeScript Getting Started**: Full guide including WASM initialization, async patterns, memory management, and browser/Node.js setup **Python SDK - kodexa-document v8.0.0:** * High-performance document processing via Go backend with CFFI bindings * \~100x faster in-memory mode for processing pipelines * Full support for KDDB format, hierarchical nodes, features, tags, and XPath-like selectors * Context manager support for automatic resource cleanup ```bash theme={null} pip install kodexa-document ``` **TypeScript SDK - @kodexa-ai/document-wasm-ts v8.0.0:** * WebAssembly-powered SDK for Node.js and modern browsers * \~5x faster than pure JavaScript implementations * Full async API with TypeScript type safety * Works with both file-based and in-browser SQLite ```bash theme={null} npm install @kodexa-ai/document-wasm-ts ``` **Key Features (Both SDKs):** * Create, load, and save KDDB documents * Hierarchical ContentNode tree structure * XPath-like selector queries * Features (key-value metadata) and Tags (annotations with confidence) * JSON and binary export formats **Links:** * [Python SDK Documentation](/sdk/python) * [TypeScript SDK Documentation](/sdk/typescript) * [PyPI - kodexa-document](https://pypi.org/project/kodexa-document) * [npm - @kodexa-ai/document-wasm-ts](https://www.npmjs.com/package/@kodexa-ai/document-wasm-ts) ### KDX CLI v0.5.2 Stability & User Experience Improvements: **Bug Fixes:** * **Increased Client Timeout**: Extended HTTP client timeout from 60 seconds to 10 minutes (600s) to prevent premature failures during long-running deployment operations, particularly beneficial for large manifest deployments, multiple resource/module deployments, and deployments to slow or distant environments * **Improved Branch Mapping Error Handling**: Changed behavior when no branch mapping is found - now displays informational message and exits gracefully instead of returning error, providing better UX when working on unmapped branches **Improvements:** * **Better CI/CD Integration**: Non-disruptive behavior when working on unmapped branches doesn't fail pipelines unnecessarily * **Clearer User Feedback**: Informational messages clearly distinguish between configuration issues and actual errors * **Enhanced Reliability**: Deployments that previously failed due to timeout will now complete successfully **Impact:** * Prevents deployment timeouts for operations with large manifests or multiple resources * Better user experience when working with selective branch mapping configurations * More reliable long-running deployment operations **Breaking Changes:** None - fully backward compatible with v0.5.0 and v0.5.1 **Download Links:** * [macOS (Intel)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.2/kdx_0.5.2_darwin_x86_64.tar.gz) * [macOS (Apple Silicon)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.2/kdx_0.5.2_darwin_arm64.tar.gz) * [Linux (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.2/kdx_0.5.2_linux_x86_64.tar.gz) * [Linux (ARM64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.2/kdx_0.5.2_linux_arm64.tar.gz) * [Windows (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.2/kdx_0.5.2_windows_x86_64.zip) Available in [kdx-cli v0.5.2](https://github.com/kodexa-ai/kdx-cli-releases/releases/tag/v0.5.2) ### Kodexa Sync Action v2.2.0 Built-in Slack Notifications & GitHub Job Summary: **New Features:** * **Slack Notifications**: Send rich deployment summaries to Slack automatically with `slack-channel-id` and `slack-token` inputs * **GitHub Job Summary**: Add deployment summary to workflow run with `annotate-summary: true` * **Renamed Input**: `workers` → `threads` to match kdx-cli flag naming **Slack Message Includes:** * 🚀 Deployment status (or 🔍 for dry runs) * Repository and branch information * Resource counts (created, updated, unchanged) * Direct link to the GitHub Actions run **Usage Example:** ```yaml theme={null} - uses: kodexa-ai/kdx-sync-action@v2 with: threads: 8 annotate-summary: true slack-channel-id: $\{{ secrets.SLACK_CHANNEL_ID }} slack-token: $\{{ secrets.SLACK_BOT_TOKEN }} env: KODEXA_PROD_API_KEY: $\{{ secrets.KODEXA_PROD_API_KEY }} ``` Available in [kdx-sync-action v2.2.0](https://github.com/kodexa-ai/kdx-sync-action/releases/tag/v2.2.0) ### KDX CLI v0.5.0 Tag-Based Deployments & Enhanced GitOps: **Major Features:** * **Tag-Based Deployment Mappings**: Deploy using git tags in addition to branches, enabling release-driven workflows with `tag_mappings` configuration supporting semantic versions, release candidates, and preview tags * **Manual Deployment Overrides**: New `--branch` and `--tag` flags provide explicit control over deployment routing without requiring git operations, perfect for CI/CD, testing, and rollback scenarios * **JSON Deployment Reports**: Generate structured JSON reports of deployment actions with `--json-report ` for CI/CD integration * **Parallel Resource Deployment**: New `--threads ` flag for configuring parallel threads during resource deployment - significantly faster for large deployments * **Resource Filtering**: Filter resources during deployment with `--filter ` for selective deployments **Improvements:** * **Enhanced Error Messages**: Clear, actionable error messages with hints when mappings are not found * **Better User Feedback**: Deployment mode indicators showing whether using branch detection, tag detection, or manual override * **Improved Mapping Resolution**: Support for multiple overlapping mappings, enabling sophisticated multi-environment deployment strategies **Usage Examples:** ```bash theme={null} # Deploy with JSON report kdx sync deploy --json-report ./deploy-report.json # Deploy with parallel threads (8) kdx sync deploy --threads 8 # Deploy specific tag mapping kdx sync deploy --tag v1.0.0 # Filter resources during deployment kdx sync deploy --filter "invoice-*" ``` **Configuration - Tag Mappings:** ```yaml theme={null} branch_mappings: - pattern: "main" target: production environment: prod tag_mappings: - pattern: "v*" target: production environment: prod - pattern: "rc-*" target: staging environment: staging ``` **Download Links:** * [macOS (Intel)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.0/kdx_0.5.0_darwin_x86_64.tar.gz) * [macOS (Apple Silicon)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.0/kdx_0.5.0_darwin_arm64.tar.gz) * [Linux (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.0/kdx_0.5.0_linux_x86_64.tar.gz) * [Linux (ARM64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.0/kdx_0.5.0_linux_arm64.tar.gz) * [Windows (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.5.0/kdx_0.5.0_windows_x86_64.zip) Available in [kdx-cli v0.5.0](https://github.com/kodexa-ai/kdx-cli-releases/releases/tag/v0.5.0) ### KDX CLI v0.4.1 Enhanced Debugging, Error Handling & Sync Improvements: **New Features:** * **Enhanced Client Debugging**: Added detailed API request/response logging when debug mode is enabled, providing comprehensive information for troubleshooting * **Improved Module Syncing**: Updated module syncing to build and display full slugs with organization prefixes, improving clarity in logs and progress reporting * **Enhanced Deployment Output**: Deployment command now includes the environment URL in planned deployment messages, providing clearer context for users * **Alternate Extension Support**: Added support for both `.yaml` and `.yml` extensions when reading resource files, with improved error hints showing all attempted file paths **Improvements:** * **Better Error Messages**: Error messages now reference full slugs with organization prefixes, making it easier to identify and debug issues * **Improved Error Handling**: Enhanced deployment error handling to capture and report errors without terminating the process immediately * **Robust Payload Handling**: New utility function to safely extract string values from interface types * **Debug Mode Formatting**: Conditionally display full response bodies based on debug mode for cleaner output in normal operation **Code Quality:** * Fixed gofmt formatting in `resource_types_test.go` **Download Links:** * [macOS (Intel)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.4.1/kdx_0.4.1_darwin_x86_64.tar.gz) * [macOS (Apple Silicon)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.4.1/kdx_0.4.1_darwin_arm64.tar.gz) * [Linux (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.4.1/kdx_0.4.1_linux_x86_64.tar.gz) * [Linux (ARM64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.4.1/kdx_0.4.1_linux_arm64.tar.gz) * [Windows (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.4.1/kdx_0.4.1_windows_x86_64.zip) Available in [kdx-cli v0.4.1](https://github.com/kodexa-ai/kdx-cli-releases/releases/tag/v0.4.1) ### KDX CLI v0.3.0 Knowledge Sets & Immutable Resources: **New Features:** * **Knowledge Set Support**: New `knowledgeset` resource type with full CRUD operations, including example configurations for financial knowledge sets and full support in metadata API and sync operations * **Immutable Resource Types**: Added support for immutable resources (`featuretype` and `featureinstance`) that cannot be modified once created, maintaining database integrity. Sync operations automatically skip updates for immutable resources with appropriate warnings **Download Links:** * [macOS (Intel)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.3.0/kdx_0.3.0_darwin_x86_64.tar.gz) * [macOS (Apple Silicon)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.3.0/kdx_0.3.0_darwin_arm64.tar.gz) * [Linux (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.3.0/kdx_0.3.0_linux_x86_64.tar.gz) * [Linux (ARM64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.3.0/kdx_0.3.0_linux_arm64.tar.gz) * [Windows (x86\_64)](https://github.com/kodexa-ai/kdx-cli-releases/releases/download/v0.3.0/kdx_0.3.0_windows_x86_64.zip) Available in [kdx-cli v0.3.0](https://github.com/kodexa-ai/kdx-cli-releases/releases/tag/v0.3.0) # Activity Source: https://developer.kodexa.ai/concepts/activities An Activity is how Kodexa represents a real business process in motion: automated work, human judgment points, state, results, and audit. An **Activity** is how Kodexa represents a real business process in motion. It is bigger than a Task. A Task is a unit of human work inside the process. An Activity is the process run itself: the thing that carries work from a business trigger to a trusted outcome. For document-heavy workflows, the Activity is where Kodexa keeps the automation, human review, institutional knowledge, integrations, results, and audit trail aligned.
An Activity run carries a document-heavy business process from trigger to outcome
## Start With The Business Process When you model work in Kodexa, start by naming the Activity in business terms: | Real-world process | Kodexa Activity | | ---------------------------------- | ------------------------------------------------------------------------------------ | | Invoice intake and three-way match | Receive invoice, extract fields, match PO and receipt, route exceptions, post result | | Loan packet intake | Classify packet, extract covenant data, validate completeness, route exceptions | | Claims intake | Classify claim documents, extract loss details, validate evidence, assign review | | KYC review | Collect identity documents, extract entities, check lists, create case or exception | The Activity should describe the work the platform is responsible for carrying forward. The Task should describe the point where a person needs to participate. ## Activity, Task, Activity Plan These concepts work together, but they are not interchangeable. | Concept | Meaning | Example | | ----------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | | **Activity Plan** | The reusable definition of how work should run | "AP invoice three-way match" | | **Activity** | One run of that plan inside a project | "Run invoice match for document family 8f2..." | | **Activity Step** | One configured operation inside the run | Extract, classify, call bridge, create task, approve | | **Task** | A unit of human work created when judgment is needed | Analyst reviews mismatched vendor fields | | **Project** | The workspace that binds plans to documents, data definitions, forms, queues, permissions, and integrations | North America AP project | The Activity Plan is the reusable definition. The Activity is the running instance. The Task is the moment a person needs to make a decision inside that run. ## What An Activity Carries An Activity gives business process state a first-class shape. | Area | What it answers | | -------------- | -------------------------------------------------------------------------------------------- | | Inputs | What document, document family, task, project, data definition, or context started the work? | | Step graph | Which automated operations need to run, and in what order? | | Dependencies | What must finish before another step can start? | | Runtime state | What is pending, running, waiting, complete, failed, or canceled? | | Human judgment | Where should the Activity pause and create a Task? | | Results | What data, decision, exception, status, or external action came out of the run? | | Audit | What happened, who or what changed state, and when? | This is why Activity is a larger concept than document extraction. Extraction may be one step inside the Activity. The Activity is the coordinated business process around it. ## Activity Lifecycle A typical Activity moves through this shape: ```mermaid theme={null} flowchart LR Trigger["Business trigger"] --> Activity["Activity starts"] Activity --> Inputs["Resolve inputs"] Inputs --> Steps["Materialize steps"] Steps --> Auto["Run automated work"] Auto --> Gate{"Need judgment?"} Gate -->|Yes| Task["Create Task"] Task --> Decision["Reviewer completes action"] Decision --> Auto Gate -->|No| Result["Record result"] Result --> Done["Complete with audit trail"] ``` The Activity owns the process state. The Task owns the human work item. When the Task is completed, rejected, escalated, or routed, the Activity can continue or branch based on that result. ## Tasks Are Part Of Activity Tasks belong inside the Activity mental model. They are the explicit human-work points in an Activity, not a separate way to model the business process. Activities create Tasks when the workflow reaches a point that should not be fully automated. Examples: * The extraction confidence is low and an analyst needs to review fields. * A vendor match conflicts with ERP data and AP needs to decide which record is correct. * A loan packet is missing a document and a credit analyst needs to request remediation. * A sanctions check returns a possible match and compliance needs to resolve it. The Task should not contain the whole business process. It should contain the human decision point, and the Activity should continue or branch after that decision is made. ## How Activities Work With Projects Activity Plans are reusable organization-level process definitions. Projects bind those plans to the resources needed for a specific business use case: * Document stores and document families * Data definitions and data forms * Task templates, queues, and statuses * Modules, prompts, Service Bridges, and integrations * Permissions, users, and teams That binding is what lets the same business process shape run across different teams or business units without rebuilding the process every time. ## Design Rule Ask this first: **What business process should Kodexa carry forward from unstructured input to trusted outcome?** That answer is usually the Activity. Then ask: **Where does the process need a person to make a judgment, resolve an exception, or approve the next move?** Those points are Tasks. ## Next Steps * [Tasks](/concepts/tasks) explains the human work surface inside an Activity. * [Data Forms](/guides/data-forms/introduction) explains the review UI that a Task uses inside an Activity. * [Projects](/concepts/projects) explains where Activity Plans are bound to documents, forms, queues, permissions, and integrations. * [Activity Plans](/guides/activity-plans/index) explains how to define the reusable process. * [SCRIPT steps](/guides/activity-plans/script-steps) explains how custom Activity logic runs inside a plan. # Adding Validation Rules Source: https://developer.kodexa.ai/concepts/adding_validation_rules Apply different validation rules based on document characteristics in Kodexa, conditionally adding fields and approval requirements to extractions. This guide shows how to apply different validation rules based on document characteristics. For example, invoices from certain vendors might require additional approval fields, or documents above a threshold might need extra verification. ## The Goal Apply conditional validation: * **High-value invoices** (over \$10,000) require manager approval field * **International vendors** require additional tax documentation fields * **Rush orders** skip certain optional validations ## What You'll Create ```mermaid theme={null} flowchart TD subgraph features["Document Features"] F1[Feature: "High Value"
amount > $10K] F2[Feature: "International Vendor"] F3[Feature: "Rush Order"] end subgraph items["Validation Rules"] I1[Item: "Require Manager Approval"] I2[Item: "Require Tax Docs"] I3[Item: "Skip Optional Fields"] end subgraph sets["Knowledge Sets"] KS1[Set: High Value Rules] KS2[Set: International Rules] KS3[Set: Rush Order Rules] end F1 --> KS1 --> I1 F2 --> KS2 --> I2 F3 --> KS3 --> I3 ``` ## Step 1: Create Feature Types ### Invoice Value Tier ```yaml theme={null} # kodexa-resources/knowledge-feature-types/invoice-value-tier.yaml slug: invoice-value-tier name: Invoice Value Tier description: Categorization of invoices by total value options: - name: tier type: string label: Value Tier description: The value tier (standard, high, critical) required: true extendedOptions: - name: thresholdMin type: number label: Minimum Threshold - name: thresholdMax type: number label: Maximum Threshold ``` ### Vendor Location ```yaml theme={null} # kodexa-resources/knowledge-feature-types/vendor-location.yaml slug: vendor-location name: Vendor Location description: Geographic classification of vendor options: - name: locationType type: string label: Location Type description: domestic or international required: true extendedOptions: - name: country type: string label: Country - name: region type: string label: Region ``` ### Processing Priority ```yaml theme={null} # kodexa-resources/knowledge-feature-types/processing-priority.yaml slug: processing-priority name: Processing Priority description: Processing priority level options: - name: priority type: string label: Priority description: standard, expedited, rush required: true ``` ## Step 2: Create the Validation Item Type ```yaml theme={null} # kodexa-resources/knowledge-item-types/validation-rule.yaml slug: validation-rule name: Validation Rule description: Define validation requirements for extracted data options: - name: ruleType type: select label: Rule Type required: true options: - value: require-field label: Require Field - value: skip-field label: Skip Field Validation - value: require-value-range label: Require Value in Range - value: require-approval label: Require Approval - name: targetField type: string label: Target Field description: The taxon path this rule applies to required: true - name: condition type: string label: Condition Expression description: Optional condition for when rule applies - name: errorMessage type: string label: Error Message description: Message shown when validation fails - name: severity type: select label: Severity options: - value: error label: Error (blocks processing) - value: warning label: Warning (allows override) - value: info label: Info (notification only) default: error ``` ## Step 3: Create Knowledge Items ### High-Value Invoice Rules ```yaml theme={null} # Require manager approval for high-value invoices title: Require Manager Approval description: High-value invoices must have manager approval knowledgeItemType: validation-rule active: true properties: ruleType: require-approval targetField: "approval/manager_signature" errorMessage: "Invoices over $10,000 require manager approval" severity: error ``` ```yaml theme={null} # Require cost center for high-value invoices title: Require Cost Center description: High-value invoices must specify cost center knowledgeItemType: validation-rule active: true properties: ruleType: require-field targetField: "accounting/cost_center" errorMessage: "Cost center is required for invoices over $10,000" severity: error ``` ### International Vendor Rules ```yaml theme={null} # Require tax documentation for international vendors title: Require International Tax Documentation description: International vendors require W-8BEN or equivalent knowledgeItemType: validation-rule active: true properties: ruleType: require-field targetField: "vendor/tax_documentation_type" errorMessage: "International vendors require tax documentation (W-8BEN, W-8BEN-E, etc.)" severity: error ``` ```yaml theme={null} # Require wire transfer details for international title: Require Wire Transfer Details description: International payments require wire transfer information knowledgeItemType: validation-rule active: true properties: ruleType: require-field targetField: "payment/wire_transfer_info" errorMessage: "Wire transfer details required for international vendors" severity: warning ``` ### Rush Order Rules ```yaml theme={null} # Skip optional fields for rush orders title: Skip Optional Field Validation description: Rush orders can skip optional documentation knowledgeItemType: validation-rule active: true properties: ruleType: skip-field targetField: "documentation/supporting_quotes" condition: "processing.priority == 'rush'" severity: info ``` ## Step 4: Create Knowledge Sets ### High-Value Invoice Set ```yaml theme={null} name: High-Value Invoice Validation description: Additional validation for invoices over $10,000 status: ACTIVE features: - featureTypeSlug: invoice-value-tier properties: tier: "high" items: - itemSlug: require-manager-approval - itemSlug: require-cost-center ``` ### International Vendor Set ```yaml theme={null} name: International Vendor Validation description: Validation rules for international vendors status: ACTIVE features: - featureTypeSlug: vendor-location properties: locationType: "international" items: - itemSlug: require-international-tax-documentation - itemSlug: require-wire-transfer-details ``` ### Rush Order Set ```yaml theme={null} name: Rush Order Processing description: Relaxed validation for rush orders status: ACTIVE features: - featureTypeSlug: processing-priority properties: priority: "rush" items: - itemSlug: skip-optional-field-validation ``` ## Combining Multiple Features Knowledge Sets can require multiple features. For example, high-value international invoices might need extra scrutiny: ```yaml theme={null} name: High-Value International Invoice Validation description: Enhanced validation for high-value international invoices status: ACTIVE # Both features must be present features: - featureTypeSlug: invoice-value-tier properties: tier: "high" - featureTypeSlug: vendor-location properties: locationType: "international" items: - itemSlug: require-manager-approval - itemSlug: require-international-tax-documentation - itemSlug: require-compliance-review # Additional item for this combination ``` ## How Validation Works at Runtime ```mermaid theme={null} sequenceDiagram participant Doc as Document participant Proc as Processor participant KS as Knowledge System participant Val as Validator Doc->>Proc: Invoice processed Proc->>Doc: Assign features based on content Note over Doc: Features: high-value, international Proc->>KS: Query matching Knowledge Sets KS-->>Proc: Returns matching sets Proc->>KS: Get validation items KS-->>Proc: Returns validation rules Proc->>Val: Apply validation rules Val-->>Proc: Validation results alt Validation Passed Proc->>Doc: Mark as validated else Validation Failed Proc->>Doc: Create exceptions end ``` ## Viewing Validation Results When validation rules are applied: 1. **Passed validations** - Document proceeds to next step 2. **Failed validations (error)** - Document is flagged with exceptions 3. **Failed validations (warning)** - Document can proceed with manual override 4. **Informational** - Notifications logged but processing continues View applied knowledge and validation results in: * Document details panel * Exceptions list * Workflow search (filter by validation status) ## Changing Rules on Existing Documents Rules change as the business changes, and reloading a document applies the current rules in full, so an already-processed document does not have to be reprocessed to pick up an edit: * Rules you added or changed are evaluated when the document loads. * Open exceptions raised by a rule you have since removed from its data element are closed on reload, and the close is recorded in the document's change history. Two cases still need a reprocess to clear the exceptions their rules raised: removing an entire Data Definition, and removing every rule from a single data element. ## Best Practices ### 1. Use Appropriate Severity Levels * **Error**: Critical business rules that must be met * **Warning**: Important but can be overridden with justification * **Info**: Nice-to-have or informational checks ### 2. Write Clear Error Messages ```yaml theme={null} # Good errorMessage: "Invoices over $10,000 require manager approval. Please upload signed approval form." # Too vague errorMessage: "Validation failed" ``` ### 3. Document Your Rules Use the description field to explain the business reason: ```yaml theme={null} title: Require Tax Documentation description: | IRS requires W-8BEN or W-8BEN-E for payments to non-US vendors. This ensures compliance with tax withholding requirements. Reference: IRS Publication 515 ``` ### 4. Test with Pending Review Status Create Knowledge Sets with `status: PENDING_REVIEW` first to test without affecting production: ```yaml theme={null} name: New Validation Rule (Testing) status: PENDING_REVIEW # Won't apply until activated ``` ## Next Steps * [Knowledge Feature Types](/concepts/knowledge_feature_types) - Full reference * [Knowledge Item Types](/concepts/knowledge_item_types) - Full reference * [Customizing Extraction](/concepts/customizing_extraction) - Another common use case * [Knowledge and Agents](/concepts/knowledge_and_agents) - Automate rule discovery # Components and Structure Source: https://developer.kodexa.ai/concepts/components_and_structure How Kodexa components are owned by organizations and shared across the platform, including resource types, URI schemes, and the resolver API. ## Ownership of Components by Organizations Each component in the Kodexa platform is uniquely owned by a single organization. Components can be designated as "public", permitting their utilization by other organizations. ## Resource URIs Components are identified by a **resource URI** that combines a scheme, the organization's slug, and the component's slug: ``` scheme://org_slug/component_slug ``` For example: ``` module://kodexa/pdf-parser taxonomy://acme-corp/invoice-taxonomy data-form://kodexa/spreading-form ``` Versions are no longer used in resource URIs. The platform resolves resources by organization and slug only. If a legacy URI includes a version suffix (e.g., `module://kodexa/pdf-parser:1.0.0`), the version is stripped automatically. ## Resolver API The platform provides a resolver endpoint that converts a resource URI into the concrete API path for that resource. ### Resolving a Resource ```bash theme={null} POST /api/resolve?path=module://kodexa/pdf-parser ``` **Response:** ```json theme={null} { "uri": "module://kodexa/pdf-parser", "path": "/api/modules/abc-123-def-456" } ``` The returned `path` is the ID-based API endpoint you can use for subsequent operations on that resource. ### Discovering Available Schemes ```bash theme={null} GET /api/resolve/schemes ``` Returns the list of all supported resource schemes and their API endpoint patterns. ## Resource Types Kodexa supports the following resource types, each with a URI scheme and corresponding API path: | Scheme | Aliases | API Path | Scope | Description | | ------------------------ | ------------------------ | ------------------------------ | ------- | ------------------------------------------------------------------- | | `module` | `model`, `model-runtime` | `/api/modules` | Org | Modules for parsing, transforming, and labeling documents | | `document-store` | `store` | `/api/document-stores` | Org | Repositories for files and their document representations | | `data-store` | | `/api/data-stores` | Org | Structured data extracted from documents | | `taxonomy` | `data-definition` | `/api/data-definitions` | Org | Data structure definitions within Kodexa | | `project-template` | | `/api/project-templates` | Org | Blueprints for creating new projects with predefined configurations | | `prompt` | `prompt-template` | `/api/prompts` | Org | Prompt templates for LLM-based processing | | `data-form` | | `/api/data-forms` | Org | Form definitions for data entry and review interfaces | | `knowledge-set` | | `/api/knowledge-sets` | Org | Collections of knowledge items for reference and extraction | | `knowledge-item-type` | | `/api/knowledge-item-types` | Org | Type definitions for knowledge items | | `knowledge-feature-type` | | `/api/knowledge-feature-types` | Org | Type definitions for knowledge features | | `service-bridge` | | `/api/service-bridges` | Org | Bridge definitions for external service integrations | | `task-status` | | `/api/project-task-statuses` | Project | Task status definitions within a project | | `task-template` | | `/api/task-templates` | Project | Task template blueprints within a project | Aliases resolve to the same underlying resource. For example, `model://kodexa/invoice-extractor` and `module://kodexa/invoice-extractor` both resolve via `/api/modules`. ### Project-Scoped Resources Most resource schemes are scoped to the organization. However, some resources (`task-status`, `task-template`) are scoped to a project within an organization. Their URIs use a three-part path: ``` scheme://orgSlug/projectSlug/resourceSlug ``` For example: ``` task-status://acme-corp/invoice-project/approved task-template://acme-corp/invoice-project/document-review ``` The resolver looks up the project by `orgSlug/projectSlug` and then finds the resource within that project by its slug. ## Referencing Components When referencing a component in YAML configuration files, use the URI scheme format: ```yaml theme={null} # Module reference in a pipeline step ref: module://kodexa/pdf-parser # Taxonomy reference ref: taxonomy://acme-corp/invoice-taxonomy # Service bridge reference in an Activity step serviceBridgeRef: service-bridge://acme-corp/ap-system ``` In contexts where the scheme is implicit (such as a `taxonomies` section that only accepts taxonomy references), you can use the short form: ```yaml theme={null} taxonomies: - ref: "acme-corp/invoice-taxonomy" ``` ## Projects and Entities Projects amalgamate various components to address a specific use-case. Components capture metadata and are deployable, while entities store data or link to content. Typical entities include: * Projects * Documents * Data objects * Activity runs * Tasks Entities each have a unique ID and are typically managed by a type of component or fall under the organization's purview. This distinction is critical: components form part of the organizational configuration for a use-case, whereas entities are associated with the projects, data, or documents being worked on. # Customizing Extraction by Document Type Source: https://developer.kodexa.ai/concepts/customizing_extraction Customize document extraction in Kodexa by routing different document types through different prompts, models, and processing rules at runtime. This guide walks through a complete example: using different extraction prompts for SEC 10K vs 10Q filings. The same pattern applies to any scenario where you need different processing for different document types. ## The Goal When processing SEC filings: * **10K documents** should use prompts optimized for annual reports * **10Q documents** should use prompts optimized for quarterly reports ## What You'll Create ```mermaid theme={null} flowchart TD subgraph types["1. Define Types"] FT[Feature Type
"SEC Filing Type"] IT[Item Type
"Extraction Prompt"] end subgraph instances["2. Create Instances"] F1[Feature: "10K"] F2[Feature: "10Q"] I1[Item: "10K Prompt"] I2[Item: "10Q Prompt"] end subgraph sets["3. Connect via Sets"] KS1[Set: "10K Rules"] KS2[Set: "10Q Rules"] end FT --> F1 FT --> F2 IT --> I1 IT --> I2 F1 --> KS1 KS1 --> I1 F2 --> KS2 KS2 --> I2 ``` ## Step 1: Create the Feature Type First, define a Feature Type to classify documents by their SEC filing type. ### Via YAML ```yaml theme={null} # kodexa-resources/knowledge-feature-types/sec-filing-type.yaml slug: sec-filing-type name: SEC Filing Type description: Classification of SEC filing documents (10K, 10Q, 8K, etc.) active: true options: - name: filingType type: string label: Filing Type description: The SEC filing type code required: true extendedOptions: - name: filingName type: string label: Filing Name description: Human-readable filing name - name: frequency type: string label: Reporting Frequency description: Annual, Quarterly, etc. ``` ### Via UI 1. Go to **Knowledge > Feature Types** 2. Click **Create Feature Type** 3. Enter: * Slug: `sec-filing-type` * Name: `SEC Filing Type` * Description: `Classification of SEC filing documents` 4. Add option: `filingType` (string, required) 5. Add extended option: `filingName` (string) 6. Save ## Step 2: Create Features Create features for each filing type you want to handle. ### Via YAML ```yaml theme={null} # These are typically created via API or UI during document processing # Here's the structure for reference: # 10K Feature featureType: sec-filing-type properties: filingType: "10K" extendedProperties: filingName: "Annual Report" frequency: "Annual" active: true # 10Q Feature featureType: sec-filing-type properties: filingType: "10Q" extendedProperties: filingName: "Quarterly Report" frequency: "Quarterly" active: true ``` ### Via UI 1. Go to **Knowledge > Features** 2. Click **Create Feature** 3. Select Feature Type: `SEC Filing Type` 4. Enter properties: * Filing Type: `10K` * Filing Name: `Annual Report` 5. Save 6. Repeat for 10Q ## Step 3: Create the Item Type Define an Item Type for customizing extraction prompts. ### Via YAML ```yaml theme={null} # kodexa-resources/knowledge-item-types/extraction-prompt-override.yaml slug: extraction-prompt-override name: Extraction Prompt Override description: Customize the extraction prompt for specific data elements options: - name: targetTaxon type: string label: Target Taxon description: The taxon path this prompt applies to (e.g., "financial/revenue") required: true - name: promptText type: text label: Prompt Text description: The custom extraction prompt required: true - name: includeContext type: boolean label: Include Document Context description: Whether to include surrounding context in extraction default: true - name: confidenceThreshold type: number label: Confidence Threshold description: Minimum confidence score (0-1) default: 0.8 ``` ### Via UI 1. Go to **Knowledge > Item Types** 2. Click **Create Item Type** 3. Enter: * Slug: `extraction-prompt-override` * Name: `Extraction Prompt Override` 4. Add options as shown above 5. Save ## Step 4: Create Knowledge Items Create specific prompt configurations for each filing type. ### Via YAML ```yaml theme={null} # 10K Revenue Extraction Item title: 10K Revenue Extraction Prompt description: Optimized prompt for extracting revenue from annual reports knowledgeItemType: extraction-prompt-override active: true properties: targetTaxon: "financial/total_revenue" promptText: | Extract the Total Revenue (also called Net Revenue or Total Sales) from this SEC 10K annual report. Instructions: 1. Look in the Consolidated Statements of Operations 2. Find the most recent fiscal year column 3. Extract the Total Revenue line item 4. Include the fiscal year end date Return format: {"value": , "fiscalYearEnd": "", "currency": "USD"} includeContext: true confidenceThreshold: 0.85 ``` ```yaml theme={null} # 10Q Revenue Extraction Item title: 10Q Revenue Extraction Prompt description: Optimized prompt for extracting revenue from quarterly reports knowledgeItemType: extraction-prompt-override active: true properties: targetTaxon: "financial/total_revenue" promptText: | Extract the Total Revenue from this SEC 10Q quarterly report. Instructions: 1. Look in the Consolidated Statements of Operations 2. Find the current quarter column (not year-to-date) 3. Extract the Total Revenue line item 4. Include the quarter end date Return format: {"value": , "quarterEnd": "", "currency": "USD"} includeContext: true confidenceThreshold: 0.8 ``` ### Via UI 1. Go to **Knowledge > Items** 2. Click **Create Item** 3. Select Item Type: `Extraction Prompt Override` 4. Enter title, description, and properties 5. Save 6. Repeat for 10Q prompt ## Step 5: Create Knowledge Sets Connect features to items with Knowledge Sets. ### Via YAML ```yaml theme={null} # 10K Processing Rules name: 10K Document Processing Rules description: Apply 10K-specific extraction prompts status: ACTIVE # Conditions: when document has this feature features: - featureTypeSlug: sec-filing-type properties: filingType: "10K" # Actions: apply these items items: - itemSlug: 10k-revenue-extraction-prompt ``` ```yaml theme={null} # 10Q Processing Rules name: 10Q Document Processing Rules description: Apply 10Q-specific extraction prompts status: ACTIVE features: - featureTypeSlug: sec-filing-type properties: filingType: "10Q" items: - itemSlug: 10q-revenue-extraction-prompt ``` ### Via UI 1. Go to **Knowledge > Sets** 2. Click **Create Knowledge Set** 3. Enter: * Name: `10K Document Processing Rules` * Status: `Active` 4. Add Feature condition: SEC Filing Type = 10K 5. Add Item: 10K Revenue Extraction Prompt 6. Save 7. Repeat for 10Q ## How It Works at Runtime ```mermaid theme={null} sequenceDiagram participant Doc as Document participant Proc as Processor participant KS as Knowledge System Doc->>Proc: Document uploaded Proc->>KS: Check features KS-->>Proc: Document has "10K" feature Proc->>KS: Find matching Knowledge Sets KS-->>Proc: Return "10K Processing Rules" Proc->>KS: Get Items from Set KS-->>Proc: Return "10K Revenue Prompt" Proc->>Doc: Apply customized extraction ``` 1. Document is uploaded and classified as 10K 2. Feature "10K" is linked to the document 3. Processor queries Knowledge Sets for matching features 4. Knowledge Set "10K Processing Rules" matches 5. Item "10K Revenue Extraction Prompt" is retrieved 6. Custom prompt is used for extraction ## Complete GitOps Example Here's the full set of files for deploying via `kdx sync`: ```text theme={null} kodexa-resources/ ├── knowledge-feature-types/ │ └── sec-filing-type.yaml ├── knowledge-item-types/ │ └── extraction-prompt-override.yaml └── projects/ └── sec-processing/ ├── knowledge-items/ │ ├── 10k-revenue-prompt.yaml │ └── 10q-revenue-prompt.yaml └── knowledge-sets/ ├── 10k-rules.yaml └── 10q-rules.yaml ``` Manifest: ```yaml theme={null} # manifests/sec-processing.yaml resources: knowledge-feature-types: - sec-filing-type knowledge-item-types: - extraction-prompt-override projects: - sec-processing ``` Deploy: ```bash theme={null} kdx sync deploy --target my-org --env prod ``` ## Next Steps * [Knowledge Feature Types](/concepts/knowledge_feature_types) - Full reference * [Knowledge Item Types](/concepts/knowledge_item_types) - Full reference * [Adding Validation Rules](/concepts/adding_validation_rules) - Another common use case # Data Stores Source: https://developer.kodexa.ai/concepts/data_stores Data Stores hold structured data extracted from labeled documents in Kodexa, linking Data Definitions, attributes, and lineage back to source document stores. Data stores are designed to hold structured data that has been extracted from a set of labeled documents that are held in a document store. The data store is linked to a Data Definition. The Data Definition formalizes the structure of the data into groups and individual data attributes, then the actual data points and their related groups are created in the data store with lineage back to the document store holding the document representation. ## Store Options The data store has several options that can be set to control how it behaves. These are set on the store object and are: * `allowDataEditing` - If set to `true` then the UI allows the user to edit the data. Note that this setting is not currently enforced by the API. * `deleteProtection` - If set to `true` then the store will be protected from deletion. This means that you can't delete the store or delete all its contents. However, you can still delete documents from the store. # Document-Based Validation Source: https://developer.kodexa.ai/concepts/document-based_validation Add dynamic validation rules to Kodexa documents based on content analysis, ensuring decisions made during processing surface in human-in-the-loop review. There are situations where we want to be able to add validation rules to a document. It is common to want to add validation rules to data elements in a data definition (in the API these refer to taxons in a Taxonomy), however sometimes based on analysis of a document we might want to add additional validation rules that are dynamic. The validation rules will be carried through the extraction from the document and into the data objects. This ensures that decisions made by a model while processing show up later in the user experience for human-in-the-loop processing. To begin, let's consider a simple document created using Kodexa's Document class: ```python theme={null} from kodexa_document import Document document = Document.from_text("Hello, world!") ``` Next, we'll define a taxonomy with a nested taxon structure: ```python theme={null} from kodexa.model.objects import Taxonomy, Taxon taxonomy = Taxonomy(type="taxonomy", name="Testing", slug="testing", version="1.0.0") person = Taxon(path="person", name="person", group=True) person_name = Taxon(path="person/name", name="name") person.children = [person_name] taxonomy.taxons = [person] ``` In this example, we've created a taxonomy called "Testing" with a parent taxon "person" and a child taxon "name". The "person" taxon is marked as a group, allowing it to contain child taxons. Now, let's add a validation rule to the "person" taxon: ```python theme={null} from kodexa.model.objects import TaxonValidation, DocumentTaxonValidation validation_rule = TaxonValidation( name="NameRequired", description="Name is required", rule_formula="ifnull(name, '') != ''" ) document_validation = DocumentTaxonValidation( taxonomy_ref="test/test-taxonomy", taxon_path="person", validation=validation_rule ) ``` This validation rule, named "NameRequired", ensures that the "name" field is not empty for any instance of the "person" taxon. To apply this validation to our document, we use the `set_validations` method: ```python theme={null} document.set_validations([document_validation]) ``` Validations added with `set_validations` persist with the document rather than living only in the process that added them. They are stored on the document, so a later extraction run — including one in a separate process — will evaluate them, and they are included when the document is exported to JSON with `to_json`. We can then retrieve and verify the applied validations: ```python theme={null} validations = document.get_validations() assert len(validations) == 1 assert validations[0].taxonomy_ref == "test/test-taxonomy" assert validations[0].taxon_path == "person" assert validations[0].validation.name == "NameRequired" assert validations[0].validation.description == "Name is required" assert validations[0].validation.rule == "name is not None" ``` These assertions confirm that our validation has been successfully added to the document and can be retrieved as expected. By implementing such validations on nested taxon structures, we can ensure that specific data requirements are met within our documents, even for complex hierarchical data. This approach is particularly useful when dealing with documents that require consistent nested data structures or when implementing data quality checks in document processing pipelines. The nested structure allows for more granular control over data validation. For example, we could add additional validations to the "name" taxon specifically, or create more complex rules that span multiple levels of the taxon hierarchy. In conclusion, Kodexa's ability to add validations to nested taxons provides a powerful mechanism for maintaining data integrity in document processing workflows. By leveraging these features, developers can create more robust and reliable document management systems that can handle complex, hierarchical data structures with ease. # Document Stores Source: https://developer.kodexa.ai/concepts/document_stores Document Stores in Kodexa hold original files and their derived documents in document families, supporting both training and operational workflows. Document stores are designed to hold the original documents that are used to extract data. They are designed to be a repository of documents that can be used to train models and extract data. A document store holds what we call *Document Families*. These are logical containers that relate both the original file and any of the derived documents that are created from it. ## Store Purposes There are two main purposes for a document store: * To hold documents that we will be using for training models * To hold documents that we will be using to extract data On the store object we have a `storePurpose` property that can be set to either `TRAINING` or `OPERATIONAL`. This is used to determine which documents are available for use in the store. The actual functionality of the store itself is the same regardless of the purpose. ## Anatomy of a Document Family A document family consists of a document and any of the derived documents that are created from it. Since a document family can contain both a native PDF and also the Kodexa Documents derived from it, we have a stereotype we call a content object. A content object points to something that contains content. This can be a file or a document, the content type on the content object is then either 'Document' or 'Native'. In this case 'Native' means the original file, since it could be of any file type. The document family holds the list of content objects and also a concept called "Document Transitions". A document transition is a link between two content objects that shows how a content object was derived from another content object, and which assistant (or user) was responsible for the derivation. ## Store Options The document store has a number of options that can be set to control how it behaves. These are set on the store object and are: * `highQualityPreview` - If set to `true` then the store will generate high quality previews of the documents. This will increase the time it takes to generate the previews but will result in better quality previews. The default value is `false`. This setting is used in the UI. * `searchable` - If set to `true` then the store will be searchable. This means that the platform will pass content from document to indexing. * `deleteProtection` - If set to `true` then the store will be protected from deletion. This means that you can't delete the store or delete all its contents. However, you can still delete documents from the store. ## Document Properties You can specify document properties, these will be shown to the user using the options when they are uploading a file to the document store. This is a good way to capture information in the document family metadata that you can use later. ```yaml theme={null} documentProperties: - type: string label: Customer ID name: CustomerID required: true ``` You can combine these with the label expression you will see in the next section to automatically apply as document tagging. ```yaml theme={null} labelExpressions: - expression: "['CustomerID']" ``` ## Expression Labels When a document (either a native file or a Kodexa document) is added to a Document store, we want to have the ability to determine if we want to add a label to it. This can be achieved with Label Expressions. A label expression allows you to, on a document store, add a specific label to the new document based on the results of an expression. The expression itself is actually a Spring Expression Language ([https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html](https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html)) expression. This can allow for a use-case where the application that is uploading the document to the platform can include metadata with the upload. This metadata (as well as the document and document family) are then available for the expression to use. Let's say we have an application that is uploading documents to an instance of Kodexa. When the upload is associating a value in metadata called "ShouldProcessXML", the value can be `True` or `False`. As we load the document into the document store, we want to determine if this metadata flag is present, and if it is there and not set to `True` we want to add a label `dont_publish` to the document. In order to do this, we will want to create a label expression at the document store level that has properties: **label:** dont\_publish **expression:** ```groovy theme={null} containsKey('ShouldProcessXML') && ['ShouldProcessXML'].toLowerCase() != 'true' ``` This expression will then be evaluated - if the expression returns not True (not case-sensitive), then we will add the label. If the expression returns a string value then we will use this as the name of the label, for example let's say we wanted to add a label that was the value of the metadata field available on upload called 'CustomerName'. We would use the expression: ```groovy theme={null} containsKey('CustomerName') ? ['CustomerName'] : null ``` Expression Labels are part of the Store Metadata. ## Store Metadata Settings such as `indexed`, `documentProperties`, and `labelExpressions` are part of the store metadata. Store bodies expose this nested store metadata under a `contentMetadata` key, which is the preferred place to read and write these settings: ```yaml theme={null} contentMetadata: type: document indexed: true documentProperties: - type: string label: Customer ID name: CustomerID labelExpressions: - expression: "['CustomerID']" ``` The same settings also appear as flattened top-level keys on the store body; these copies remain for backward compatibility. Reads are stable — fetching a store returns the same body every time — and saving a full store body back preserves every setting. ## Uploading Documents To upload documents to a document store, use the [Document Families API](/api-reference/documentfamilies/post-document-families). This provides support for: * Uploading new content to existing document families * Attaching knowledge features during upload * Adding labels and metadata * Content versioning and transitions * Data extraction during upload See the [Document Families API](/api-reference/documentfamilies/get-document-families) for complete documentation on uploading and managing documents. # Document Tagging Source: https://developer.kodexa.ai/concepts/document_tagging Tag content in Kodexa documents at the node or text level, attaching metadata and relationships to power extraction, validation, and review workflows. ## Overview Tagging in Kodexa is a powerful feature that allows you to mark and annotate specific portions of content within your document nodes. Tags can be applied to entire nodes or specific portions of text, and can include additional metadata and relationships between tagged elements. ## Tag Structure A tag in Kodexa consists of the following components: * **Name**: The identifier for the tag (e.g., 'name', 'address', 'phone') * **Value**: The actual content being tagged * **Start/End Positions**: Optional positions within the node's content (if tagging specific text) * **UUID**: Unique identifier for the tag instance * **Confidence**: A score between 0 and 1 indicating tagging certainty * **Group UUID**: Links related tags together * **Data**: Additional JSON-serializable metadata * **Owner URI**: Identifies the source that created the tag (e.g., a model reference) ## Tagging Methods ### 1. Basic Node Tagging The simplest form of tagging applies a tag to an entire node: ```python Python theme={null} # Basic node tagging node.tag('category') ``` ```typescript TypeScript theme={null} // Basic node tagging await node.tag('category'); ``` ### 2. Fixed Position Tagging Tag specific portions of text using start and end positions: ```python theme={null} # Tag text from position 6 to 12 node.tag('name', fixed_position=[6, 12]) ``` ### 3. Regular Expression Tagging Tag content that matches a specific pattern (Python): ```python theme={null} # Tag all email addresses in the content node.tag('email', content_re=r'[\w\.-]+@[\w\.-]+') ``` ## Advanced Tagging Features ### Tag Groups Tags can be grouped together using group UUIDs to show they are related: ```python Python theme={null} import uuid group_id = str(uuid.uuid4()) # Tag multiple related elements with the same group UUID node.tag('person_name', fixed_position=[0, 10], group_uuid=group_id) node.tag('person_age', fixed_position=[15, 17], group_uuid=group_id) ``` ```typescript TypeScript theme={null} // Tag with group ID await node.tagWithOptions('person_name', { start: 0, end: 10, groupId: 1 }); await node.tagWithOptions('person_age', { start: 15, end: 17, groupId: 1 }); ``` ### Tag Metadata Additional data can be associated with tags (Python): ```python theme={null} node.tag('address', data={ 'type': 'residential', 'verified': True }) ``` ### Tag Confidence You can specify confidence levels for tags: ```python Python theme={null} node.tag('product_code', confidence=0.95) ``` ```typescript TypeScript theme={null} await node.tagWithOptions('product_code', { confidence: 0.95 }); ``` ### Tag Owner URI Identify the source that created a tag: ```python theme={null} node.tag('invoice_number', owner_uri='model://kodexa/invoice-extractor') ``` ## Working with Tagged Content ### Retrieving Tags ```python Python theme={null} # Get all tags on a node tags = node.get_tags() # Iterate over tags for tag in tags: print(f"Name: {tag.uuid}, Value: {tag.value}, Confidence: {tag.confidence}") # Check if a node has a specific tag if node.has_tag('address'): print("Node has address tag") # Get tag names tag_names = node.get_tag_names() ``` ```typescript TypeScript theme={null} // Get all tag names on a node const tags = await node.getTags(); // Check if a node has a specific tag if (await node.hasTag('address')) { console.log("Node has address tag"); } ``` ### Removing Tags ```python Python theme={null} # Remove a specific tag by name node.remove_tag('category') ``` ```typescript TypeScript theme={null} // Remove a specific tag by name await node.removeTag('category'); ``` ## Tag Instances Tag instances allow you to group multiple nodes under a single tag. This is useful when a piece of information spans multiple nodes: ```python theme={null} # Create a tag instance spanning multiple nodes nodes = document.select('//line') document.add_tag_instance('address_block', nodes) ``` This tags all the selected nodes with the same tag name and the same UUID, linking them together as a group. ## Finding Tagged Nodes You can use selectors to find nodes with specific tags: ```python Python theme={null} # Find all nodes with a specific tag tagged_nodes = document.select("//*[hasTag('company_name')]") # Get all nodes with a specific tag all_tagged = document.select("//*[hasTag()]") ``` ```typescript TypeScript theme={null} // Find all nodes with a specific tag const taggedNodes = await document.select("//*[hasTag('company_name')]"); // Get all tagged nodes const allTagged = await document.getTaggedNodes(); ``` ## Diagrams ### Basic Tag Structure ```mermaid theme={null} classDiagram class Tag { +String name +String value +Integer start +Integer end +String uuid +Float confidence +String group_uuid +String owner_uri +Dict data } class ContentNode { +String content +List features +tag() +get_tags() +has_tag() +remove_tag() } ContentNode "1" --> "*" Tag ``` ### Tag Relationships ```mermaid theme={null} graph LR A[Node 1] -- group_uuid_1 --> B((Tag: Name)) C[Node 2] -- group_uuid_1 --> D((Tag: Age)) E[Node 3] -- group_uuid_2 --> F((Tag: Address)) ``` ## Best Practices 1. **Use Meaningful Tag Names**: Choose descriptive names that reflect the content being tagged. 2. **Group Related Tags**: Use `group_uuid` (Python) or `groupId` (TypeScript) to group related pieces of information. 3. **Include Confidence**: When using automated tagging, include confidence scores. 4. **Add Metadata**: Use the `data` parameter to store additional context about the tag. 5. **Set Owner URI**: When tagging from models or automated processes, set the `owner_uri` to track the tag source. ## Common Patterns ### Document Classification ```python theme={null} # Tag document type based on content node.tag('document_type', value='invoice', data={ 'confidence': 0.98, 'classifier': 'invoice_classifier_v1' }) ``` ### Entity Extraction ```python theme={null} # Tag named entities using regex node.tag('organization', content_re=r'Microsoft|Google|Apple') ``` ### Form Field Extraction ```python theme={null} # Tag form fields with metadata node.tag('field', fixed_position=[100, 150], data={ 'field_name': 'total_amount', 'field_type': 'currency', 'required': True }) ``` ## Tag Options Reference The `tag()` method in Python accepts these keyword arguments: | Option | Type | Description | | ------------------- | ----- | ------------------------------------------- | | `content_re` | str | Regular expression to match content | | `fixed_position` | list | `[start, end]` positions in content | | `tag_uuid` | str | UUID for the tag instance | | `group_uuid` | str | UUID to group related tags | | `parent_group_uuid` | str | Parent group UUID for hierarchical grouping | | `confidence` | float | Confidence score (0-1) | | `value` | str | Tagged value | | `data` | dict | Additional metadata | | `cell_index` | int | Cell index for table structures | | `owner_uri` | str | Source identifier for the tag | In TypeScript, use `tagWithOptions(name, options)` with the `TagOptions` interface: | Option | Type | Description | | --------------- | ------ | ------------------------------- | | `start` | number | Start position in content | | `end` | number | End position in content | | `confidence` | number | Confidence score (0-1) | | `groupId` | number | Group ID for related tags | | `parentGroupId` | number | Parent group ID | | `cellIndex` | number | Cell index for table structures | ## Error Handling When working with tags, consider these common issues: 1. **Position Errors**: Ensure fixed positions are within content bounds 2. **Regular Expression Matching**: Test patterns thoroughly 3. **Node Selection**: Verify node existence before tagging 4. **Content Accessibility**: Check content availability before tagging ```python theme={null} # Example of safe tagging with error handling try: if node.content: # Check if content exists if len(node.content) >= end_position: # Verify position node.tag('field', fixed_position=[start_position, end_position]) except Exception as e: print(f"Tagging error: {str(e)}") ``` ## Performance Considerations 1. Batch related tags together using `group_uuid` 2. Use specific selectors to limit the scope of tagging operations 3. Consider using tag instances for large groups of related nodes 4. Use transactions when performing many tag operations together Tags are stored in the document's KDDB persistence layer, so efficient tagging practices improve overall document processing performance. # Documents Source: https://developer.kodexa.ai/concepts/documents The Kodexa Document Model represents content, metadata, and features in the SQLite-based KDDB format used across the Kodexa Platform and SDKs. At the heart of Kodexa is the concept of a Document. A document represents the content, metadata and features associated with any type of information. Rather than storing the information as text, we store it in **KDDB** (Kodexa Document Database) format -- a SQLite-based structure containing a hierarchical tree of content nodes, metadata, tags, features, and extracted data. ## Document Structure A Kodexa Document consists of these core components: * **Content Node Tree**: A hierarchical tree of nodes representing the document's structure (pages, paragraphs, lines, words, tables, cells, etc.) * **Metadata**: Flexible key-value pairs for document-level information * **Source Metadata**: Information about the document's origin (filename, MIME type, checksum) * **Native Documents**: Embedded binary files (the original PDF, images, etc.) * **Data Objects & Attributes**: Structured extracted data organized by Data Definition * **Tags**: Annotations on content nodes linking them to extracted data * **Audit Trail**: Change history tracking ## Creating Documents Documents can be created using the SDK in Python or TypeScript: ```python Python theme={null} from kodexa_document import Document # Create an empty document doc = Document() # Create from text content doc = Document.from_text("Hello, World!") # Load from a KDDB file doc = Document.from_kddb("my-document.kddb") # Load from JSON doc = Document.from_json(json_string) ``` ```typescript TypeScript theme={null} import { Kodexa } from '@kodexa/document'; await Kodexa.init(); // Create an empty document const doc = await Kodexa.createDocument(); // Create from text content const doc = await Kodexa.fromText("Hello, World!"); // Load from a KDDB blob const doc = await Kodexa.fromBlob(blob); // Load from JSON const doc = await Kodexa.fromJson(jsonString); ``` ## Accessing Original Source Content Kodexa documents can embed the original source files (PDFs, images, Word documents) as **native documents** within the KDDB. This allows you to access the raw file data at any point during processing. You can use the `get_source` utility to retrieve the first embedded native document as bytes: ```python theme={null} from kodexa_document.utils import get_source # Get the original file data as BytesIO source_bytes = get_source(document) ``` Alternatively, you can access native documents directly through the accessor: ```python Python theme={null} # List all embedded files native_docs = doc.native_documents.get_all() # Get file data by ID data = doc.native_documents.get_data(native_docs[0]["id"]) ``` ```typescript TypeScript theme={null} // List all embedded files const nativeDocs = await doc.nativeDocuments.getAll(); // Get file data by ID const data = await doc.nativeDocuments.getData(nativeDocs[0].id); ``` This capability is particularly useful for tasks like OCR processing or extracting content directly from the original file format at any stage of the processing pipeline. ## Saving Documents Documents are saved in KDDB format (SQLite) for efficient storage and retrieval: ```python Python theme={null} # Save to KDDB file doc.to_kddb("my-document.kddb") doc.close() ``` ```typescript TypeScript theme={null} // Export as binary blob const blob = await doc.toBlob(); // Export as JSON const json = await doc.toJson(); await doc.dispose(); ``` ## Next Steps * [Working with a Document](/concepts/working_with_a_document) - Learn how to navigate and manipulate document content * [SDK Getting Started](/sdk/getting-started) - Detailed guide with code examples for both Python and TypeScript * [Content Nodes](/sdk/content-nodes) - Deep dive into the node hierarchy * [Document Tagging](/concepts/document_tagging) - Learn about tagging and annotation # Event Handling with Modules Source: https://developer.kodexa.ai/concepts/event_handling_with_modules Build event-handler modules in Kodexa that react to platform events such as uploads, transitions, and document changes alongside training and inference. In some cases, we want to have modules that not only handle training and inference, but can also react to events from the platform. An example might be a module that is used to determine which documents should be inferred in a project for example. These are not traditional modules, but are really event handlers that are triggered by events in the platform. To support this, we need to do two things: * Add a flag to our module metadata to indicate that we want to handle events * Include a new method that will receive the event ## Setting up a module to handle events To set up a module to handle events, we need to add a new flag to the module metadata called `eventAware` and set it to `true`. ```yaml theme={null} slug: my-module version: 1.0.0 orgSlug: kodexa type: store storeType: MODEL name: My Event Module metadata: eventAware: true moduleRuntimeRef: kodexa/base-module-runtime type: module contents: - module/* ``` Then in your module, add a `handle_event` function. Event-aware modules receive the same injected runtime parameters as inference modules, so you typically read the raw event data from `pipeline_context.context`. ```python theme={null} import logging logger = logging.getLogger(__name__) def handle_event(document=None, pipeline_context=None, status_reporter=None): context = pipeline_context.context if pipeline_context else {} if status_reporter: status_reporter.update("Handling event", status_type="processing") logger.info( "Received event %s for document family %s", context.get("eventType"), context.get("documentFamilyId"), ) return document ``` If the event is tied to a content object, `document` is hydrated for you. Otherwise, use `pipeline_context.context` for the event metadata you need. # Kodexa Concepts Introduction Source: https://developer.kodexa.ai/concepts/introduction Core Kodexa concepts for modeling document-heavy business processes with Activities, Tasks, documents, knowledge, and human-in-the-loop review. Kodexa is a platform for running document-heavy business processes where automation, human review, institutional knowledge, and audit trails need to stay aligned. The Concepts section starts with the business problem first. Activities are how the platform shows up in the workflow: a run of automated work with inputs, steps, state, and results. Tasks are how people show up in that workflow: reviewable, assignable units of human work when judgment or exception handling is needed.
Document-heavy enterprise processes split across automation, inboxes, spreadsheets, and institutional knowledge
## Mental Model Kodexa models real-world business workflow challenges around unstructured data by starting with Activity. Tasks are incorporated into that Activity model as the human-work points where judgment, approval, exception handling, or correction is needed. * **Activity**: a run of automated work, defined by an Activity Plan, with inputs, steps, dependencies, status, logs, and results. * **Task**: a unit of human work inside an Activity, owned by a person or queue, reviewable, completable, and tied to the workflow when judgment is needed. * **Workflow**: the choreography between Activities and Tasks. Activities create Tasks when a human decision is needed; Tasks can start Activities when their status changes. * **Project**: the workspace where the right document types, resources, Activity Plans, review surfaces, and permissions are bound together for a business use case. ## Where To Go Next * [Activities](/concepts/activities) explain how Kodexa models automated business process runs. * [Tasks](/concepts/tasks) explain where human review and judgment fit inside those runs. * [Projects](/concepts/projects) explain the workspace boundary for a business process. * [Project templates](/concepts/project_templates) explain reusable project setup. * [Activity Plans](/guides/activity-plans/index) explain how automated work is configured and run. * [Knowledge system](/concepts/knowledge_system) explains how Kodexa represents reusable knowledge. * [Working with exceptions](/concepts/working_with_exceptions) explains where human review fits. # Knowledge and Agents Source: https://developer.kodexa.ai/concepts/knowledge_and_agents How Kodexa agents build and consume knowledge with human-in-the-loop approval, capturing institutional knowledge as agents work with documents. This feature is coming soon. This documentation describes the planned functionality for how Kodexa agents will interact with the Knowledge System. Agents in Kodexa can both **build** and **consume** knowledge, creating a feedback loop where the system learns from documents and proposes configurations for human approval. ## The Vision ```mermaid theme={null} flowchart LR subgraph learn["Agents Learn"] DOC[Documents] --> AGENT[Agent] AGENT --> PROPOSE1[Propose Features] AGENT --> PROPOSE2[Propose Sets] end subgraph review["Humans Review"] PROPOSE1 --> REVIEW[Pending Review] PROPOSE2 --> REVIEW REVIEW --> HUMAN[Human Approves] end subgraph apply["System Applies"] HUMAN --> ACTIVE[Active Knowledge] ACTIVE --> PROC[Processing] PROC --> DOC end style learn fill:#e0f2fe style review fill:#fef3c7 style apply fill:#d1fae5 ``` Agents do the heavy lifting of discovering patterns and proposing configurations. Humans stay in control by reviewing and approving what goes into production. ## Building Knowledge: Feature Discovery When an agent processes documents, it may discover new entities that should be tracked as Knowledge Features. ### The Flow ```mermaid theme={null} flowchart TD DOC[New Invoice Arrives] --> AGENT[Agent Processes] AGENT --> CHECK{Known Vendor?} CHECK -->|Yes| LINK[Link to existing Feature] CHECK -->|No| PROPOSE[Propose new Feature] PROPOSE --> PENDING[Feature: Pending Review] PENDING --> HUMAN[Human Reviews] HUMAN -->|Approve| CREATE[Feature Created] HUMAN -->|Reject| DISCARD[Discarded] HUMAN -->|Modify| EDIT[Edit & Approve] CREATE --> LINK LINK --> DONE[Document Linked to Feature] ``` ### Example: New Vendor Discovery 1. **Agent processes invoice** from "NewTech Solutions Inc." 2. **Agent checks** if a vendor feature with matching ID exists 3. **No match found** - agent proposes new feature: ```yaml theme={null} # Agent-proposed feature (status: pending_review) featureType: vendor status: pending_review proposedBy: invoice-processing-agent confidence: 0.92 properties: vendorId: "NTS-2024-001" # Extracted from invoice extendedProperties: displayName: "NewTech Solutions Inc." address: "123 Innovation Way, Austin, TX" extractedFrom: "invoice-2024-03-15-001.pdf" ``` 4. **Human reviews** the proposed feature in the Knowledge interface 5. **Human approves** (or modifies and approves) 6. **Feature becomes active** and available for linking ### What Agents Extract Agents can propose features based on: * **Explicit data**: Vendor names, customer IDs, document types * **Inferred classifications**: Language, document category, urgency * **Patterns**: Recurring entities across multiple documents ## Consuming Knowledge: Intelligent Processing Agents have access to all active knowledge and use it to make processing decisions. ### The Flow ```mermaid theme={null} flowchart TD DOC[Document Arrives] --> AGENT[Agent Analyzes] AGENT --> FEATURES[Identify Features] FEATURES --> QUERY[Query Knowledge Sets] QUERY --> ITEMS[Get Applicable Items] ITEMS --> APPLY[Apply to Processing] APPLY --> RESULT[Processing Result] ``` ### Example: Applying Extraction Rules 1. **Document arrives** - classified as SEC 10K filing 2. **Agent queries Knowledge Sets** matching "SEC Filing Type = 10K" 3. **Knowledge Set returns Items**: * Use 10K-specific extraction prompt * Apply annual report validation rules * Route to SEC compliance queue 4. **Agent applies these configurations** during processing ## Proposing Knowledge Sets The most powerful capability: agents can propose entire Knowledge Sets by observing patterns. ### The Flow ```mermaid theme={null} flowchart TD AGENT[Agent Observes] --> PATTERN[Detects Pattern] PATTERN --> ANALYZE[Analyzes Correlation] ANALYZE --> PROPOSE[Proposes Knowledge Set] PROPOSE --> PENDING[Set: Pending Review] PENDING --> HUMAN[Human Reviews] HUMAN -->|Approve| ACTIVE[Set Activated] HUMAN -->|Reject| LEARN[Agent Learns] HUMAN -->|Modify| EDIT[Edit & Activate] ``` ### Example: Pattern Discovery **Scenario**: Agent notices that invoices from Vendor X frequently have validation exceptions for missing tax IDs, and users always override with the same justification. **Agent proposes**: ```yaml theme={null} # Agent-proposed Knowledge Set name: Vendor X Tax ID Exception description: | Vendor X (Government Agency) is tax-exempt. Skip tax ID validation for their invoices. status: pending_review proposedBy: validation-analysis-agent confidence: 0.88 evidence: - 47 invoices from Vendor X in past 90 days - 45 had tax ID validation overridden - Override reason consistently: "Government agency - tax exempt" # Conditions features: - featureTypeSlug: vendor properties: vendorId: "VENDOR-X-001" # Actions items: - itemType: validation-rule properties: ruleType: skip-field targetField: "vendor/tax_id" reason: "Government agency - tax exempt" ``` **Human reviews**: * Sees the evidence (47 invoices, consistent overrides) * Verifies the business logic makes sense * Approves the Knowledge Set * System now automatically skips tax ID validation for Vendor X ## The Human-in-the-Loop Principle All agent-created knowledge requires human approval before becoming active. ### Why This Matters | Aspect | Agent Role | Human Role | | --------------------- | ------------------------------- | ------------------------- | | **Pattern Detection** | Analyzes thousands of documents | Reviews proposed patterns | | **Feature Creation** | Extracts and proposes entities | Validates accuracy | | **Rule Discovery** | Identifies correlations | Confirms business logic | | **Configuration** | Proposes settings | Approves for production | ### Review Interface The Knowledge interface shows: * **Pending Features**: Agent-proposed entities awaiting approval * **Pending Sets**: Agent-proposed rules awaiting approval * **Evidence**: Why the agent made the proposal * **Confidence Score**: Agent's certainty level * **Impact Preview**: What would change if approved ## Feedback Loop Agents learn from human decisions: ```mermaid theme={null} flowchart LR PROPOSE[Agent Proposes] --> REVIEW[Human Reviews] REVIEW -->|Approve| LEARN1[Agent: Good Pattern] REVIEW -->|Reject| LEARN2[Agent: Avoid Similar] REVIEW -->|Modify| LEARN3[Agent: Refine Approach] LEARN1 --> IMPROVE[Improved Proposals] LEARN2 --> IMPROVE LEARN3 --> IMPROVE ``` When humans: * **Approve** - Agent learns this pattern is valuable * **Reject** - Agent learns to avoid similar proposals * **Modify** - Agent learns the correct approach ## Configuration ### Enabling Agent Knowledge Building ```yaml theme={null} # Assistant configuration assistantDefinitionRef: kodexa/document-processing-assistant options: knowledge: featureDiscovery: true # Propose new features setProposal: true # Propose knowledge sets confidenceThreshold: 0.75 # Minimum confidence to propose requireEvidence: true # Must include evidence ``` ### Review Notifications ```yaml theme={null} # Project configuration notifications: knowledgePendingReview: enabled: true channels: - email - slack recipients: - knowledge-admins@company.com ``` ## Best Practices ### 1. Start with High Confidence Threshold Begin with `confidenceThreshold: 0.9` and lower as you trust the agent's proposals. ### 2. Review Regularly Don't let pending items pile up. Regular review keeps the feedback loop active. ### 3. Document Rejections When rejecting proposals, add notes so the pattern is understood: ```yaml theme={null} # Rejection with feedback status: rejected rejectionReason: "This pattern only applies to Q4, not year-round" rejectedBy: john.smith rejectedAt: 2024-03-15T10:30:00Z ``` ### 4. Use Staging Environment Test agent knowledge building in staging before production: ```yaml theme={null} # Development/staging only options: knowledge: featureDiscovery: true setProposal: true autoActivate: false # Never auto-activate, always review ``` ## Coming Soon * **Batch Review**: Review multiple proposals at once * **Approval Workflows**: Route proposals to specific reviewers * **A/B Testing**: Test proposed rules on subset before full activation * **Confidence Trends**: Track agent accuracy over time ## Related Documentation * [Knowledge System Overview](/concepts/knowledge_system) - Foundation concepts * [Knowledge Feature Types](/concepts/knowledge_feature_types) - Define metadata categories * [Knowledge Item Types](/concepts/knowledge_item_types) - Define configurable behaviors # Knowledge Feature Types Concept Source: https://developer.kodexa.ai/concepts/knowledge_feature_types Knowledge Feature Types define reusable categories of metadata you capture from documents, providing the vocabulary for cross-document knowledge in Kodexa. A **Knowledge Feature Type** is a template that defines a category of metadata you want to capture and track across documents. Once defined, you create reusable **Knowledge Features** of that type, which can be linked to multiple documents. ## When Do You Need Feature Types? Create a Knowledge Feature Type when you want to: * Track which vendor invoices came from * Classify documents by type (10K, 10Q, Contract, etc.) * Identify document language * Group documents by customer, region, or business unit ## How Feature Types Work ```mermaid theme={null} flowchart LR subgraph "Define Once" FT[Knowledge Feature Type
"Vendor"] end subgraph "Create Features" F1[Feature: "Acme Corp"] F2[Feature: "Globex Inc"] F3[Feature: "Initech"] end subgraph "Link to Documents" D1[Invoice #1] D2[Invoice #2] D3[Invoice #3] D4[Invoice #4] end FT --> F1 FT --> F2 FT --> F3 F1 --> D1 F1 --> D2 F2 --> D3 F3 --> D4 ``` 1. **Define the Type** - Create a Knowledge Feature Type (e.g., "Vendor") 2. **Create Features** - Create reusable features of that type (e.g., "Acme Corp", "Globex Inc") 3. **Link to Documents** - Associate features with documents as they're processed ## Feature Type Structure | Field | Purpose | Example | | ----------------- | ----------------------------- | ------------------------------------- | | `slug` | Unique identifier | `vendor` | | `name` | Display name | `Vendor` | | `description` | What this feature represents | `The vendor that issued this invoice` | | `options` | Define natural key properties | `vendorId`, `taxId` | | `extendedOptions` | Define display properties | `displayName`, `address` | ### Options vs Extended Options **Options** define the **natural key** - the unique identifier for each feature: ```yaml theme={null} options: - name: vendorId type: string label: Vendor ID description: Unique identifier for this vendor ``` **Extended Options** define **presentation properties** - additional info for display: ```yaml theme={null} extendedOptions: - name: displayName type: string label: Display Name description: Human-readable vendor name - name: address type: string label: Address ``` When you create a feature, the `options` values become `properties` (the key), and `extendedOptions` values become `extendedProperties` (for display). ## Creating a Feature Type ### Via YAML (GitOps) Create a file in `kodexa-resources/knowledge-feature-types/`: ```yaml theme={null} # kodexa-resources/knowledge-feature-types/vendor.yaml slug: vendor name: Vendor description: Tracks the vendor associated with invoice documents active: true options: - name: vendorId type: string label: Vendor ID description: Unique identifier for this vendor required: true extendedOptions: - name: displayName type: string label: Display Name description: Human-readable vendor name - name: website type: string label: Website ``` Add to your manifest: ```yaml theme={null} # manifests/main.yaml resources: knowledge-feature-types: - vendor ``` Deploy: ```bash theme={null} kdx sync deploy ``` ### Via API ```bash theme={null} curl -X POST "https://platform.kodexa.ai/api/knowledgeFeatureTypes" \ -H "Authorization: Bearer $KODEXA_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "slug": "vendor", "name": "Vendor", "description": "Tracks the vendor associated with invoice documents", "active": true, "options": [ { "name": "vendorId", "type": "string", "label": "Vendor ID", "required": true } ], "extendedOptions": [ { "name": "displayName", "type": "string", "label": "Display Name" } ] }' ``` ### Via Kodexa UI 1. Navigate to **Knowledge** in the sidebar 2. Click **Feature Types** 3. Click **Create Feature Type** 4. Fill in the form with name, slug, description 5. Add options for natural keys 6. Add extended options for display properties 7. Save ## Creating Features Once you have a Feature Type, create features of that type: ### Via YAML ```yaml theme={null} # Creating a feature instance featureType: vendor properties: vendorId: "V001" extendedProperties: displayName: "Acme Corporation" website: "https://acme.com" active: true ``` ### Via API ```bash theme={null} curl -X POST "https://platform.kodexa.ai/api/knowledgeFeatures" \ -H "Authorization: Bearer $KODEXA_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "featureTypeSlug": "vendor", "properties": { "vendorId": "V001" }, "extendedProperties": { "displayName": "Acme Corporation", "website": "https://acme.com" }, "active": true }' ``` ## Linking Features to Documents Features are linked to document families. See the [Document Families API](/api-reference/documentfamilies/get-document-families) for details on attaching features during processing. ## Common Feature Type Examples ### Document Type Classification ```yaml theme={null} slug: document-type name: Document Type description: Classification of document (10K, 10Q, Contract, etc.) options: - name: typeCode type: string label: Type Code required: true extendedOptions: - name: typeName type: string label: Type Name - name: category type: string label: Category ``` ### Language Detection ```yaml theme={null} slug: language name: Language description: Primary language of the document options: - name: isoCode type: string label: ISO Language Code required: true extendedOptions: - name: languageName type: string label: Language Name ``` ### Customer Identification ```yaml theme={null} slug: customer name: Customer description: Customer associated with this document options: - name: customerId type: string label: Customer ID required: true extendedOptions: - name: customerName type: string label: Customer Name - name: region type: string label: Region ``` ## Next Steps * [Knowledge Item Types](/concepts/knowledge_item_types) - Define configurable behaviors * [Knowledge Sets](/concepts/knowledge_system) - Connect features to actions * [Customizing Extraction](/concepts/customizing_extraction) - Use features to customize processing # Knowledge Item Types Source: https://developer.kodexa.ai/concepts/knowledge_item_types Knowledge Item Types in Kodexa define configurable capabilities and behaviors for document processing, customizing extraction, prompts, and workflows. A **Knowledge Item Type** is a template that defines a configurable capability in the system. While [Knowledge Feature Types](/concepts/knowledge_feature_types) describe *what you know* about documents, Knowledge Item Types describe *what you can do* - such as customizing extraction prompts, applying validation rules, or modifying processing behavior. ## When Do You Need Item Types? Create a Knowledge Item Type when you want to: * Allow customization of extraction prompts for specific data elements * Define validation rules that can be selectively applied * Create processing behaviors that can be configured per document type * Enable business users to customize system behavior without code changes ## How Item Types Work ```mermaid theme={null} flowchart LR subgraph "Define Capability" IT[Knowledge Item Type
"Prompt Override"] end subgraph "Configure Instances" I1[Item: "SEC 10K Prompt"] I2[Item: "Invoice Total Prompt"] I3[Item: "Contract Date Prompt"] end subgraph "Apply via Knowledge Set" KS[Knowledge Set] end subgraph "Affects Processing" DOC[Document Processing] end IT --> I1 IT --> I2 IT --> I3 I1 --> KS I2 --> KS KS --> DOC ``` 1. **Define the Type** - Create a Knowledge Item Type that describes a capability (e.g., "Prompt Override") 2. **Create Items** - Create configured instances with specific values (e.g., "Use this prompt for invoice totals") 3. **Connect via Sets** - Knowledge Sets link Features to Items, triggering behaviors based on document characteristics ## Item Type Structure | Field | Purpose | Example | | ------------- | -------------------------------------- | ---------------------------------------------------- | | `slug` | Unique identifier | `prompt-override` | | `name` | Display name | `Prompt Override` | | `description` | What this capability does | `Customize the extraction prompt for a data element` | | `options` | Configuration parameters users can set | `targetField`, `promptText`, `model` | ### Options Define Configuration Options specify what parameters users can configure when creating items: ```yaml theme={null} options: - name: targetField type: string label: Target Field description: The data element this prompt applies to required: true - name: promptText type: text label: Prompt Text description: The custom prompt to use for extraction required: true - name: temperature type: number label: Temperature description: LLM temperature setting (0-1) default: 0.1 ``` ## Creating an Item Type ### Via YAML (GitOps) Create a file in `kodexa-resources/knowledge-item-types/`: ```yaml theme={null} # kodexa-resources/knowledge-item-types/prompt-override.yaml slug: prompt-override name: Prompt Override description: Customize the extraction prompt for a specific data element options: - name: targetField type: string label: Target Field description: The taxon path this prompt applies to required: true - name: promptText type: text label: Prompt Text description: The custom prompt to use for extraction required: true - name: includeExamples type: boolean label: Include Examples description: Whether to include few-shot examples default: true ``` Add to your manifest: ```yaml theme={null} # manifests/main.yaml resources: knowledge-item-types: - prompt-override ``` Deploy: ```bash theme={null} kdx sync deploy ``` ### Via API ```bash theme={null} curl -X POST "https://platform.kodexa.ai/api/knowledgeItemTypes" \ -H "Authorization: Bearer $KODEXA_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "slug": "prompt-override", "name": "Prompt Override", "description": "Customize the extraction prompt for a specific data element", "options": [ { "name": "targetField", "type": "string", "label": "Target Field", "required": true }, { "name": "promptText", "type": "text", "label": "Prompt Text", "required": true } ] }' ``` ### Via Kodexa UI 1. Navigate to **Knowledge** in the sidebar 2. Click **Item Types** 3. Click **Create Item Type** 4. Fill in name, slug, description 5. Add options to define configurable parameters 6. Save ## Creating Knowledge Items Once you have an Item Type, create configured items: ### Via YAML ```yaml theme={null} # Knowledge Item configuration title: SEC 10K Extraction Prompt description: Specialized prompt for extracting data from SEC 10K filings knowledgeItemType: prompt-override active: true properties: targetField: "financial/revenue" promptText: | Extract the total revenue figure from this SEC 10K filing. Look for "Total Revenue", "Net Revenue", or "Total Sales" in the financial statements section. Return the most recent fiscal year value. includeExamples: true ``` ### Via API ```bash theme={null} curl -X POST "https://platform.kodexa.ai/api/knowledgeItems" \ -H "Authorization: Bearer $KODEXA_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "SEC 10K Extraction Prompt", "description": "Specialized prompt for extracting data from SEC 10K filings", "knowledgeItemTypeSlug": "prompt-override", "active": true, "properties": { "targetField": "financial/revenue", "promptText": "Extract the total revenue figure from this SEC 10K filing...", "includeExamples": true } }' ``` ## Connecting Items to Features via Knowledge Sets Knowledge Items don't do anything on their own - they need to be connected to documents through **Knowledge Sets**. A Knowledge Set says "when a document has *these features*, apply *these items*." ```mermaid theme={null} flowchart LR F[Feature
"Document Type = 10K"] --> KS[Knowledge Set] KS --> I[Item
"SEC 10K Prompt"] DOC[Document with
type = 10K] -.-> F I -.-> DOC ``` See [Knowledge Sets](/concepts/knowledge_system) for details on creating sets. ## Common Item Type Examples ### Validation Rule ```yaml theme={null} slug: validation-rule name: Validation Rule description: Apply custom validation logic to extracted data options: - name: targetField type: string label: Target Field required: true - name: ruleType type: select label: Rule Type options: - value: required label: Required Field - value: range label: Numeric Range - value: pattern label: Regex Pattern - value: custom label: Custom Expression - name: ruleExpression type: string label: Rule Expression description: The validation expression or pattern - name: errorMessage type: string label: Error Message description: Message shown when validation fails ``` ### Field Ignore Rule ```yaml theme={null} slug: field-ignore name: Field Ignore Rule description: Skip extraction for specific fields under certain conditions options: - name: targetField type: string label: Target Field required: true - name: reason type: string label: Reason description: Why this field should be ignored ``` ### Processing Priority ```yaml theme={null} slug: processing-priority name: Processing Priority description: Adjust processing priority for specific document types options: - name: priority type: select label: Priority Level options: - value: high label: High Priority - value: normal label: Normal - value: low label: Low Priority - name: slaHours type: number label: SLA (Hours) description: Target processing time in hours ``` ## Item Types vs Feature Types | Aspect | Feature Type | Item Type | | --------------- | ------------------------------------ | ------------------------------------ | | **Purpose** | Capture metadata about documents | Define configurable behaviors | | **Direction** | Information FROM documents | Actions applied TO documents | | **Examples** | Vendor, Document Type, Language | Prompt Override, Validation Rule | | **Persistence** | Features are reused across documents | Items are applied via Knowledge Sets | | **Created by** | Often by agents during processing | Typically by developers/admins | ## Next Steps * [Knowledge Feature Types](/concepts/knowledge_feature_types) - Define document metadata categories * [Customizing Extraction](/concepts/customizing_extraction) - End-to-end guide using Item Types * [Adding Validation Rules](/concepts/adding_validation_rules) - Practical validation example # Knowledge System Source: https://developer.kodexa.ai/concepts/knowledge_system The Kodexa Knowledge System captures document metadata and configures intelligent processing behaviors, connecting what you know to what you do. The Knowledge System is how Kodexa captures information about documents and uses that information to customize processing. It connects **what you know** about documents to **what you do** with them.
Knowledge connects document context to behavior inside Activity execution
## The Two Sides of Knowledge ```mermaid theme={null} flowchart LR subgraph know["What We Know"] direction TB FT[Knowledge Feature Type
e.g., "Vendor"] F[Knowledge Feature
e.g., "Acme Corp"] FT --> F end subgraph do["What We Do"] direction TB IT[Knowledge Item Type
e.g., "Prompt Override"] I[Knowledge Item
e.g., "SEC extraction prompt"] IT --> I end subgraph bridge["The Bridge"] KS[Knowledge Set] end F --> KS KS --> I DOC[Document] -.->|has feature| F I -.->|applied to| DOC style know fill:#e0f2fe style do fill:#fef3c7 style bridge fill:#d1fae5 ``` ### "What We Know" - Document Metadata | Concept | Purpose | Example | | -------------------------- | ------------------------------------------- | ------------------------------------- | | **Knowledge Feature Type** | Template defining a category of metadata | "Vendor", "Document Type", "Language" | | **Knowledge Feature** | Reusable instance (shared across documents) | "Acme Corp", "10K", "English" | You define Feature Types once, then create Features of that type. Multiple documents can share the same Feature (e.g., 50 invoices all linked to "Acme Corp"). ### "What We Do" - Configurable Behaviors | Concept | Purpose | Example | | ----------------------- | ------------------------------------------- | --------------------------------------- | | **Knowledge Item Type** | Template defining a configurable capability | "Prompt Override", "Validation Rule" | | **Knowledge Item** | Specific configured behavior | "Use SEC prompt for revenue extraction" | Item Types define what can be configured. Items are the actual configurations with specific values. ### The Bridge - Knowledge Sets | Concept | Purpose | Example | | ----------------- | -------------------------- | ------------------------------------------------------ | | **Knowledge Set** | Connects features to items | "When Document Type = 10K, use SEC extraction prompts" | Knowledge Sets are the rules that say "when a document has *these features*, apply *these items*." ## When Do You Need This? Track which vendor invoices came from, classify document types, identify languages Use different extraction prompts for different document types Apply specific validation rules based on document characteristics Let agents propose knowledge configurations for human approval ## Quick Example: Vendor Tracking **Goal:** Track which vendor each invoice comes from. **Step 1: Create a Feature Type** ```yaml theme={null} slug: vendor name: Vendor description: The vendor that issued this invoice options: - name: vendorId type: string label: Vendor ID extendedOptions: - name: displayName type: string label: Display Name ``` **Step 2: Create Features** ```yaml theme={null} # Feature 1 featureType: vendor properties: vendorId: "V001" extendedProperties: displayName: "Acme Corporation" # Feature 2 featureType: vendor properties: vendorId: "V002" extendedProperties: displayName: "Globex Inc" ``` **Step 3: Link to Documents** As invoices are processed, they get linked to the appropriate vendor feature. Now you can: * Search for all documents from a specific vendor * See which vendor a document belongs to * Trigger different processing based on vendor ## Quick Example: Customizing Extraction **Goal:** Use different extraction prompts for 10K vs 10Q documents. **Step 1: Create Feature Type + Features** ```yaml theme={null} # Feature Type slug: sec-filing-type name: SEC Filing Type options: - name: filingType type: string # Features - featureType: sec-filing-type properties: { filingType: "10K" } - featureType: sec-filing-type properties: { filingType: "10Q" } ``` **Step 2: Create Item Type + Items** ```yaml theme={null} # Item Type slug: extraction-prompt name: Extraction Prompt Override options: - name: promptText type: text # Items - knowledgeItemType: extraction-prompt title: "10K Revenue Prompt" properties: promptText: "Extract annual revenue from the 10K financial statements..." - knowledgeItemType: extraction-prompt title: "10Q Revenue Prompt" properties: promptText: "Extract quarterly revenue from the 10Q financial statements..." ``` **Step 3: Create Knowledge Sets** ```yaml theme={null} # When document is 10K, use 10K prompt - name: "10K Extraction Rules" features: - featureType: sec-filing-type properties: { filingType: "10K" } items: - itemRef: "10K Revenue Prompt" # When document is 10Q, use 10Q prompt - name: "10Q Extraction Rules" features: - featureType: sec-filing-type properties: { filingType: "10Q" } items: - itemRef: "10Q Revenue Prompt" ``` ## Detailed Guides Define categories of document metadata with natural keys and display properties Define configurable capabilities like prompt overrides and validation rules End-to-end guide: different prompts for different document types End-to-end guide: conditional validation based on document features How agents build and consume knowledge with human-in-the-loop approval ## Knowledge Set Attachments Knowledge sets can have **set-level file attachments** — files that belong to the knowledge set itself rather than to individual items. These are useful for storing reference documents, images, templates, or other supporting files that apply to the entire set. ### Uploading Attachments Upload attachments via the API using a multipart form POST: ``` POST /api/knowledge-sets/{id}/attachments ``` Each attachment includes: | Field | Description | | -------------- | ----------------------------------------------------------------------------------------------------------- | | `attachmentId` | Custom slug identifier (e.g., `company-logo`). Optional on upload; generated from the file name if omitted. | | `contentHash` | SHA-256 hash of the file content, used for deduplication and storage. | | `fileName` | Original file name. | | `size` | File size in bytes. | | `contentType` | MIME type of the file. | | `uploadedAt` | Timestamp of when the file was uploaded. | | `uploadedBy` | User who uploaded the file. | ### Referencing Attachments in Markdown Attachments can be referenced in knowledge item markdown content using the `attachment://` protocol: ```markdown theme={null} ![Company Logo](attachment://company-logo) See the reference template: ![](attachment://ref-template) ``` When the platform renders the markdown, it resolves `attachment://` references to presigned download URLs for the corresponding files. ### CLI Sync Format When defining knowledge sets via the CLI sync YAML format, attachments can be declared alongside items and features: ```yaml theme={null} name: "Invoice Processing Rules" features: - featureType: vendor properties: { vendorId: "V001" } items: - itemRef: "extraction-prompt-override" attachments: - attachmentId: sample-invoice attachmentPath: ./attachments/sample-invoice.pdf - attachmentId: vendor-logo attachmentPath: ./attachments/vendor-logo.png ``` ### Managing Attachments Use the following API endpoints to manage set-level attachments: * **List** — `GET /api/knowledge-sets/{id}/attachments` * **Upload** — `POST /api/knowledge-sets/{id}/attachments` * **Download** — `GET /api/knowledge-sets/{id}/attachments/{attachmentId}` * **Delete** — `DELETE /api/knowledge-sets/{id}/attachments/{attachmentId}` See the [Knowledge Sets API Reference](/api-reference/knowledge-sets/get-knowledge-sets-id) for full details. ## Expression-Based Matching Knowledge sets use **expression trees** to define when a set of items should be applied to a document. Expressions support logical operators for flexible feature matching. ### Expression Operators | Operator | Behavior | | --------- | ------------------------------------------------------------------------ | | `FEATURE` | Leaf node — matches if the document has the referenced feature (by slug) | | `AND` | All child expressions must evaluate to true | | `OR` | At least one child expression must evaluate to true | | `NOT` | The child expression must evaluate to false | ### Example To match documents that have **both** the "10K" filing type **and** the "Acme Corp" vendor feature: ``` AND ├── FEATURE: sec-filing-type/10K └── FEATURE: vendor/acme-corp ``` To match documents that are **either** 10K **or** 10Q filings: ``` OR ├── FEATURE: sec-filing-type/10K └── FEATURE: sec-filing-type/10Q ``` To match documents that are 10K filings but **not** from Acme Corp: ``` AND ├── FEATURE: sec-filing-type/10K └── NOT └── FEATURE: vendor/acme-corp ``` ### Simple and Advanced Editing You can build a knowledge set's matching expression in one of two modes. **Simple mode** works from a flat list of features. You pick the features that should take part in matching from a palette, then choose a single rule that applies to the whole set: * **Match ALL** — a document must carry *every* selected feature to qualify (an `AND` across the features). * **Match ANY** — a document needs *at least one* of the selected features to qualify (an `OR` across the features). This covers the most common cases and keeps the whole rule readable as a single list. **Advanced mode** exposes the full expression tree, letting you nest `AND`, `OR`, and `NOT` groups to describe conditions a flat list can't capture — for example, "is a 10K *and* is *not* from a particular vendor." You can switch to Advanced mode at any time. Collapsing an advanced expression back to Simple mode is only lossless when it is already a flat list of features. If the expression contains `NOT` conditions or nested groups, the platform asks you to confirm first: simplifying keeps a flat list of the features the expression references but drops the `NOT` and nested-group logic. ### Managing Feature Links via the API The features linked to a knowledge set — the palette that matching expressions pick from — are managed through the `features` array on `POST` and `PUT /api/knowledge-sets`: | Payload | Effect | | ----------------------------- | ------------------------------------------------ | | Field absent or `null` | Existing feature links are left untouched | | `[]` (empty array) | All feature links are removed | | `[...]` (one or more entries) | Links are reconciled to exactly the entries sent | Each entry must reference a feature by `id` or `slug`. References are validated against the knowledge set's organization — an unknown feature is rejected with a clear error, so a request can never link features from another organization. ### Project Scoping A knowledge set can be scoped to a single project or defined at the organization level. During matching, the project is taken from the processing run itself — the context the document is being processed in — not from where the document happens to be stored. A document processed in a project is evaluated against that project's sets plus the organization-level sets; a document processed outside any project context is matched against organization-level sets only. ### How Assessment Works When a document's features change (e.g., a new feature is assigned via an intake, script step, or agent), the platform evaluates all knowledge sets against the document's current feature set. The assessment produces four categories: | Category | Meaning | | -------------------- | ----------------------------------------------------------------- | | **New Matches** | Knowledge sets that now match but did not before | | **Still Matching** | Knowledge sets that continue to match | | **No Longer Match** | Knowledge sets that previously matched but no longer do | | **Snapshot Changed** | Knowledge sets that still match but whose items have been updated | This drives automatic reprocessing — when a document gains or loses a knowledge set match, the platform can trigger the appropriate processing pipeline. ## Set Priority Every knowledge set carries a `priority` from 0 to 10 (default 5). When several applied sets contribute knowledge to the same document, their items are ordered by priority — highest first, with a stable tie-break so sets of equal priority always land in the same deterministic order. The ordering applies everywhere item order is visible: * **Extraction context** — items from higher-priority sets lead the knowledge context supplied to extraction. * **Review form** — the knowledge/instruction panel shows the highest-priority set's instruction first. Priority affects **ordering only** — it never changes which knowledge sets match a document. Matching is decided entirely by the set's expression and its project scope. ## Reference For GitOps deployment of knowledge resources, see: * [Metadata Sync](/guides/kdx-cli/sync/overview) - Deploy via `kdx sync` * [Resource Deployments](/guides/deployment/resource-deployments) - CI/CD integration # Manage Source: https://developer.kodexa.ai/concepts/manage The Manage administration area is the top-level context for organization administration in Kodexa, gathering organization profile, teams, secrets, intakes, custom modules, subscriptions, the model library, and concurrency in one place. **Manage** is a top-level context in the Kodexa platform, alongside **Studio**, **Workflow**, and **Knowledge**. It is the home for organization administration: profile and feature settings, team access, secrets, intakes, custom modules, subscriptions, the model library, and execution concurrency. Manage is served under the `/m` route prefix, and switching to it (from the context switcher) takes you to your organization's Manage home at `/m/o/{organizationId}/home`. The Manage context is role-gated. It is only visible to users who hold the `MANAGE` role or the `PLATFORM_ADMIN` role. Users without either role do not see the Manage context in the switcher. ## Access, Controls and Reporting The Manage organization home presents a single **Access, Controls and Reporting** grid. Each card opens an administration surface for the current organization: Manage the organization name, description, and organization-level features (see [Features](#organization-features) below). Manage the organization's teams and their access. Define tags used to categorize documents across the organization. Manage the organization's secrets and credentials used by modules and integrations. Configure document intake endpoints for automated ingestion. View and manage the custom modules deployed to this organization. Manage billing and subscriptions for the organization. Browse the available AI models, providers, and pricing. View execution concurrency limits and scheduling weight for the organization. These administration surfaces previously lived under Studio's Organization Home. They now live in Manage. Studio's Organization Home retains its project and knowledge-architecture entry points (Projects, Project Templates, Project Status, Activity Plans, Task Statuses, Knowledge Feature Types, and Knowledge Types); the organization administration cards have moved here. ## Organization features Open **Organization Profile** from the Manage grid to reach organization settings. In addition to the **Overview** tab (name, description, and support access), there is a **Features** tab with organization-level toggles. ### Strict team matching Controls how take-next assigns tasks with respect to team matching. * **Off (default)** — soft matching. Take-next prefers team-matched candidates and falls back to team-null candidates when there is no team match. * **On** — strict matching. Only team-matched candidates are assignable; team-null candidates are excluded. Enabling this sets the organization's `features.teamMatchingMode` to `strict`; turning it off clears the key and returns to the soft default. ### Presence tracking Controls whether the platform collects per-user presence and activity telemetry for this organization. * **Off (default)** — no per-user presence or activity telemetry is collected. * **On** — the UI emits periodic presence heartbeats (active/idle, tab visibility) and task-open events tied to named users, surfaced on the organization's activity dashboards. This is an opt-in, customer-controlled switch backed by `features.presenceTrackingEnabled`. Enabling it is a deliberate decision by the organization as the data controller and carries the associated responsibilities for the telemetry collected. See [Organizations → Organization Features](/concepts/organizations#organization-features) for the collection detail and legal considerations. # Module Runtimes Source: https://developer.kodexa.ai/concepts/module_runtimes Module Runtimes provide the execution environment for Kodexa modules, packaging Linux and Python dependencies for Lambda and Kubernetes-based execution. Module runtimes provide the execution environment for Kodexa modules. They package the Linux and Python dependencies needed to run your code consistently across Lambda and Kubernetes-based execution. The runtime is responsible for downloading your module, importing the correct package, injecting execution context, and returning the processed document back to the platform. In practice, the runtime defines the contract between your module code and Kodexa. ## How do Modules interact with the Module Runtime? When you deploy a module into Kodexa you include the module runtime that you want to use. Today, all Kodexa module runtimes have the same interface, but this may change in the future. How the module runtime calls your module is based on how you have declared your module in the module.yml file. ## Inference The most common starting point with working with a module is learning how inference works. Let's take a simple example of a module.yml: ```yaml theme={null} # A very simple first module slug: my-module version: 1.0.0 orgSlug: kodexa type: store storeType: MODEL name: My Module metadata: moduleRuntimeRef: kodexa/base-module-runtime type: module contents: - module/* ``` The key thing to note here is the `moduleRuntimeRef`, which is set to `kodexa/base-module-runtime`. The platform resolves that runtime, provisions the correct execution environment, and uses it to run the module during assistant executions. Module Runtimes When the module runtime is called, it receives the document plus the inference options configured for that step. By convention most modules package their Python code under `module/`, and the runtime calls `infer` from that package. If your archive contains multiple Python packages, the runtime can be told which package to import by setting `metadata.moduleRuntimeParameters.module`. The module runtime will pass the document that we are processing to the module and then the module will return a document. The module runtime will then pass the document back to the platform for further processing. ## Inference with Options In the previous example, we saw how the module runtime would pass the document to the module. In this example, we will see how the module runtime will pass options to the module. First, let's add some inference options to our module.yml file: ```yaml theme={null} # A very simple first module slug: my-module version: 1.0.0 orgSlug: kodexa type: store storeType: MODEL name: My Module metadata: moduleRuntimeRef: kodexa/base-module-runtime type: module inferenceOptions: - name: my_option type: string default: "Hello World" description: "A simple option" contents: - module/* ``` Here we can see we have added an inference option to the module.yml file. This option will be displayed in the UI when the module is used. The user can then change the value of the option and that value will be passed to the module runtime. When we deploy this module update, we now can use that new option in our inference code. ```python theme={null} import logging logger = logging.getLogger(__name__) def infer(document, my_option): logger.info(f"Hello from the module, the option is {my_option}") return document ``` As we can see, the option is passed to the module as a parameter. ## Targeting a Specific Package If your module ZIP contains more than one Python package, set `metadata.moduleRuntimeParameters.module` so the bridge imports the correct package: ```yaml theme={null} metadata: moduleRuntimeParameters: module: my_module ``` The runtime still calls `infer` by default. For event-aware modules it falls back to `handle_event` when that function is present. ## Magic Parameter Injection When a module function is called by the Kodexa bridge, parameters are automatically injected based on the function signature. You only need to declare the parameters you want — the bridge inspects your function signature and passes matching values automatically. ### Available Parameters | Parameter | Type | Description | | ------------------ | ----------------- | ---------------------------------------------------- | | `document` | `Document` | The Kodexa document being processed (inference only) | | `model_base` | `str` | Path to the model's base directory on disk | | `pipeline_context` | `PipelineContext` | The pipeline execution context | | `module_ref` | `str` | Reference to the module being executed | | `module_options` | `dict` | Module-level configuration options | | `assistant` | `Assistant` | The assistant associated with this execution | | `assistant_id` | `str` | The assistant ID | | `project` | `Project` | The project this execution belongs to | | `execution_id` | `str` | The current execution ID | | `status_reporter` | `StatusReporter` | Helper for posting live status updates to the UI | `pipeline_context` is the main entry point for execution metadata. It exposes: * `pipeline_context.document_family` * `pipeline_context.content_object` * `pipeline_context.document_store` * `pipeline_context.context` for the raw event/context payload ### Usage Declare only the parameters your function needs: ```python theme={null} def infer(document, pipeline_context=None, model_base=None): # Only document, pipeline_context, and model_base are injected ... ``` ### Inference Options In addition to the magic parameters above, any inference options declared in your `module.yml` are also injected by name. If you have an inference option called `my_option` then you will get a parameter called `my_option` passed to your inference function. ```python theme={null} def infer(document, my_option): logger.info(f"Hello from the module, the option is {my_option}") return document ``` ### StatusReporter The `status_reporter` parameter provides fire-and-forget status updates that appear in the UI during execution. All calls are safe — errors are logged but never propagated. ```python theme={null} status_reporter.update(title, subtitle=None, status_type="processing") ``` | Argument | Required | Description | | ------------- | -------- | --------------------------------------------------------------------------------------------------------- | | `title` | Yes | Primary status message | | `subtitle` | No | Secondary detail text | | `status_type` | No | One of: `thinking`, `searching`, `planning`, `reviewing`, `processing`, `analyzing`, `writing`, `waiting` | Updates are rate-limited to one per second. ```python theme={null} def infer(document, status_reporter=None, model_base=None): if status_reporter: status_reporter.update("Extracting tables", status_type="processing") # ... do work ... if status_reporter: status_reporter.update("Running classification", subtitle="Page 3 of 12", status_type="analyzing") # ... more work ... return document ``` ## Available Runtimes Kodexa provides several built-in module runtimes for different processing needs: | Runtime | Slug | Description | | ------------------------ | ----------------------------- | ------------------------------------------------------------------------ | | **Base Module Runtime** | `kodexa/base-module-runtime` | Standard Python runtime for custom modules | | **Go Scripting Runtime** | `kodexa/go-scripting-runtime` | Lightweight runtime for inline JavaScript modules | | **Cloud Model Runtime** | `kodexa/cloud-model-runtime` | Runtime for cloud-hosted AI models | | **Excel Runtime** | `kodexa/excel-runtime` | Excel/spreadsheet document processing | | **Azure Runtime** | `kodexa/azure-runtime` | Azure Form Recognizer integration | | **Textract Runtime** | `kodexa/textract-runtime` | AWS Textract integration | | **Google Runtime** | `kodexa/google-runtime` | Google Document AI integration | | **UNO Runtime** | `kodexa/uno-runtime` | Office document conversion via LibreOffice/UNO (Word, Excel, PowerPoint) | | **Agent Model Runtime** | `kodexa/agent-model-runtime` | Runtime for agentic AI processing | ### Pipeline Context Status Handler For step-level progress tracking (progress bars in the UI), use the `pipeline_context.status_handler` callback: ```python theme={null} def infer(document, pipeline_context=None): pages = document.get_nodes() for i, page in enumerate(pages): pipeline_context.status_handler( f"Processing page {i+1}", # message i + 1, # progress len(pages) # progress_max ) # ... process page ... return document ``` # Module Sidecars Source: https://developer.kodexa.ai/concepts/module_sidecars Module sidecars in Kodexa let modules share code by referencing other modules in module.yml so the runtime downloads them alongside your module. The concept of a module sidecar is to allow us to have shared code in a module that other modules can use. The implementation of sidecars is in the module runtime actions. Basically in your module.yml you would refer to one or more module sidecars, these would be the URIs of modules that you want downloaded alongside your module. For example: ```yaml theme={null} # A very simple first module slug: llm-taxonomy-module orgSlug: kodexa version: 1.0.0 type: store storeType: MODEL name: LLM Taxonomy Module metadata: type: module moduleRuntimeRef: kodexa/base-module-runtime moduleSidecars: - kodexa/kodexa-langchain-module ``` In this snippet of the llm-taxonomy-module we see that we want to have the kodexa-langchain-module available as a sidecar. With this in place you can now import classes from the module that is part of this module, for example in your llm-taxonomy-module you can use: ```python theme={null} from kodexa_langchain.utils import get_bedrock_client bedrock_client = get_bedrock_client(region="us-east-1") ``` # Module Skills Source: https://developer.kodexa.ai/concepts/module_skills Skill modules in Kodexa package prompts, configurations, and knowledge that agents discover and use, without containing executable code themselves. Skill modules (`moduleType: skill`) are file packs that agents can discover and use. They provide prompts, configurations, and knowledge to agents without containing executable code. ## How Agents Use Skills When an agent session starts, the platform downloads any skill modules assigned to the agent and extracts them to the agent's container at: ``` /home/kodexa/skills/{org_slug}/{module_slug}/ ``` The agent's system prompt includes the list of available skill directories so it can read and use the files. ## Creating a Skill Module A skill module is defined with a YAML file, just like a model module, but with `moduleType: skill`: ```yaml theme={null} name: My LLM Skills slug: my-llm-skills type: module moduleType: skill metadata: type: skill contents: - prompts/** - tools/** - config.yml ``` ### Directory Structure A typical skill module directory looks like: ``` my-llm-skills/ module.yml # Module definition (the YAML above) prompts/ system.md # System prompt template extraction.md # Extraction instructions tools/ search.yml # Tool definitions config.yml # Skill configuration ``` ### Deploying a Skill Use the Kodexa CLI to deploy: ```bash theme={null} kdx apply -f module.yml ``` The CLI reads the `contents` patterns from the metadata, creates a ZIP of matching files, and uploads them as the module's implementation. ## Assigning Skills to Agents Skills are assigned to agents through `moduleRefs` in the agent's workspace context. This is configured at the project level — when a workspace is created, its context includes the module references that the agent should load. The agent runtime resolves each module reference, checks its `moduleType`, downloads the implementation ZIP, and extracts it to the skills directory. ## Key Differences from Model Modules | | Model Module | Skill Module | | --------------- | ------------------------------------- | ----------------------------------- | | **moduleType** | `model` (default) | `skill` | | **Contains** | Python code | Files (prompts, configs, knowledge) | | **Execution** | Runs via module runtime | Not executed — read by agent | | **Used by** | Scheduled jobs, pipelines, assistants | Agents | | **Runtime ref** | Required (`moduleRuntimeRef`) | Not needed | # Modules Source: https://developer.kodexa.ai/concepts/modules Modules are the core processing units in Kodexa, providing pluggable parsing, transformation, labeling, and inference for documents in projects. Modules are a key component in Kodexa. They are a way to bring intelligent processing to documents, supporting pluggable parsing, transformation, and labeling of documents. While many products have a concept of a module, we think of modules in Kodexa as being a bit different. We think of modules as not just the code and processing, but how the user will experience the module. This means that when we are looking at developing a module we are looking at the user experience and the module code. ## Anatomy of a Module ![Module deployment pipeline](https://files.readme.io/01b683d-image.png) A module is made up of a few key concepts: * **Module Code** — The code that is used to parse, transform and label documents * **Inference Options** — The options that are used to run the module * **Module Taxonomy** — The taxonomy that is used to define the structure of labels that the module uses to "guide" the extraction process * **Additional Taxonomy Options** — Additional options that the module can add to Taxonomies that we will be using for extraction These different parts of the module allow you to build and deploy modules that provide rich ways in which you can capture knowledge about the documents from the user. ## Module Types Every module has a `moduleType` field that determines how the platform uses it. There are two module types: ### Model Modules (`moduleType: model`) Model modules are the traditional Kodexa modules — Python executable code that processes documents. They are used with scheduled jobs, pipelines, and assistants. A model module contains: * Python code with a `handle_event()` entry point * A reference to a **module runtime** that provides the execution environment * Optional inference options, taxonomy definitions, and sidecars This is the default module type. Existing modules without an explicit `moduleType` are treated as models. ### Skill Modules (`moduleType: skill`) Skill modules are file packs (prompts, configurations, knowledge files) that agents can discover and use. Unlike model modules, skills are not executed directly — they are downloaded into the agent's container and made available as readable directories. Skills are ideal for packaging: * Prompt templates and system instructions * Configuration files (e.g., tool definitions, workflows) * Knowledge files and reference data See [Module Skills](/concepts/module_skills) for details on creating and using skill modules. # Organizations Source: https://developer.kodexa.ai/concepts/organizations Organizations in Kodexa are logical containers for users, resources, and settings, enabling team collaboration, access control, and workflow management. The Kodexa platform provides a powerful way to manage and organize your data processing workflows. One of the key concepts in Kodexa is the Organization, which serves as a container for various resources and settings. This article will explore the Organization concept and demonstrate how to interact with it using the Kodexa client. ## What is a Kodexa Organization? A Kodexa Organization is a logical grouping of resources, settings, and users within the Kodexa platform. It allows you to: * Manage access to resources * Configure settings for your team * Organize projects and workflows Each Organization has a unique identifier (slug) and can have various properties such as a name, description, and associated image. ## Interacting with Organizations using the Kodexa Client The Kodexa client provides a convenient way to interact with Organizations programmatically. Let's explore some common operations you can perform using the client. ### Accessing Organization Information To work with an Organization, you typically use the `OrganizationEndpoint` class. This class provides methods to retrieve and modify Organization-related data. ```python theme={null} from kodexa import KodexaClient, OrganizationEndpoint # Assuming you have already initialized the KodexaClient client = KodexaClient(...) # Get the Organization endpoint organization = client.organization("your-org-slug") ``` ### Retrieving Available Resources Organizations have access to various resources such as templates, models, and assistants. You can retrieve these using the following methods: ```python theme={null} # Get available templates templates = organization.available_templates # Get available models models = organization.available_models # Get available assistants assistants = organization.available_assistants ``` ### Managing Products and Subscriptions Organizations can subscribe to different products within the Kodexa platform. Here's how you can manage products and subscriptions: ```python theme={null} # List available products products = client.products.list() # Add a subscription to a product organization.add_subscription(product) # Get current subscriptions subscriptions = organization.get_subscriptions() # Remove a subscription organization.remove_subscription(subscription) ``` ## Example: Adding and Removing a Product Subscription Let's walk through an example of adding and removing a product subscription for an Organization: ```python theme={null} def manage_product_subscription(client: KodexaClient, organization: OrganizationEndpoint): # List available products products = client.products.list() if len(products.content) > 0: # Select the first product test_product = products.content[0] # Add subscription to the product organization.add_subscription(test_product) # Get current subscriptions subscriptions = organization.get_subscriptions() # Remove the subscription we just added for subscription in subscriptions.content: if subscription.organization.slug == organization.slug: organization.remove_subscription(subscription) # Verify that the subscription was removed updated_subscriptions = organization.get_subscriptions() assert len(updated_subscriptions.content) == 0 ``` ## Organization Features Some organization-wide behaviour is controlled from **Manage → Organization Profile → Features**. These settings are stored on the organization's `features` object and apply to every project and user in the organization. (Manage is the top-level administration area introduced in 2026.6 -- see [Manage](/concepts/manage).) ### User Presence & Activity Enabling user presence & activity tracking is a form of workforce monitoring. Your organization acts as the data controller for the collected information and is responsible for ensuring the collection is lawful in your jurisdiction -- including having a valid lawful basis and notifying the affected individuals. Review the on-screen legal & privacy callout before enabling it. Presence tracking is **off by default**. When an administrator enables **Presence tracking** on the Features tab, the platform sets `features.presenceTrackingEnabled = true` on the organization and begins collecting a small set of derived, per-user signals to power presence ("who is online") and task-open timing views: * A periodic presence heartbeat (roughly every two minutes while the tab is visible, throttled while it is hidden) recording whether the user is **active** (recent mouse, keyboard, touch, or scroll input) versus idle, and whether the browser tab is visible. * Task and task-group open events, including how long the task took to load. **No raw input is ever captured** -- no keystrokes, no mouse coordinates, and no scroll positions. Only the derived `active` / `tabVisible` booleans and load timing are recorded, and these signals are tied to the named, authenticated user. Collection is enforced on the server: while the flag is off, the platform silently drops the presence, task, and task-group monitoring events for the organization (failing closed), so nothing is collected without an explicit opt-in. See [Publish UI events batch](/api-reference/events/post-events-ui-batch) for the event ingestion endpoint. ## Conclusion The Kodexa platform's Organization concept provides a flexible way to manage resources and settings for your team. By using the Kodexa client, you can easily interact with Organizations programmatically, allowing you to automate various tasks and integrate Kodexa functionality into your workflows. Remember to refer to the Kodexa documentation for the most up-to-date information on available methods and best practices when working with Organizations and the Kodexa client. # Project Template Structure Reference Source: https://developer.kodexa.ai/concepts/project_template_structure Reference for activity-first Kodexa project template structure: stores, data definitions, task templates, forms, statuses, and workspace config. A `ProjectTemplate` is a metadata blueprint for creating a repeatable project workspace. It should define the resources a project needs to run a business process, while Activity Plans define the automated process itself. ## Top-Level Shape ```yaml theme={null} slug: invoice-operations orgSlug: acme version: 1.0.0 name: Invoice Operations type: projectTemplate description: Workspace setup for invoice intake, review, and posting stores: [] taxonomies: [] dataForms: [] taskTemplates: [] documentStatuses: [] taskStatuses: [] attributeStatuses: [] knowledgeSets: [] workspaces: [] options: {} tags: [] ``` | Field | Required | Description | | ------------- | -------- | ------------------------------- | | `slug` | Yes | Stable template slug | | `orgSlug` | Yes | Owning organization slug | | `version` | Yes | Template version | | `name` | Yes | Display name | | `type` | Yes | Must be `projectTemplate` | | `description` | No | Human-readable purpose | | `helpUrl` | No | Link to implementation guidance | | `tags` | No | Search and categorization tags | ## Variable Substitution Templates can use project and organization variables in slugs, names, descriptions, and references. | Variable | Description | | ----------------- | -------------------- | | `${project.id}` | Unique project ID | | `${project.name}` | Project display name | | `${project.slug}` | Project slug | | `${orgSlug}` | Organization slug | Use `${project.id}` for resources that must be unique across projects. ```yaml theme={null} stores: - slug: "${project.id}-intake" name: "${project.name} Intake" ``` ## Stores Stores define where documents and structured data live. ```yaml theme={null} stores: - slug: "${project.id}-intake" name: "Intake" description: "Incoming documents" storeType: DOCUMENT storePurpose: OPERATIONAL deleteProtection: true ``` | Field | Description | | -------------------- | ------------------------------------------------------- | | `slug` | Store slug | | `name` | Display name | | `description` | Purpose shown to users | | `storeType` | Usually `DOCUMENT` for document workflows | | `storePurpose` | `OPERATIONAL`, `TRAINING`, or another supported purpose | | `deleteProtection` | Prevent accidental deletion | | `documentProperties` | Optional metadata fields for document families | ## Data Definitions Use `taxonomies` to create or reference the Data Definitions used by extracted data and Data Forms. ```yaml theme={null} taxonomies: - ref: "acme/invoice" ``` Prefer references for shared business schemas. Create inline definitions only when the template owns the schema lifecycle. ## Task Templates Task Templates define human work that Activity Plans can create with `CREATE_TASK` steps. ```yaml theme={null} taskTemplates: - slug: invoice-review name: Invoice Review description: Review extracted invoice fields and resolve exceptions taskType: REVIEW ``` Use Task Templates for queue assignment, review intent, task metadata, and links to the forms reviewers need. ## Data Forms Data Forms provide the structured review UI for extracted data. ```yaml theme={null} dataForms: - ref: "acme/invoice-review-form" ``` Bind forms to the Data Definitions they review. Keep form definitions reusable when several projects share the same review experience. ## Statuses Project templates can define document, task, and attribute statuses. Use statuses to make the workflow observable to operators. ```yaml theme={null} taskStatuses: - slug: ready-for-review label: Ready for Review statusType: TODO color: "#3B82F6" - slug: complete label: Complete statusType: DONE locked: true color: "#10B981" ``` Task statuses with status type `DONE` are important for Activity choreography: script and task status changes can advance downstream Activity work. ## Knowledge Sets Knowledge Sets provide reusable classifications, reference data, and extraction context. ```yaml theme={null} knowledgeSets: - ref: "acme/supplier-reference" - ref: "acme/invoice-document-types" ``` Use Knowledge Sets when a workflow should reuse business knowledge across projects instead of embedding that knowledge in scripts or forms. ## Workspace Configuration Workspaces control the operator surface for a project. Enable only the panels the process needs. ```yaml theme={null} workspaces: - name: Operations Workspace slug: operations workspaceStorage: availablePanels: documentStores: true dataForms: true taxonomies: true exceptions: true auditEvents: true navigation: true overview: |- # Operations Workspace Upload documents, review extracted data, and resolve exceptions. ``` Common panels include `documentStores`, `dataForms`, `taxonomies`, `properties`, `exceptions`, `auditEvents`, `navigation`, and `channels`. ## Options Use options for project-level configuration that must vary between projects. ```yaml theme={null} options: options: - name: confidence_threshold type: number label: Confidence Threshold description: Minimum extraction confidence before review is required defaultValue: 0.85 ``` The nested `options.options` shape is intentional in the current template format. ## Activity Plan Bindings Activity Plans are reusable process definitions. When your environment supports binding Activity Plans in templates, treat those bindings as project setup: they should connect the reusable plan to this project's stores, data definitions, task templates, forms, Service Bridges, and statuses. Keep the Activity Plan itself as the source of truth for steps, dependencies, script actions, Service Bridge calls, LLM steps, approvals, and Task creation. ## Validation Checklist 1. Validate the YAML syntax. 2. Create a test project from the template. 3. Confirm stores, statuses, Data Definitions, Task Templates, Data Forms, and workspace panels are created. 4. Confirm the relevant Activity Plan can run against the project resources. 5. Upload representative documents and run the workflow end to end. 6. Verify that Tasks, Activity step logs, document statuses, and audit history are visible to operators. ## Related Guides Understand what belongs in a project template. Model the automated work that runs inside the project. # Project Templates Concept Source: https://developer.kodexa.ai/concepts/project_templates Project templates define reusable Kodexa project blueprints with stores, data definitions, Activity Plan bindings, forms, tasks, and workspace setup. Project templates help you create repeatable Kodexa projects without rebuilding the same workspace setup each time. They capture the project boundary: stores, data definitions, forms, statuses, knowledge, task templates, workspace panels, and the bindings a project needs to run its Activities. Use project templates for the setup that changes by project. Keep reusable business process logic in Activity Plans so multiple projects can adopt the same process shape with different stores, data definitions, forms, permissions, and integrations. ## What a Template Should Capture | Area | Typical template responsibility | | ---------------- | ---------------------------------------------------------------------- | | Stores | Create the intake, review, exception, and output document stores | | Data Definitions | Reference or create the structured data model for the process | | Data Forms | Provide the review UI used by Tasks | | Task Templates | Define human review work that Activity Plans can create | | Statuses | Define project-specific document, task, and attribute states | | Knowledge | Bind knowledge sets and feature types used by the process | | Workspace | Configure panels and default project workspace behavior | | Resources | Attach service bridges, modules, prompts, and other reusable resources | ## Minimal Template ```yaml theme={null} slug: invoice-operations orgSlug: acme version: 1.0.0 name: Invoice Operations type: projectTemplate description: Workspace setup for AP invoice intake and review stores: - slug: "${project.id}-intake" name: "Invoice Intake" description: "Upload incoming invoices here" storeType: DOCUMENT storePurpose: OPERATIONAL workspaces: - name: Review Workspace slug: review workspaceStorage: availablePanels: documentStores: true dataForms: true taxonomies: true exceptions: true overview: |- # Invoice Operations Upload documents, review extracted data, and resolve exceptions from this workspace. ``` ## Activity-First Project Setup An Activity Plan should model the business process. A project template should provide the local project resources that plan needs. ```text theme={null} Project Template -> creates project stores, statuses, forms, and task templates -> binds reusable data definitions and knowledge -> prepares the workspace where people review Tasks Activity Plan -> runs extraction, classification, scripts, bridge calls, LLM steps, and approvals -> creates Tasks when human judgment is needed -> records step outputs, logs, status, and audit history ``` This split keeps project setup reusable without hiding the process logic inside project-specific wiring. ## Template Design Guidance * Start with the real business workflow, then identify the project resources it needs. * Put repeatable process logic in Activity Plans. * Put human review configuration in Task Templates and Data Forms. * Keep store names and slugs project-specific with `${project.id}` where uniqueness matters. * Bind shared Data Definitions and Knowledge Sets by reference instead of duplicating them. * Keep workspace panels focused on repeated operator work: stores, data forms, Data Definitions, exceptions, audit trail, and navigation. ## Next Steps See the template fields and structure. Build an activity-first project template. # Projects Source: https://developer.kodexa.ai/concepts/projects Projects in Kodexa bind document stores, data definitions, Activity Plans, Tasks, forms, knowledge, and permissions into one business-process workspace. A Project is the workspace boundary for a document-heavy business process. It brings together the documents, data definitions, review forms, Activity Plans, Tasks, knowledge, integrations, and access rules needed to run a real workflow. Projects do not define the whole operating model by themselves. The operating model comes from the mental model introduced in Concepts: * **Activities** run automated work. * **Tasks** represent human review and exception handling. * **Data Definitions** describe the structured business data you want from unstructured documents. * **Data Forms** give reviewers a usable surface for reviewing that data. * **Knowledge** captures reusable business context, classifications, and rules. ## What a Project Contains | Resource | Role in the workflow | | ---------------- | ------------------------------------------------------------------- | | Document Stores | Where incoming files and their Kodexa document representations live | | Data Stores | Structured operational data associated with the process | | Data Definitions | The object and field model for extracted business data | | Activity Plans | Reusable automated processes bound into this project | | Task Templates | Human review surfaces and assignments that Activities can create | | Data Forms | Review and correction UI for extracted data | | Knowledge Sets | Reference knowledge, classifications, and enrichment rules | | Statuses | Project-specific document, task, and attribute states | | Service Bridges | External systems the workflow calls through governed integrations | ## Why Projects Matter Projects let you keep reusable platform assets and local business configuration separate. An Activity Plan can be defined once for an organization, then bound to multiple projects that have different stores, data definitions, review queues, or downstream systems. That separation is important when the same process shape repeats across business units. An invoice intake workflow, claims intake workflow, or covenant compliance workflow may share the same Activity structure while still using project-specific stores, forms, statuses, and permissions. ## Working with Projects in the SDK Projects are owned by organizations. Use the SDK to find an organization, then list or load its projects. ```python theme={null} from kodexa.platform import KodexaClient client = KodexaClient() organization = client.organizations.find_by_slug("acme") projects = organization.projects.list().to_df() print(projects) project = organization.projects.find_by_name("Invoice Operations") print(project.id) ``` Once you have a project, use its resource accessors to inspect the project boundary. ```python theme={null} document_stores = project.document_stores.to_df() print(document_stores) ``` ## Project Design Checklist When modeling a new business process, define these decisions explicitly: 1. Which documents enter the process, and where they should be stored. 2. Which business objects and fields must be extracted. 3. Which Activity Plan runs the automated work. 4. Which Tasks are created when judgment or exception handling is required. 5. Which Data Forms reviewers use to complete those Tasks. 6. Which Service Bridges connect to downstream systems. 7. Which statuses show the state of documents, Tasks, and attributes. ## Next Steps Understand automated business process runs. Understand human review work inside the workflow. Configure the steps that run automated work. Package reusable project setup for repeatable deployments. # Security Model Source: https://developer.kodexa.ai/concepts/security_model The Kodexa Fine-Grained Access Control (FGAC) system uses teams, roles, and permissions to govern access at organization and project levels. Kodexa uses a **Fine-Grained Access Control (FGAC)** system built on teams, roles, and permissions. This model controls what users can see and do across the platform at both the organization and project levels. ## Overview The security model has four key concepts: 1. **Users** are people who log in to the platform 2. **Teams** group users together 3. **Roles** define a set of permissions 4. **Assignments** grant a team a role at the organization or project level ```mermaid theme={null} graph LR U[User] -->|member of| T[Team] T -->|assigned role in| O[Organization] T -->|assigned role in| P[Project] O -->|contains| P R[Role] -->|defines| Perms[Permissions] T ---|with role| R ``` Users never receive permissions directly. Instead, permissions flow through the team-role-assignment chain: **User → Team → Assignment (org or project) → Role → Permissions** A user's **platform role** (set when the user is first provisioned) is a separate concept from the FGAC roles, teams, and permissions described below. Platform roles decide which top-level surfaces a user can reach (Studio, Workflow, Knowledge); FGAC decides what that user can do with individual resources inside an organization or project. ## Platform Roles & User Provisioning In addition to the FGAC model, every user carries one or more **platform roles**. Platform roles are stored on the user record (not through teams or assignments) and gate access to the platform's top-level surfaces. They are distinct from the organization- and project-level FGAC roles described in the rest of this page. ### Platform Roles | Role | Description | | ---------------- | ------------------------------------------------------------------------------------------------------------ | | `PLATFORM_ADMIN` | Full platform administrator. Bypasses FGAC permission checks. | | `SUPPORT` | Support access. | | `STUDIO` | Access to the Studio surface. | | `WORKFLOW` | Access to the Workflow surface. | | `WORKFLOW_KIOSK` | Restricted, task-mode kiosk access. Reaches the Workflow surface only, locked into the kiosk task-review UI. | | `KNOWLEDGE` | Access to the Knowledge surface. | A user with only `WORKFLOW_KIOSK` reaches the Workflow surface and is locked into the kiosk (task-review) UI. A user who holds both `WORKFLOW` and `WORKFLOW_KIOSK` can toggle kiosk mode on and off from their organization home. ### Default Role on Provisioning When a new user signs in for the first time, the platform provisions their user record from the Auth0 identity token and assigns platform roles using the following precedence: ```mermaid theme={null} flowchart TD A[New user first login] --> B{Email ends in @kodexa.com?} B -->|Yes| C[Roles = PLATFORM_ADMIN] B -->|No| D{Token provided any roles?} D -->|No| E[Roles = WORKFLOW_KIOSK] D -->|Yes| F[No platform role assigned; admin must grant one] ``` 1. **`@kodexa.com` email → `PLATFORM_ADMIN`.** Users with a `kodexa.com` email address are automatically made platform admins. This takes precedence even when the token carries no roles. 2. **No token roles → `WORKFLOW_KIOSK`.** If the Auth0 token carries no roles for a non-`kodexa.com` user, the user defaults to the restricted `WORKFLOW_KIOSK` platform role (task-mode kiosk). This is the entry point for new users until an administrator grants broader roles. 3. **Token roles are not adopted as platform roles.** Roles carried in the identity token are recorded on the user record's raw `roles_json` field but are NOT copied into the platform-role set that gates surfaces. The `WORKFLOW_KIOSK` default is applied only when the token carries no roles; when the token does carry roles for a non-kodexa.com user, the platform neither forces the kiosk default nor grants any platform role from the token — the user starts with no platform roles until an administrator assigns them. The default only applies at first provisioning. Once a user exists, an administrator changes their platform roles directly on the user record — for example, to grant `WORKFLOW`, `STUDIO`, or `KNOWLEDGE` access beyond the initial kiosk role. ## Permission Format Every permission is a `resource:action` pair. For example: | Permission | Meaning | | ----------------------- | ----------------------------- | | `document-family:read` | Can read document families | | `task:lock` | Can lock tasks | | `document-store:upload` | Can upload to document stores | | `*:read` | Can read any resource type | | `*:*` | Full access to everything | The `*` wildcard matches any resource type or action. This allows roles to grant broad access (like `*:read` for read-only) or full access (`*:*` for admins). ### Standard Actions These actions map to standard CRUD operations: | Action | HTTP Method | Description | | -------- | ----------- | -------------------- | | `create` | POST | Create new resources | | `read` | GET | View resources | | `update` | PUT/PATCH | Modify resources | | `delete` | DELETE | Remove resources | ### Custom Actions Beyond CRUD, the platform defines custom actions for domain-specific operations: | Action | Description | | ----------------- | -------------------------------------- | | `lock` | Lock a document family or task | | `unlock` | Unlock a document family or task | | `reprocess` | Reprocess a document family | | `rename` | Rename a document family | | `label` | Add or remove labels | | `assign` | Manage assignees on tasks or documents | | `assign-next` | Auto-assign next available task | | `update-status` | Change status of a task or document | | `upload` | Upload files to stores or modules | | `export` | Export document data | | `assess` | Assess a document family | | `manage-features` | Add or remove knowledge features | | `activate` | Activate an assistant | | `deactivate` | Deactivate an assistant | | `trigger` | Trigger an assistant event or schedule | | `invoke` | Invoke an agent | | `cancel` | Cancel an execution | ## Teams Teams are the bridge between users and access control. A team belongs to an organization and can have any number of members. ```mermaid theme={null} graph TD subgraph Organization T1[Extraction Team] T2[Review Team] T3[Admin Team] end U1[Alice] -->|member| T1 U1 -->|member| T3 U2[Bob] -->|member| T1 U2 -->|member| T2 U3[Carol] -->|member| T2 ``` Key points about teams: * A user can belong to multiple teams * Each team has a unique slug within its organization (e.g., `extraction-team`) * Teams are managed from the **Members & Teams** page in organization settings * Task templates can reference teams by slug for automatic task routing ## Roles Roles define what a team can do. The platform ships with **system-defined roles** that cover common access patterns. If you need a custom role tailored to your organization, contact Kodexa Support. ### System-Defined Roles ```mermaid theme={null} graph TD subgraph "Organization-Level Roles" OO["org-owner
Full access (non-removable)"] OA["org-admin
Full access"] OM["org-member
All resource operations"] OV["org-viewer
Read + export only"] end subgraph "Project-Level Roles" PA["project-admin
Full access"] PE["project-editor
CRUD + all custom actions, no delete"] PC["project-contributor
Create, read, update, upload"] PV["project-viewer
Read + export only"] end OO -.->|"*:*"| ALL[All Permissions] OA -.->|"*:*"| ALL PA -.->|"*:*"| ALL ``` #### Organization Roles | Role | Permissions | Use Case | | -------------- | ----------------------------- | ------------------------------------------------------------------------------- | | **org-owner** | `*:*` (full access) | The organization creator. Cannot be removed. | | **org-admin** | `*:*` (full access) | Administrators who manage teams, roles, and all resources. | | **org-member** | All CRUD + all custom actions | Regular members who work with resources but don't manage teams or org settings. | | **org-viewer** | `*:read`, `*:export` | Stakeholders or auditors with read-only access across the org. | #### Project Roles | Role | Permissions | Use Case | | ----------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------ | | **project-admin** | `*:*` (full access) | Project leads who manage all project resources. | | **project-editor** | Create, read, update + all 17 custom actions (no delete) | Team members who actively work with documents, tasks, and knowledge. | | **project-contributor** | Create, read, update, upload, update-status | Users who submit data and update statuses but don't manage assignments or reprocess. | | **project-viewer** | Read + export | Users who review results without modifying anything. | ## Access Assignment Teams receive roles through **assignments** at two levels: ```mermaid theme={null} graph TD subgraph "Team: Extraction Team" TA1["Org Assignment
Role: org-member
Scope: Acme Corp"] TA2["Project Assignment
Role: project-editor
Scope: Invoice Project"] TA3["Project Assignment
Role: project-viewer
Scope: Contract Project"] end ``` ### Organization-Level Assignment When a team is assigned a role at the organization level, all team members receive those permissions across **every resource in the organization**. ```mermaid theme={null} sequenceDiagram participant Admin participant API participant DB Admin->>API: POST /api/team-org-assignments Note right of Admin: { teamId, organizationId, roleId } API->>DB: Create assignment DB-->>API: Assignment created API-->>Admin: 201 Created Note over Admin,DB: All team members now have
the role's permissions org-wide ``` ### Project-Level Assignment When a team is assigned a role at the project level, members receive those permissions only for **resources linked to that project**. ```mermaid theme={null} sequenceDiagram participant Admin participant API participant DB Admin->>API: POST /api/team-project-assignments Note right of Admin: { teamId, projectId, roleId } API->>DB: Create assignment DB-->>API: Assignment created API-->>Admin: 201 Created Note over Admin,DB: Team members now have
the role's permissions
within this project only ``` ## How Permission Checks Work When a user makes an API request, the platform evaluates permissions in this order: ```mermaid theme={null} flowchart TD A[API Request] --> B{User authenticated?} B -->|No| C[401 Unauthorized] B -->|Yes| D{Platform Admin?} D -->|Yes| E[Allowed] D -->|No| F{Check org-level
permissions} F -->|Granted| E F -->|Not granted| G{Find projects
linking this resource} G --> H{Check project-level
permissions} H -->|Granted| E H -->|Not granted| I[403 Forbidden] ``` The check flow in detail: 1. **Authentication** -- Is the user logged in? If not, return 401. 2. **Platform Admin bypass** -- Platform admins (`platform_admin` role) skip all permission checks. 3. **Organization-level check** -- Look up the user's teams, find team-org assignments for this org, collect all permissions from assigned roles. If the required permission matches, allow. 4. **Project-level check** -- Find which projects this resource is linked to (via `project_resources`). For each project, look up the user's team-project assignments and check permissions. If any project grants the required permission, allow. 5. **Deny** -- If no check grants the permission, return 403. ### Effective Permissions A user's effective permissions in a given context are the **union** of: * Permissions from all org-level team assignments in that organization * Permissions from all project-level team assignments for the relevant project You can query a user's effective permissions: ``` GET /api/account/permissions?organizationId={orgId}&projectId={projectId} ``` Response: ```json theme={null} { "permissions": ["*:create", "*:read", "*:update", "*:lock", "*:unlock", "..."], "organizationId": "org-123", "projectId": "proj-456" } ``` ## Complete Example Here's a full example showing how Alice gets access to work on documents in the Invoice Project: ```mermaid theme={null} graph LR subgraph Users Alice end subgraph "Acme Corp (Organization)" subgraph Teams ET[Extraction Team] end subgraph Projects IP[Invoice Project] end subgraph Roles PE[project-editor] end end Alice -->|member of| ET ET -->|assigned| TPA[Team-Project Assignment] TPA -->|in project| IP TPA -->|with role| PE PE -->|grants| P1["*:create"] PE -->|grants| P2["*:read"] PE -->|grants| P3["*:update"] PE -->|grants| P4["*:lock"] PE -->|grants| P5["*:unlock"] PE -->|grants| P6["...17 custom actions"] ``` **Setup steps:** 1. An admin creates the **Extraction Team** in Acme Corp 2. Alice is added as a **team member** 3. The team is given a **project-level assignment** in Invoice Project with the **project-editor** role 4. Alice can now create, read, update, lock, unlock, reprocess documents -- but **cannot delete** anything in Invoice Project If Alice also needs read-only access to the Contract Project, the admin creates a second assignment for the Extraction Team in that project with the **project-viewer** role. ## API Endpoints | Endpoint | Purpose | | ---------------------------------------- | ---------------------------------------- | | `GET/POST /api/teams` | Manage teams | | `GET/POST /api/team-members` | Manage team membership | | `GET/POST /api/team-org-assignments` | Assign teams to orgs with roles | | `GET/POST /api/team-project-assignments` | Assign teams to projects with roles | | `GET /api/roles` | List available roles | | `GET /api/permissions` | List available permissions | | `GET /api/role-permissions` | List role-permission links | | `GET /api/account/permissions` | Get current user's effective permissions | # Storage Source: https://developer.kodexa.ai/concepts/storage Kodexa storage uses two store types: Document Stores for files and their KDDB representations, and Data Stores for structured data extracted from them. The Kodexa platform incorporates a unique system called "stores" to manage and store information efficiently. This system is divided into two primary types of stores: 1. **Document Stores**: These are specialized in storing files and their corresponding document representations. 2. **Data Stores**: These focus on holding extracted data objects and attributes identified within documents stored in a Document Store. ## Concept and Design Stores in Kodexa are designed to manage both native files and their associated "Document" representations, which contain unstructured data. The process involves defining a Data Structure to label documents, enabling the platform to convert these labeled documents into a structured format. ## Detailed Explanation of Store Types ### Document Stores Document Stores play a pivotal role in managing files that are subject to parsing, labeling, and conversion into structured data. The term "document" here implies that upon uploading a file (like a PDF), Kodexa creates a "container." This container holds: * The original file (referred to as the native file). * One or more Kodexa Documents representing the semi-structured version of the native file. These containers, known as Document Families, are integral as they encompass both the native files and their document representations. This setup allows for the independent labeling of documents by models or humans. ### Data Stores Data Stores are engineered to manage structured data extracted from labeled documents stored in a Document Store. They are connected to a Data Definition, which: * Formalizes data structure into groups and individual data attributes. * Stores actual data points and their related groups in the Data Store, with a lineage tracing back to the document representation in the Document Store. ## Interrelation of Store Types Kodexa is strategically designed to facilitate the transformation of a document from a native file to a structured data set. This transformation process involves both Document Stores and Data Stores. Next is an overview of how each of these two store types interacts within the Kodexa ecosystem. # Tasks Source: https://developer.kodexa.ai/concepts/tasks Tasks in Kodexa represent human-in-the-loop review steps inside Activities for verifying extracted data, resolving exceptions, and approvals. A **Task** is a unit of human work inside an Activity. It is owned, reviewable, completable, and tied to the business process when the platform needs a person to make a decision, resolve an exception, or confirm extracted data. Tasks are not the whole workflow and they are not the primary process model. They are how people participate in an Activity. ## What Tasks Are For Tasks exist where automation should pause for judgment: * Review extracted fields before a process continues * Resolve validation errors or missing data * Approve or reject a business decision * Route an exception to the right queue * Capture a user decision that determines the next Activity step The Task gives the person the right document context, data form, exceptions, and actions. The Activity keeps the process state and continues after the Task is completed. ## Task Shape | Area | Purpose | | ---------------- | ------------------------------------------------------------------ | | Owner or queue | Who should work on it? | | Status | Is it open, in progress, blocked, complete, rejected, or canceled? | | Document context | Which document, packet, or extracted data is being reviewed? | | Data form | Which review UI should the user see? | | Actions | What decisions can the user make? | | Exceptions | Which issues need attention before completion? | | Audit | Who completed the work, when, and with what result? | ## Tasks in an Activity An Activity creates a Task when a configured step needs a person. The Task completion result becomes workflow state that can drive the next Activity step. ```mermaid theme={null} sequenceDiagram participant Activity participant Task participant Reviewer Activity->>Task: Create review task Task->>Reviewer: Assign work Reviewer->>Task: Review data and choose action Task->>Activity: Complete with result Activity->>Activity: Continue or branch ``` ## What Belongs in a Task A Task should be specific enough that a person can finish it without reconstructing the entire process. Good Tasks: * Review this invoice extraction * Resolve these validation exceptions * Approve this loan packet summary * Confirm this vendor match * Reject this claim intake with a reason Weak Tasks: * Process this business workflow * Run invoice automation * Handle the loan process * Figure out what happened If the work is the overall process, model it as an Activity. If the work is a human decision point inside the process, model it as a Task. ## Tasks and Data Forms Most Tasks are paired with a Data Form. The form controls how extracted data, validation errors, document context, and user actions appear to the reviewer. The Task owns the work item. The Data Form owns the review experience. ```mermaid theme={null} flowchart LR Task["Task"] --> Form["Data Form"] Form --> Data["Extracted Data"] Form --> Doc["Document Context"] Form --> Exceptions["Exceptions"] Form --> Actions["Complete / Reject / Route"] ``` See [Data Forms](/guides/data-forms/introduction) for the schema-driven review UI model. ## Design Rule Use a Task when you need a person to show up in the workflow. Use an Activity when you need the platform to carry the business process forward. # Working with a Document Source: https://developer.kodexa.ai/concepts/working_with_a_document Create, save, load, and manipulate Kodexa Documents using the Document SDK in Python and TypeScript, with examples for each common operation. Kodexa is a powerful document processing platform that allows developers to work with documents in a structured and efficient manner. This guide will walk you through the basics of creating, saving, loading, and working with Kodexa Documents. ## Creating a Document You can create documents in several ways using the Kodexa Document SDK: ```python Python theme={null} from kodexa_document import Document # Create an empty document doc = Document() # Create from text doc = Document.from_text("Some content") # Create from JSON doc = Document.from_json(json_string) ``` ```typescript TypeScript theme={null} import { Kodexa } from '@kodexa/document'; await Kodexa.init(); // Create an empty document const doc = await Kodexa.createDocument(); // Create from text const doc = await Kodexa.fromText("Some content"); // Create from JSON const doc = await Kodexa.fromJson(jsonString); ``` ## Saving a Document You can save a Kodexa Document to a file or a store. Documents are saved in the KDDB format (a SQLite database): ```python Python theme={null} doc.to_kddb('my-document.kddb') doc.close() ``` ```typescript TypeScript theme={null} const blob = await doc.toBlob(); // Save blob to file or upload await doc.dispose(); ``` By convention, we use the `.kddb` extension for Kodexa Document Database files. ## Loading a Document To load a previously saved Kodexa Document: ```python Python theme={null} another_document = Document.from_kddb('my-document.kddb') another_document.close() ``` ```typescript TypeScript theme={null} const blob = await fetch('my-document.kddb').then(r => r.blob()); const doc = await Kodexa.fromBlob(blob); await doc.dispose(); ``` ### Detached Documents Sometimes you may want to make changes to a document without affecting the original file. In Python, you can load the document in detached mode: ```python theme={null} detached_document = Document.from_kddb('my-document.kddb', detached=True) ``` ## Anatomy of a Kodexa Document The Kodexa Document Model provides a flexible and powerful way to represent structured and unstructured documents. At its core, it consists of a Document object that contains metadata and a hierarchical tree of ContentNodes, each of which can have features and tags attached to them. Let's explore the key components of the model. ## Core Components ### Document Structure ```mermaid theme={null} graph TD A[Document] --> B[Metadata] A --> C[Content Node Tree] A --> D[Source Metadata] A --> E[Native Documents] A --> F[Data Objects & Attributes] A --> G[Audit Trail] C --> H[Root Content Node] H --> I[Child Node 1] H --> J[Child Node 2] I --> K[Grandchild Node] style A fill:#f9f,stroke:#333,stroke-width:2px style C fill:#bbf,stroke:#333,stroke-width:2px style H fill:#bfb,stroke:#333,stroke-width:2px ``` A Kodexa Document consists of: 1. **Document Metadata**: Flexible dictionary-based metadata about the document 2. **Content Node Tree**: Hierarchical structure of content nodes 3. **Source Metadata**: Information about the document's origin (filename, MIME type, checksum) 4. **Native Documents**: Embedded binary files (original PDFs, images, etc.) 5. **Data Objects & Attributes**: Structured extracted data organized by Data Definition 6. **Audit Trail**: Change history and revision tracking ### Content Nodes ContentNodes are the building blocks of the document structure. Each ContentNode represents a logical section of content and has the following properties: ```mermaid theme={null} graph LR A[ContentNode] --> B[Node Type] A --> C[Content] A --> D[Features] A --> E[Children] A --> F[Tags] A --> G[Index] style A fill:#f9f,stroke:#333,stroke-width:2px ``` Key attributes: * **node\_type**: Identifies the type of node (e.g., 'page', 'line', 'word', 'cell') * **content**: The actual text content of the node * **features**: List of attached features (metadata) * **tags**: Annotations linking content to extracted data * **children**: Child nodes in the hierarchy * **id**: Unique numeric identifier * **index**: Position among siblings * **virtual**: Whether the node is a virtual/synthesized node ### Features Features are flexible metadata containers attached to ContentNodes: ```mermaid theme={null} graph TD A[Feature Types] --> B[Tags] A --> C[Spatial] A --> D[Custom Features] B --> E[Named Tags] B --> F[Tag Values] C --> G[Bounding Boxes] C --> H[Coordinates] style A fill:#f9f,stroke:#333,stroke-width:2px style B fill:#bbf,stroke:#333,stroke-width:2px style C fill:#bfb,stroke:#333,stroke-width:2px ``` Each feature has: * **feature\_type**: Category of the feature (e.g., 'tag', 'spatial') * **name**: Identifier for the feature * **value**: The feature's data (always stored as an array) ## Working with Documents ### Creating Document Structure ```python Python theme={null} from kodexa_document import Document doc = Document() # Create a root node root = doc.create_node(node_type="document") doc.content_node = root # Add child nodes page = doc.create_node(node_type="page", content="Page content") root.add_child(page) line = doc.create_node(node_type="line", content="A line of text") page.add_child(line) ``` ```typescript TypeScript theme={null} import { Kodexa } from '@kodexa/document'; await Kodexa.init(); const doc = await Kodexa.createDocument(); const root = await doc.getRoot(); const node = await doc.createNode("page"); await root.addChild(node); ``` ### Working with Features ```python Python theme={null} # Add a feature node.add_feature("tag", "paragraph", "body") # Add spatial information (bounding box) node.set_bbox([10, 20, 100, 200]) # Get feature value value = node.get_feature_value("tag", "paragraph") # Check if feature exists has_it = node.has_feature("tag", "paragraph") # Get all features features = node.get_features() # Get features by type tag_features = node.get_features_of_type("tag") # Remove a feature node.remove_feature("tag", "paragraph") ``` ```typescript TypeScript theme={null} // Add a feature await node.setFeature("tag", "paragraph", "body"); // Set bounding box await node.setBBox(10, 20, 100, 200); // Get feature value const value = await node.getFeatureValue("tag", "paragraph"); // Check if feature exists const hasIt = await node.hasFeature("tag", "paragraph"); // Get all features const features = await node.getFeatures(); // Get features by type const tagFeatures = await node.getFeaturesOfType("tag"); ``` ### Working with Tags Tags are annotations on nodes that link content to extracted data: ```python Python theme={null} # Apply a tag node.tag("company_name", confidence=0.95, value="Acme Corp") # Check if node has a tag if node.has_tag("company_name"): tags = node.get_tags() for tag in tags: print(f"Tag: {tag.value} (confidence: {tag.confidence})") # Remove a tag node.remove_tag("company_name") ``` ```typescript TypeScript theme={null} // Apply a tag await node.tag("company_name"); // Apply a tag with options await node.tagWithOptions("company_name", { confidence: 0.95 }); // Check if node has a tag if (await node.hasTag("company_name")) { const tags = await node.getTags(); console.log(tags); } // Remove a tag await node.removeTag("company_name"); ``` ## Node Navigation and Selection The document model provides powerful ways to navigate and select nodes: 1. **Direct Navigation**: * `get_children()` / `getChildren()`: Get immediate child nodes * `get_parent()` / `getParent()`: Get parent node * `next_node()` / `nextNode()`: Get next sibling * `previous_node()` / `previousNode()`: Get previous sibling * `get_child(index)` / `getChild(index)`: Get child by index 2. **Selector-based Navigation**: ```python Python theme={null} # Select all nodes of type 'page' pages = document.select("//page") # Select nodes with specific tags tagged = document.select("//*[hasTag('paragraph')]") # Select first match only first_page = document.select_first("//page") ``` ```typescript TypeScript theme={null} // Select all nodes of type 'page' const pages = await document.select("//page"); // Select nodes with specific tags const tagged = await document.select("//*[hasTag('paragraph')]"); // Select first match only const firstPage = await document.selectFirst("//page"); ``` ## Data Objects and Attributes Documents can contain structured extracted data organized by Data Definition: ```python Python theme={null} # Get all data objects objects = doc.data_objects.get_all() # Get attributes for an object attrs = doc.data_attributes.get_for_data_object(obj_id) ``` ```typescript TypeScript theme={null} // Get all data objects const objects = await doc.dataObjects.getAll(); // Get attributes for an object const attrs = await doc.dataAttributes.getForDataObject(objId); ``` ## Document Metadata ```python Python theme={null} # Access metadata doc.metadata["schema_version"] = "1.0" print(doc.metadata) # Source metadata print(doc.source) ``` ```typescript TypeScript theme={null} // Set metadata await doc.setMetadataValue("schemaVersion", "1.0"); // Get metadata const meta = await doc.getMetadata(); ``` ## Working with Document Content Kodexa uses a powerful selector syntax to find and manipulate content within documents. Selectors work similarly to CSS selectors or XPath, allowing you to build queries that can be executed on a document instance. ### Basic Selector Example To find all content nodes matching a regex: ```python theme={null} nodes = document.select('//*[contentRegex("Name")]') ``` This returns a list of the matching content nodes. ### Selector Syntax The selector syntax is composed of several parts: 1. Axis & Node Type: Defines how to navigate the tree structure. 2. Predicate: Further filters the selected nodes based on conditions. ### Axis Examples * `//`: Current node and all children * `/`: Root node * `.`: Current Node (or root if from the document) * `./line/.`: All nodes of type line under the current node * `parent::line`: Any node in the parent structure of this node that is of node type line ### Predicate Functions Predicates can use various functions, such as: * `contentRegex`: Matches content against a regular expression * `typeRegex`: Matches node type name against a regular expression * `hasTag`: Checks if a node has a specific tag * `hasFeature`: Checks if a node has a specific feature * `content`: Returns the content of the node * `uuid`: Returns the UUID of the node ### Operators Operators can be used to combine functions: * `|`: Union the results of two sides * `=`: Test that two sides are equal * `and`: Boolean AND operation * `or`: Boolean OR operation ### Pipeline Selectors Kodexa also supports "pipeline" selectors, allowing you to chain multiple selectors: ```python theme={null} document.select('//word stream //*[hasTag("ORG")] stream * [hasTag("PERSON")]') ``` This example streams all nodes of type word, then filters those with the "ORG" tag, and finally filters those with the "PERSON" tag. ## Best Practices 1. **Node Types**: Use consistent node types throughout your document to make selection and processing easier 2. **Features**: * Use features to add metadata rather than modifying node content * Keep feature names consistent across your application * Use appropriate feature types for different kinds of metadata 3. **Content Structure**: * Maintain a logical hierarchy that reflects the document's structure * Use indexes appropriately to maintain node order * Consider using virtual nodes for sparse content 4. **Performance**: * Use selectors efficiently * Batch operations when possible * Use KDDB format for large documents 5. **Resource Management**: * Always close documents when done (Python: `doc.close()`, TypeScript: `await doc.dispose()`) * Use context managers in Python: `with Document() as doc:` ## Error Handling The document model includes error handling through exceptions: ```python Python theme={null} from kodexa_document import Document, ContentException try: doc = Document.from_kddb("my-document.kddb") # ... work with document except Exception as e: print(f"Error: {str(e)}") finally: doc.close() ``` ```typescript TypeScript theme={null} try { const doc = await Kodexa.fromBlob(blob); // ... work with document await doc.dispose(); } catch (error) { console.error("Error:", error); } ``` ## Conclusion Kodexa Documents provide a powerful way to work with structured content. By understanding how to create, save, load, and query documents using selectors, you can efficiently process and analyze complex document structures in your applications. # Working with Exceptions Source: https://developer.kodexa.ai/concepts/working_with_exceptions Use the Kodexa ContentException class to flag, track, and resolve issues found during document processing, with severity, type, and metadata fields. In the realm of document processing and management, handling exceptions effectively is crucial for maintaining data integrity and streamlining workflows. Kodexa, a powerful document processing framework, offers a sophisticated ContentException class that allows for detailed and flexible exception handling. This article delves into the advanced features of ContentException and how to leverage them in your document management processes. The ContentException class in Kodexa is defined with a rich set of attributes: ```python theme={null} class ContentException(BaseModel): id: Optional[str] = Field(None) uuid: Optional[str] = None change_sequence: Optional[int] = Field(None, alias="changeSequence") created_on: Optional[StandardDateTime] = Field(None, alias="createdOn") updated_on: Optional[StandardDateTime] = Field(None, alias="updatedOn") tag: Optional[str] = None message: Optional[str] = None exception_type: Optional[str] = Field(None, alias="exceptionType") severity: Optional[str] = None exception_details: Optional[str] = Field(None, alias="exceptionDetails") group_uuid: Optional[str] = Field(None, alias="groupUuid") tag_uuid: Optional[str] = Field(None, alias="tagUuid") content_object: Optional[ContentObject] = Field(None, alias="contentObject") ``` Content exceptions in Kodexa allow developers to flag and track issues within documents, providing valuable metadata for error handling, quality control, and process improvement. Let's dive into how to work with content exceptions in Kodexa. ### Adding a Basic Exception To add a simple content exception to a document, you can use the following code: ```python theme={null} from kodexa_document import Document from kodexa.model import ContentException document = Document() exception = ContentException("Test", "Testing exception") document.add_exception(exception) # Verify the exception was added assert len(document.get_exceptions()) == 1 ``` This code creates a new document, generates a content exception with a title and description, and adds it to the document. The assertion confirms that the exception was successfully added. ### Working with Exceptions in Existing Documents Kodexa allows you to work with exceptions in documents that are loaded from files. Here's an example: ```python theme={null} import os from kodexa_document import Document from kodexa.model import ContentException # Load an existing document document = Document.from_kddb('path/to/your/document.kddb', detached=True) # Check for existing exceptions assert len(document.get_exceptions()) == 0 # Create and add a new exception content_exception = ContentException("Test", "Testing exception", exception_type_id="123123") document.add_exception(content_exception) # Verify the exception was added assert len(document.get_exceptions()) == 1 # Save the updated document document.to_kddb("/path/to/save/updated_document.kddb") document.close() # Re-load the document and verify the exception persists updated_document = Document.from_kddb("/path/to/save/updated_document.kddb") assert len(updated_document.get_exceptions()) == 1 assert updated_document.get_exceptions()[0].exception_type_id == "123123" ``` This example demonstrates how to load an existing document, add a content exception, save the updated document, and then verify that the exception persists when the document is reloaded. ### Advanced Exception Handling Content exceptions in Kodexa can include additional metadata, such as an exception type ID. This allows for more granular categorization and handling of exceptions: ```python theme={null} content_exception = ContentException("Test", "Testing exception", exception_type_id="123123") ``` By including an exception\_type\_id, you can create a system for categorizing different types of exceptions, which can be useful for filtering, reporting, or automated handling of specific issue types. ### Best Practices for Using Content Exceptions * Use meaningful titles and descriptions for exceptions to make them easily understandable. * Implement a consistent system for exception type IDs to categorize different types of issues. * Regularly review and address content exceptions as part of your document management workflow. * Use exceptions to track not just errors, but also warnings or areas for improvement in your documents. ### Conclusion Content exceptions in Kodexa provide a powerful tool for managing document quality and tracking issues throughout the document lifecycle. By implementing a robust exception handling system, developers can create more resilient document processing pipelines, improve error reporting, and enhance overall document management processes. Whether you're dealing with simple text documents or complex structured data, Kodexa's content exception system offers the flexibility and functionality needed to maintain high-quality document processing operations. By leveraging these features, organizations can improve their document management workflows, reduce errors, and gain valuable insights into their document processing pipelines. Let's explore how to use these advanced features. ### Creating Detailed Content Exceptions With this comprehensive definition, you can create more detailed exceptions: ```python theme={null} from kodexa_document import Document from kodexa.model import ContentException from datetime import datetime document = Document() exception = ContentException( message="Missing required field", exception_type="ValidationError", severity="High", exception_details="The 'name' field is required but was not found in the document.", created_on=datetime.now(), tag="PersonalInfo" ) document.add_exception(exception) ``` ### Grouping and Tagging Exceptions If you are tagging data for extraction then you can use the group\_uuid and tag\_uuid on the exceptions to link the exceptions to the specific content you are tagging. The `group_uuid` and `tag_uuid` fields enable you to organize exceptions: ```python theme={null} # Group related exceptions group_id = "missing_fields_group" exceptions = [ ContentException(message="Missing name", group_uuid=group_id), ContentException(message="Missing address", group_uuid=group_id) ] for exc in exceptions: document.add_exception(exc) # Later, retrieve all exceptions in this group group_exceptions = [exc for exc in document.get_exceptions() if exc.group_uuid == group_id] ``` ### Best Practices for Advanced Exception Handling * Use the `severity` field to prioritize exception handling. * Leverage `exception_type` for categorizing and filtering exceptions. * Utilize `group_uuid` to manage related exceptions together. * Keep `exception_details` comprehensive for easier troubleshooting. * Regularly update `change_sequence` and `updated_on` to track exception lifecycle. The advanced features of Kodexa's ContentException class provide a powerful toolkit for managing document processing issues. By leveraging these capabilities, developers can create more robust, traceable, and manageable exception handling systems. This level of detail in exception handling allows for: * More precise error tracking and resolution * Better categorization and prioritization of issues * Enhanced reporting and analytics on document processing problems * Improved linkage between exceptions and the specific content causing them By fully utilizing the ContentException class, organizations can significantly enhance their document management workflows, leading to more efficient processing, better quality control, and more insightful analytics on document-related issues. # Working with Modules Source: https://developer.kodexa.ai/concepts/working_with_modules Build, deploy, and manage Kodexa modules using Python and the Kodexa SDK to extract data from documents and integrate processing into projects. One of the most important parts of the Kodexa Platform is the ability to create and manage modules. Modules are the core of the platform and are used to extract data from documents. Modules are created by using Python and the Kodexa SDK. In its simplest form, a module is simply a small Python script that receives a Document and returns a Document. The module can be as simple as: ```python theme={null} def infer(document): return document ``` You would put this code in a module, i.e. ```bash theme={null} module/ __init__.py module.py ``` ## Deployment To deploy the module, we need to also create a `module.yml` file that describes the module. This file is used to describe the module, and also to provide the metadata that is used to deploy the module to the Kodexa Platform. ```yaml theme={null} # A very simple first module slug: my-module version: 1.0.0 orgSlug: kodexa type: store storeType: MODEL name: My Module metadata: moduleRuntimeRef: kodexa/base-module-runtime type: module contents: - module/* ``` The module definition is simple, it has a slug, version, orgSlug, type, storeType, name and metadata. The metadata is the most important part of the module definition. The metadata is used to describe the module and is used to deploy the module to the Kodexa Platform. We can now deploy this module to try it out. If you are logged in, you can deploy the module using the KDX CLI. ```bash theme={null} kdx apply -f module.yml ``` ## Debugging a Deploy Sometimes you will want to understand what is in a deployment of a module. There are a couple of things you can do to understand what is on the server. First, you can set the deploy to keep a ZIP representation. This will allow you to see what was finally packaged in the module, you do this by updating the module.yml, i.e. ```yaml theme={null} # A very simple first module slug: my-module version: 1.0.0 orgSlug: kodexa type: store storeType: MODEL name: My Module metadata: # Adding Keep Zip will mean the implementation ZIP file is not cleaned up # when you run kdx apply -f keepZip: true moduleRuntimeRef: kodexa/base-module-runtime type: module contents: - module/* ``` The second way you can use is to download an implementation from the server, this is also useful for debugging if there is an issue in your deployment setup. You can use the KDX CLI to run: ```bash theme={null} kdx get module my-org/my-module -o yaml ``` # Working with Projects Source: https://developer.kodexa.ai/concepts/working_with_projects Use the Project class in the Kodexa Python SDK to create, query, and manage projects programmatically, including resources, assistants, and metadata. ## Understanding the Project Class The Project class is a fundamental component of the Kodexa SDK, representing a container for your data extraction and processing tasks. Here's the definition of the Project class: ```python theme={null} from pydantic import BaseModel, Field, ConfigDict from typing import Optional from kodexa import Organization, User, ProjectMetadata, ProjectStatus, ProjectOptions class Project(BaseModel): model_config = ConfigDict( populate_by_name=True, use_enum_values=True, arbitrary_types_allowed=True, protected_namespaces=("model_config",), ) id: Optional[str] = Field(None) organization: Optional[Organization] = None name: Optional[str] = None description: Optional[str] = None metadata: Optional[ProjectMetadata] = None has_image: Optional[bool] = Field(None, alias="hasImage") status: Optional[ProjectStatus] = None owner: Optional[User] = None options: Optional[ProjectOptions] = Field(None, alias="options") ``` Let's break down the key attributes of the Project class: * `id`: A unique identifier for the project. * `organization`: The organization to which the project belongs. * `name`: The name of the project. * `description`: A brief description of the project. * `metadata`: Additional metadata associated with the project. * `has_image`: Indicates whether the project has an associated image. * `status`: The current status of the project. * `owner`: The user who owns the project. * `options`: Additional options for the project. The Project class uses Pydantic's BaseModel, which provides data validation and settings management. The `model_config` settings enable features like populating by field name, using enum values, and allowing arbitrary types. Now that we understand the structure of a Project, let's proceed with creating a project and loading a taxonomy. ## Step 1: Initialize the Kodexa Client and Organization First, you need to have a Kodexa client and an organization endpoint: ```python theme={null} from kodexa import KodexaClient, OrganizationEndpoint client = KodexaClient(...) # Initialize your Kodexa client organization = ... # Get your organization endpoint ``` ## Step 2: Create the Project Next, create a new Project object with a name and description: ```python theme={null} from kodexa import Project project_name = "Test Project" project_description = "Test Project Description" project = Project(name=project_name, description=project_description, organization=organization.detach()) ``` Here, we're initializing a Project object with the name and description we've specified. The `organization.detach()` method is used to ensure we're working with a detached copy of the organization object. ## Step 3: Create the Project Using a Template Use the Kodexa client to create the project based on a template: ```python theme={null} template_ref = "data-extraction-starter" project = client.projects.create(project, template_ref=template_ref) ``` This step uses the Kodexa client to create the project on the Kodexa platform, using the specified template as a starting point. ## Step 4: Load a Local Data Definition (Optional) If you have a local Data Definition file, you can load it and update the project's Data Definition. The SDK object is still named `TaxonomyEndpoint` in this legacy API: ```python theme={null} from yaml import load, FullLoader from kodexa import TaxonomyEndpoint taxonomy = None taxonomy_yaml = "path/to/your/data-definition.yaml" # Set to None if not using a local Data Definition if taxonomy_yaml is not None: with open(taxonomy_yaml) as taxonomy_file: test_taxonomy = TaxonomyEndpoint.parse_obj(load(taxonomy_file, Loader=FullLoader)).set_client(client) taxonomy = project.taxonomies.list()[0] taxonomy.taxons = test_taxonomy.taxons taxonomy.update() ``` This code block loads a local Data Definition file, creates the legacy `TaxonomyEndpoint` SDK object, and updates the project's existing Data Definition with the loaded data elements. ## Putting It All Together Here's the complete code that combines all these steps: ```python theme={null} from kodexa import KodexaClient, OrganizationEndpoint, Project, TaxonomyEndpoint from yaml import load, FullLoader # Initialize client and organization client = KodexaClient(...) # Initialize your Kodexa client organization = ... # Get your organization endpoint # Create the project project_name = "Test Project" project_description = "Test Project Description" project = Project(name=project_name, description=project_description, organization=organization.detach()) # Create the project using the template template_ref = "data-extraction-starter" project = client.projects.create(project, template_ref=template_ref) # Load and update Data Definition (if applicable) taxonomy = None taxonomy_yaml = "path/to/your/data-definition.yaml" # Set to None if not using a local Data Definition if taxonomy_yaml is not None: with open(taxonomy_yaml) as taxonomy_file: test_taxonomy = TaxonomyEndpoint.parse_obj(load(taxonomy_file, Loader=FullLoader)).set_client(client) taxonomy = project.taxonomies.list()[0] taxonomy.taxons = test_taxonomy.taxons taxonomy.update() # The project and Data Definition (if loaded) are now ready to use print(f"Project '{project.name}' created successfully.") if taxonomy: print("Local Data Definition loaded and updated.") ``` By following these steps, you can create a new project based on a template and optionally load a custom Data Definition from a local YAML file. This approach allows you to quickly set up projects with custom configurations and Data Definitions, streamlining your workflow with the Kodexa SDK. Remember to replace the placeholder values (like the client initialization and organization endpoint) with your actual Kodexa account details. Also, ensure you have the necessary permissions to create projects and modify Data Definitions in your Kodexa environment. # KDX CLI Quick Start Source: https://developer.kodexa.ai/essentials/kodexa-cli Quick start guide for the KDX CLI, the kubectl-style command-line interface for managing the Kodexa AI Platform from your terminal. The KDX CLI (`kdx`) is a modern, kubectl-style command-line interface for the Kodexa AI Platform. It provides a powerful and familiar interface for managing Kodexa resources directly from the terminal. ## Why KDX CLI? * **Kubectl-Style Interface**: Familiar commands like `get`, `describe`, `create`, `apply`, and `delete` * **Dynamic Resource Discovery**: Automatically discovers resources from your platform's OpenAPI specification * **Profile-Based Authentication**: Manage multiple environments with ease * **Production Safety**: Confirmation prompts prevent accidental changes to production * **GitOps Ready**: Built-in metadata sync for version-controlled infrastructure * **Multiple Output Formats**: Interactive tables, JSON, and YAML for any workflow ## Installation ### Homebrew (macOS/Linux) - Recommended ```bash theme={null} # Add the Kodexa tap brew tap kodexa-ai/tap # Install kdx brew install kdx # Verify installation kdx --version ``` ### Pre-release Builds To try the latest development build before it's officially released, install `kdx-dev`. It installs as a separate binary so it can coexist with the stable `kdx`: ```bash theme={null} # Install the pre-release CLI brew install kodexa-ai/tap/kdx-dev # Verify installation kdx-dev --version ``` Pre-release builds are updated automatically from the `develop` branch and may contain features or fixes not yet in the stable release. Use `brew upgrade kdx-dev` to get the latest dev build. ### Scoop (Windows) ```powershell theme={null} # Add the Kodexa bucket scoop bucket add kodexa https://github.com/kodexa-ai/scoop-bucket # Install kdx scoop install kdx # Verify installation kdx --version ``` ### Pre-release Builds (Windows) To try the latest development build on Windows, install `kdx-dev` via Scoop. It installs as a separate binary so it can coexist with the stable `kdx`: ```powershell theme={null} # Install the pre-release CLI scoop install kdx-dev # Verify installation kdx-dev --version ``` Pre-release builds are updated automatically from the `develop` branch and may contain features or fixes not yet in the stable release. Use `scoop update kdx-dev` to get the latest dev build. ### Other Installation Methods See the [full installation guide](/guides/kdx-cli/installation) for Docker, direct binary downloads, and building from source. ## Quick Start ### 1. Configure Authentication **Recommended: Environment Variables** Set environment variables for your Kodexa environments: ```bash theme={null} export KODEXA_URL="https://platform.kodexa-enterprise.com" export KODEXA_ACCESS_TOKEN="your-api-key-here" ``` Add these to your shell profile (`~/.bashrc`, `~/.zshrc`) to make them permanent. **Alternative: Profiles** You can also use profiles for managing multiple environments: ```bash theme={null} kdx config set-profile local \ --url http://localhost:8080 \ --api-key your-api-key-here ``` For production (with safety prompts): ```bash theme={null} kdx config set-profile prod \ --url https://platform.kodexa-enterprise.com \ --api-key your-production-api-key \ --production ``` The `--production` flag enables confirmation prompts before destructive operations to prevent accidental changes. ### 2. Discover Resources See what resources are available in your environment: ```bash theme={null} kdx api-resources ``` ### 3. List Resources Use the `get` command to list resources: ```bash theme={null} # List all workspaces kdx get workspaces # List all projects kdx get projects # List all stores kdx get stores ``` ### 4. Get Specific Resources Retrieve details about a specific resource: ```bash theme={null} kdx get workspace my-workspace kdx describe workspace my-workspace ``` ### 5. Create Resources Create resources declaratively using YAML files: ```yaml workspace.yaml theme={null} kind: workspace name: engineering-workspace description: Engineering team workspace ``` Apply the configuration: ```bash theme={null} kdx create workspace -f workspace.yaml ``` ## Working with Multiple Environments Switch between profiles easily: ```bash theme={null} # Switch to staging kdx config use-profile staging # Use production profile for a single command kdx get workspaces --profile prod ``` ## Output Formats KDX CLI supports multiple output formats: ```bash theme={null} # Interactive table (default for terminals) kdx get workspaces # JSON output (great for scripting) kdx get workspaces -o json # YAML output kdx get workspaces -o yaml ``` ## Getting Help KDX CLI has built-in help for every command: ```bash theme={null} # General help kdx --help # Command-specific help kdx get --help kdx config --help ``` ## Configuration All KDX CLI configuration is stored in `~/.kodexa/config.yaml`. The CLI now enforces secure defaults (config directory `0700`, file `0600`) so API keys are readable only by your user. Keep keys out of shell history and prefer profiles or environment variables over inlining secrets in commands. ## Validation & Safety * Requests sent via `apply` and `run` are validated against the live OpenAPI schema before they reach the API, catching missing/invalid fields early. * Production-marked profiles prompt before destructive actions; use `--skip-production-confirm` only for trusted automation. * Module applies package implementation files into a temporary ZIP (unique per run) to avoid overwriting local files; the archive is cleaned up automatically. ## Plugins Resource-specific plugins are registered automatically—no separate `kdx plugins` command is required. Current built-ins: * `kdx project create --template --org --name ` to spin up projects from templates. ## Next Steps Learn about all KDX CLI capabilities and features Comprehensive installation options for all platforms Deep dive into profile management Learn advanced CRUD operations # Agent Steps Source: https://developer.kodexa.ai/guides/activity-plans/agent-steps Use AGENT steps in Activity Plans to delegate bounded work to a Kodexa agent runtime when workflows require tool use or multi-step reasoning. `AGENT` steps delegate a bounded part of an Activity to an agent runtime. Use them when the workflow needs tool use or multi-step reasoning, but the Activity Plan should still own the business process boundary. Agents should help the platform build, inspect, draft, or decide within a well-defined scope. They should not replace the Activity Plan as the workflow model. ## When to Use AGENT Use an `AGENT` step when the Activity needs an agent to: * Draft a routine Data Form, schema, or Activity Plan segment for review * Investigate a document exception using project context and tools * Assemble test cases for a configured extraction or review workflow * Compare document evidence against configured business rules * Produce a structured recommendation that a reviewer or downstream step can use Use `LLM` for a single bounded prompt. Use `AGENT` when the work involves tools, iterative reasoning, or multiple project resources. ## Basic Shape ```json theme={null} { "slug": "investigate-exception", "type": "AGENT", "dependsOn": ["validate:exception"], "agentRuntimeId": "runtime-uuid", "moduleRefs": [ "kodexa/invoice-tools", "kodexa/vendor-tools" ] } ``` | Step field | Description | | ---------------- | -------------------------------------------------------- | | `agentRuntimeId` | Agent runtime that should run the work | | `moduleRefs` | Optional modules/tools made available to the agent | | `dependsOn` | Prior Activity steps or actions that must complete first | The runtime validates that the agent runtime exists and is ready before starting the agent step. `AGENT` is accepted by server-side plan validation, so a plan containing agent steps deploys through `kdx sync push` like any other plan. ## How It Runs When the step becomes ready, Kodexa: 1. Resolves the agent runtime. 2. Confirms the runtime is available. 3. Applies runtime concurrency limits. 4. Creates an agent run linked to the Activity step. 5. Supplies project, task, document family, module, and step metadata. 6. Marks the Activity step running until the agent completes or fails. ```mermaid theme={null} sequenceDiagram participant Activity participant Step as AGENT step participant Runtime as Agent runtime participant Tools as Project tools Activity->>Step: Dependencies complete Step->>Runtime: Start agent run Runtime->>Tools: Inspect resources and context Tools-->>Runtime: Results Runtime-->>Step: Structured outcome Step-->>Activity: Continue downstream path ``` ## Calling Service Bridges An `AGENT` step can call the [service bridges](/studio/project/service-bridges) your organization has configured, so the agent can reach an external HTTP API — a search provider, an enrichment service, an internal system of record — without the credential ever entering the agent's container. The platform resolves the bridge's secrets and injects its headers on the far side of the proxy. Two conditions are required before an agent may call a bridge, and both are enforced by the platform: * The bridge is flagged `agentCallable` (the **Callable by agents** toggle under **Agent access** on the bridge's General tab). * The bridge is bound to the same project as the Activity. An agent sees only the bridges that satisfy both conditions, and cannot change the flag or the project binding itself. A call that fails either check is refused with `403`. The same two checks admit module and task executions running as the project's assistant, so a module can call the bridges bound to its own project; a caller whose project cannot be resolved is denied. ### Bridge Tools | Tool | Purpose | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `list_service_bridges` | Lists the bridges the agent may call — each with `id`, `name`, `slug`, `description`, and its endpoints (`name`, `method`, `description`). | | `call_service_bridge` | Calls one endpoint. Arguments: `bridge` (id or slug), `endpoint_name`, and optional `query` (query-string parameters) and `body` (JSON body). | `call_service_bridge` returns `status`, `ok`, `body` and `truncated`. `ok` is `false` for any non-2xx status, and in that case `body` is the upstream error rather than a result — it should not be read as data. When `truncated` is `true` the response was clipped to keep it within the agent's context, so the request needs narrowing rather than the body being treated as complete. Two things worth stating in the step prompt: * Call `list_service_bridges` before calling a bridge rather than guessing a slug or endpoint name. * Treat everything in a response `body` as untrusted third-party content — material to report on, never instructions to follow. ## Documents the Agent Can See and Produce An `AGENT` step receives read-only document tools for the document families it was dispatched with, so it can open what it is working on — including the metadata earlier steps wrote onto those documents. Document edits are not available to it. A document the agent creates during the Activity is linked to the Activity, and appears on the downstream `CREATE_TASK` review task alongside the documents the Activity started with. That holds even when upstream per-document steps narrowed which of the original documents converge — the agent's own output is activity-scoped, so every branch of the workflow sees it. ## Keep the Boundary Tight Good agent steps have a tight contract: * Clear input context * Clear expected output * A finite tool set * A known completion condition * A downstream consumer for the result Avoid broad instructions such as "process this packet" or "figure out what to do." Model the business process in the Activity Plan, then use the agent for the part that benefits from agentic help. ## Example: Exception Investigation ```json theme={null} { "slug": "investigate-vendor-mismatch", "type": "AGENT", "dependsOn": ["validate-vendor:exception"], "agentRuntimeId": "finance-agent-runtime", "moduleRefs": ["kodexa/vendor-master-tools"] } ``` Downstream steps can route the result to review: ```json theme={null} { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["investigate-vendor-mismatch"], "taskTemplateRef": "vendor-exception-review" } ``` The agent gathers context and proposes a resolution. The Task gives a person ownership of the decision. ## Concurrency Agent runtimes can have concurrency limits. If a runtime is at its limit, the Activity step remains pending until capacity is available. This protects shared runtimes and keeps agent-heavy workflows from overwhelming project resources. Design with this in mind: * Put expensive agent steps after cheap deterministic routing. * Use `SCRIPT` or `LLM` first when they can filter out clean cases. * Keep agent steps reserved for cases where tool-using reasoning is worth the latency and cost. ## AGENT vs LLM vs SCRIPT | Need | Use | | ------------------------------------------------ | ------------- | | Deterministic routing or simple transformation | `SCRIPT` | | One prompt with mapped output | `LLM` | | Tool use, investigation, or multi-step reasoning | `AGENT` | | Human ownership of a decision | `CREATE_TASK` | ## Outputs Agent outputs should be structured enough for downstream steps to consume. A useful agent result normally includes: * A concise recommendation * Evidence references * Confidence or risk level * Missing information * Suggested next action If the next step is a human review Task, place the agent result where the Data Form can show it to the reviewer. ## Checklist * The agent runtime is available to the project. * The Activity Plan gives the agent a narrow job. * The agent has only the tools/modules it needs. * Any service bridge the agent must call is flagged `agentCallable` and bound to the project. * The workflow still has deterministic downstream steps. * Human review owns final judgment where required. * Concurrency and latency are acceptable for the business process. Use bounded prompt execution instead of agent delegation. Hand agent recommendations to human reviewers. # AI Naming Source: https://developer.kodexa.ai/guides/activity-plans/ai-naming Use an LLM to automatically generate contextual titles and descriptions for activities, tasks, and intake documents to keep dashboards readable. When an activity, task, or intake document is created without an explicit title, the platform can use an LLM to generate a contextual title and description automatically. This keeps dashboards, task lists, and audit trails readable without requiring users to write titles by hand. ## When AI Naming Runs AI naming is evaluated at three trigger points: | Trigger | Created Resource | Where to Configure | | ---------------------------------------- | ------------------------------ | ---------------------- | | Activity creation | Activity title and description | Activity Plan metadata | | Task creation (from a CREATE\_TASK step) | Task title and description | Task Template metadata | | Intake upload (from a task template) | Task title and description | Task Template metadata | At each trigger point, the platform checks whether the resource already has an explicit title. If it does, AI naming is skipped entirely. ## Enabling AI Naming Add an `aiNaming` block to the `metadata` of an Activity Plan or Task Template. ### JSON (API) ```json theme={null} { "metadata": { "aiNaming": { "enabled": true, "prompt": "Generate a title for {templateName} processing {documentFamilyPaths}" } } } ``` ### YAML (kdx sync) ```yaml theme={null} metadata: aiNaming: enabled: true prompt: "Generate a title for {templateName} processing {documentFamilyPaths}" ``` The `aiNaming` block lives inside the resource's `metadata` object, not at the top level. This is the same `metadata` field used for other resource-level configuration. ## Prompt Placeholders The prompt string supports placeholders that the platform resolves before sending the text to the LLM. Wrap each placeholder in curly braces. | Placeholder | Description | Example Output | | ---------------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------- | | `{templateName}` | Activity plan or task template name | `Invoice Review` | | `{activityPlanName}` | Alias for `{templateName}` | `Invoice Review` | | `{documentFamilyPaths}` | Comma-separated file paths of attached documents | `invoices/acme-001.pdf, invoices/acme-002.pdf` | | `{documentFamilyCount}` | Number of document families | `2` | | `{knowledgeFeatures}` | Comma-separated knowledge feature names | `Net 30 Terms, Auto-Renewal` | | `{metadata:key}` | Top-level metadata value from document families | `Acme Corp` | | `{metadata:key.nested.path}` | Dot-path into metadata JSON (GJSON syntax) | `Austin` | | `{externalData:key}` | Entire JSON blob for an external data key | `{"invoiceNumber":"INV-001"}` | | `{externalData:key.path}` | Dot-path into external data JSON (first segment is the key, rest is GJSON path) | `INV-001` | ### Metadata and External Data Paths The `{metadata:...}` and `{externalData:...}` placeholders use a dot-separated path to reach nested values. For metadata, the path resolves directly against the document family metadata JSON: ``` {metadata:companyName} -> top-level key {metadata:sender.address.city} -> nested path using GJSON syntax ``` For external data, the first segment identifies the external data key, and the remaining segments navigate into that key's JSON value: ``` {externalData:erp-reference} -> entire JSON blob {externalData:erp-reference.invoiceNumber} -> specific field within the blob ``` ### Multi-Document Behavior When multiple document families are involved in a single activity or task, metadata and external data values are collected from all documents, deduplicated, and joined with `; `. For example, if two documents have `companyName` metadata values of "Acme Corp" and "Beta Inc", the placeholder `{metadata:companyName}` resolves to: ``` Acme Corp; Beta Inc ``` ## Title Resolution Fallback Chain The platform uses the first available title from this ordered chain: 1. **Explicit title** -- If the caller provides a title directly, it is used as-is. 2. **AI naming** -- If `aiNaming.enabled` is true and a prompt is configured, the LLM generates a title and description. 3. **Template rendering** -- If `defaultTitleTemplate` or `defaultDescriptionTemplate` is set on the plan or template, it is rendered using Go template syntax with `{{ .inputs.field }}` placeholders. 4. **Plan or template name** -- The name of the Activity Plan or Task Template is used as the title. 5. **Generic fallback** -- `"Untitled"`. You can use `defaultTitleTemplate` and AI naming together. The template acts as a reliable fallback if the LLM call fails for any reason. ## Example Prompts ### Invoice processing with company context ``` Generate a concise title for a {templateName} task processing {documentFamilyCount} document(s): {documentFamilyPaths}. The company is {metadata:companyName}. ``` This might produce: **"Acme Corp Invoice Review - 3 documents"** ### Task with external reference data ``` Name this task based on the invoice from {metadata:vendorName} with invoice number {externalData:erp-data.invoiceNumber} for {metadata:companyName}. ``` This might produce: **"GlobalParts Inc Invoice #INV-2026-0412 for Acme Corp"** ### Contract review with knowledge features ``` Generate a title for reviewing a contract with these key terms: {knowledgeFeatures}. Documents: {documentFamilyPaths}. ``` This might produce: **"Contract Review - Net 30 Terms, Auto-Renewal Clause"** ## LLM Response Format The LLM must respond with a JSON object containing `title` and `description` fields: ```json theme={null} { "title": "Acme Corp Invoice Review - 3 documents", "description": "Review of 3 invoices uploaded from Acme Corp for Q2 processing" } ``` The platform parses this JSON from the LLM response and applies both fields to the created resource. If AI naming fails -- whether from an LLM error, a JSON parse error, or a timeout -- the system silently falls back to the next step in the fallback chain. No error is surfaced to the user. Design your `defaultTitleTemplate` as a reliable backup. ## Best Practices **Keep prompts concise.** The platform uses a small, fast LLM model for naming to minimize latency. Long, detailed prompts do not improve results and slow down resource creation. **Include the most distinctive data points.** Company name, document type, and reference numbers produce the most useful titles. Avoid generic placeholders that add little differentiation. **Always set a `defaultTitleTemplate` as a fallback.** AI naming depends on an external LLM call. If the call fails, a well-crafted template ensures activities and tasks still get meaningful titles. **Test with representative data.** Use documents that reflect your production workload to verify that placeholders resolve to useful values and that the LLM produces titles at the right level of detail. ```yaml theme={null} # Complete example: Activity Plan with AI naming and template fallback name: Invoice Intake slug: invoice-intake description: Classify, extract, review, and post invoice data metadata: aiNaming: enabled: true prompt: >- Generate a title for {templateName} processing {documentFamilyCount} document(s) from {metadata:companyName} defaultTitleTemplate: "Invoice intake for {{ .inputs.documentFamilyId }}" inputsSchema: type: object required: - documentFamilyId properties: documentFamilyId: type: string steps: - slug: extract type: EXECUTION moduleRef: kodexa/invoice-extractor options: documentFamilyId: "{{ .inputs.documentFamilyId }}" ``` # Approval Steps Source: https://developer.kodexa.ai/guides/activity-plans/approval-steps Model explicit approval gates in Activity Plans with APPROVAL steps that pause processing until an authorized person or role approves the next move. `APPROVAL` steps model an explicit authorization gate in an Activity. Use them when the process must pause until an authorized person or role approves the next move. For production review work today, the most common pattern is still a `CREATE_TASK` step backed by an approval Task Template and Data Form. Use `APPROVAL` when you want the Activity Plan to express the approval gate directly. ## When to Use APPROVAL Use `APPROVAL` for authorization gates such as: * Manager approval for invoices above a threshold * Compliance approval before a covenant result is finalized * Supervisor approval before a claim is accepted * Credit approval before a lending packet advances * Operations approval before data is posted to a system of record Use `CREATE_TASK` instead when the approver needs a full review surface, document correction, multiple fields, or a rich Data Form. ## Basic Shape ```json theme={null} { "slug": "manager-approval", "type": "APPROVAL", "dependsOn": ["analyst-review:approved"], "approverRole": "finance-manager", "approvalCriteria": { "amountGreaterThan": 10000, "currency": "USD" } } ``` | Step field | Description | | ------------------ | -------------------------------------------------------- | | `approverRole` | Role or responsibility that can approve the gate | | `approvalCriteria` | Business criteria that explain why approval is required | | `dependsOn` | Prior Activity steps or actions that must complete first | ## Approval vs Review Task | Need | Prefer | | ------------------------------------------------------------ | ------------- | | Simple approve or reject gate | `APPROVAL` | | Reviewer edits extracted data | `CREATE_TASK` | | Reviewer needs a Data Form | `CREATE_TASK` | | Approval must be visible as a step type in the process model | `APPROVAL` | | Existing production workflow needs rich human work | `CREATE_TASK` | ## Common Pattern ```mermaid theme={null} flowchart LR Extract["EXECUTION: extract"] --> Validate["SCRIPT: validate"] Validate -->|"within policy"| Post["BRIDGE_CALL: post"] Validate -->|"needs approval"| Approve["APPROVAL: manager approval"] Approve -->|"approved"| Post Approve -->|"rejected"| Reject["BRIDGE_CALL: reject"] ``` The approval gate is separate from the automation. The Activity can show exactly where authorization was required and what happened next. ## Criteria Design Approval criteria should be understandable to the approver and auditable later. Good criteria: ```json theme={null} { "approvalCriteria": { "reason": "Invoice amount exceeds approval threshold", "amount": "{{steps.extract.mapped_output.total}}", "threshold": 10000, "currency": "USD" } } ``` Weak criteria: ```json theme={null} { "approvalCriteria": { "flag": true } } ``` Approval criteria should explain the business reason, not just the implementation condition. ## Downstream Routing Downstream steps should depend on explicit approval outcomes. ```json theme={null} [ { "slug": "manager-approval", "type": "APPROVAL", "dependsOn": ["validate:requires-approval"] }, { "slug": "post-approved", "type": "BRIDGE_CALL", "dependsOn": ["manager-approval:approved"] }, { "slug": "notify-rejected", "type": "BRIDGE_CALL", "dependsOn": ["manager-approval:rejected"] } ] ``` Keep approval outcomes small and explicit. Most approval gates need only `approved`, `rejected`, and sometimes `needs-more-information`. ## Production Pattern With CREATE\_TASK When the approval requires a full reviewer interface, model it as a Task: ```json theme={null} { "slug": "manager-approval-task", "type": "CREATE_TASK", "dependsOn": ["validate:requires-approval"], "taskTemplateRef": "manager-approval", "taskStatusSlug": "open", "taskData": { "approvalReason": "Invoice amount exceeds threshold" } } ``` This keeps the Activity-first model intact: the Activity owns the process and creates a Task when a person must act. ## Checklist * The approval gate represents a real business authorization, not a generic pause. * The approver role is clear. * Criteria explain why approval is required. * Downstream steps depend on explicit outcomes. * Use `CREATE_TASK` when the approver needs a full review surface. * Approval outcomes are included in audit and reporting expectations. Build rich human review and approval work. Post approval results to external systems. # Create Task Steps Source: https://developer.kodexa.ai/guides/activity-plans/create-task-steps Use CREATE_TASK steps in Activity Plans to bring people into the workflow for review, correction, approval, exception handling, or escalation. `CREATE_TASK` steps are how an Activity brings a person into the workflow. Use them when the next decision cannot be safely automated: analyst review, correction, approval, exception handling, reconciliation, or escalation. An Activity is broader than a task. The Activity owns the business process. A Task is the unit of human work created when that process needs judgment. ## When to Use CREATE\_TASK Use a `CREATE_TASK` step when the workflow needs someone to: * Review extracted fields before posting them to a downstream system * Correct data that failed validation * Decide whether an exception should be accepted, rejected, or escalated * Approve work above a business threshold * Add missing context that is not present in the document * Resolve a mismatch between the document and a system of record Do not use `CREATE_TASK` for pure automation. Use `EXECUTION`, `SCRIPT`, `BRIDGE_CALL`, or `LLM` for automated work. ## Basic Shape ```json theme={null} { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["route:review"], "taskTemplateRef": "invoice-review", "taskStatusSlug": "open", "taskData": { "priority": "high", "sourceSystem": "{{inputs.sourceSystem}}" } } ``` | Step field | Description | | ----------------- | ---------------------------------------------------------------------- | | `taskTemplateRef` | Task Template used to create the human work item | | `taskStatusSlug` | Initial status for the created Task | | `taskData` | Properties copied onto the Task | | `title` | Optional explicit title for the Task | | `description` | Optional explicit description for the Task | | `priority` | Optional priority; otherwise the parent Task priority can be inherited | In the current runtime, the underlying materialized item type is still named `TASK` in some code and stored data. The Activity Plan concept should still be modeled as `CREATE_TASK`: create a Task, wait for human work, then continue the Activity from the Task result. ## Runtime Placeholders An Activity Plan is defined once and can run in many projects, so task text often needs values that are only known when the Activity runs. Kodexa resolves the following placeholders at the moment the Task is created: | Placeholder | Resolves to | | ----------------------------------------- | -------------------------------------------------------------------------------------------------- | | `${activity.title}` | The parent Activity's title at task-creation time, reflecting any automatic or manual rename | | `${project.name}` | The name of the project the Activity is running in | | `${project.id}` | The ID of the project the Activity is running in | | `${project.options.dataProperties.}` | The value entered for `` in the project's data properties, as defined by the project template | Placeholders resolve in the Task's `title` and `description` and in top-level string values under `taskData.properties`: ```json theme={null} { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["route:review"], "taskTemplateRef": "invoice-review", "taskData": { "title": "Review ${activity.title}", "description": "For ${project.name}", "properties": { "regionCode": "${project.options.dataProperties.regionCode}" } } } ``` Resolution rules: * If `${activity.title}` or `${project.name}` cannot be resolved, the placeholder text is left as-is, so the unresolved token stays visible in the Task. * A `${project.options.dataProperties.}` placeholder always resolves: when the key is not set, or its value is not a simple value, it resolves to an empty string. This keeps literal tokens out of task properties that downstream systems filter on. This lets one plan shared across many projects give each child Task a context-specific name, or stamp a project-scoped identifier onto every Task it creates. ## How It Runs When the step becomes ready, Kodexa: 1. Resolves the target project from the running Activity context. 2. Resolves `taskTemplateRef` inside that project organization. 3. Resolves `taskStatusSlug` to the initial project task status. 4. Builds task properties from `taskData`, title, description, and priority, resolving any [runtime placeholders](#runtime-placeholders). 5. Creates the child Task and links it to the Activity step. 6. Attaches the relevant document families. 7. Waits for the Task to reach an outcome that can continue the Activity. ```mermaid theme={null} sequenceDiagram participant Activity participant Step as CREATE_TASK step participant Task as Human Task participant Reviewer Activity->>Step: Dependencies complete Step->>Task: Create from Task Template Task->>Reviewer: Appears in queue Reviewer->>Task: Complete with status or action Task-->>Activity: Outcome recorded Activity->>Activity: Continue matching downstream path ``` ## Document Family Handling A `CREATE_TASK` step should carry the document context the reviewer needs. The runtime copies document families from the parent workflow context onto the created Task. When the upstream dependency is a per-document step, Kodexa can narrow the copied document families to the document-level results that actually succeeded. This is useful for exception queues because the reviewer sees only the documents that need attention. ## Document Order on the Task A task with more than one document opens on a specific document, and its document list is presented in a fixed order. That order comes from an integer `ordinal` carried on each document link: * When the Activity starts, each document is stamped with its position in the `documentFamilyIds` list it was started with. Index `0` is the Activity's primary document. * When a `CREATE_TASK` step creates the Task, that order is copied onto the Task's own document links. Ordinal `0` is the Task's primary document — the one the reviewer's workspace opens on. * When the step narrows the copied set (a per-document dependency where only some documents succeeded), the surviving documents keep their relative Activity order. So you choose which document a reviewer sees first by listing it first in `documentFamilyIds` when you start the Activity. Document links created directly through the API follow the same rule. `ordinal` is a field on `TaskDocumentFamily` and `ActivityDocumentFamily`, and on their create and update requests. On `POST /api/task-document-families` and `POST /api/activity-document-families`, omitting `ordinal` — or sending `0` — appends the document after the existing ones, so a link added later never displaces the primary document. An explicit non-zero `ordinal` is stored as given. Existing multi-document Tasks and Activities were backfilled with a stable order, so they open deterministically without being reprocessed. ## Task Templates The Task Template is where you define the review surface: * Which Data Form the reviewer sees * Which document families and data definitions are relevant * Which actions or statuses complete the work * Which assignment, priority, or team rules should apply * Which fields are required before completion Keep the Activity Plan focused on orchestration. Keep the reviewer experience in the Task Template and Data Form. ## Routing From a Task Downstream steps can depend on the Task outcome. ```json theme={null} [ { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["route:review"], "taskTemplateRef": "invoice-review" }, { "slug": "post-approved-invoice", "type": "BRIDGE_CALL", "dependsOn": ["analyst-review:approved"], "serviceBridgeRef": "finance-erp", "endpointName": "post-invoice" }, { "slug": "notify-rejection", "type": "BRIDGE_CALL", "dependsOn": ["analyst-review:rejected"], "serviceBridgeRef": "finance-erp", "endpointName": "reject-invoice" } ] ``` The Task remains the human unit of work. The Activity uses the Task outcome to keep the business process moving. ## Naming Tasks Use clear, business-facing titles. A reviewer should be able to scan a queue and know what needs attention. Titles can include [runtime placeholders](#runtime-placeholders) — for example `Review ${activity.title}` — so a plan shared across projects still produces context-specific names. Good titles: * `Review invoice INV-10482` * `Resolve vendor mismatch` * `Approve loan packet exception` * `Review missing claim documents` Avoid titles that describe implementation details: * `Step 3 child task` * `Script exception` * `Manual checkpoint` ## Practical Pattern A common Activity Plan shape is: ```mermaid theme={null} flowchart LR Intake["EXECUTION: extract"] --> Route["SCRIPT: route"] Route -->|"clean"| Post["BRIDGE_CALL: post"] Route -->|"review"| Review["CREATE_TASK: analyst review"] Review -->|"approved"| Post Review -->|"rejected"| Reject["BRIDGE_CALL: notify rejection"] ``` The Activity handles the process. The Task exists only for the review segment. ## Checklist * The Task Template exists and is bound to the project. * The Data Form gives the reviewer all required document and data context. * The Activity Plan declares every Task outcome that downstream steps depend on. * The created Task receives the right document families. * Task titles and priorities are meaningful in a real review queue. * Automated follow-up work depends on explicit Task outcomes, not only on the Task existing. Compare CREATE\_TASK with the other step kinds. Build the reviewer interface used by human Tasks. # Execution Steps Source: https://developer.kodexa.ai/guides/activity-plans/execution-steps Run modules and automated processing as EXECUTION steps in Activity Plans, covering extraction, classification, conversion, enrichment, and validation. `EXECUTION` steps run automated processing work. Use them for extraction, classification, conversion, enrichment, validation, or any module-backed processing that should be tracked as part of an Activity. An execution step is the platform showing up in the business process: it does configured work, records status, captures outputs, and gives downstream steps a reliable dependency point. ## When to Use EXECUTION Use `EXECUTION` when the Activity should run a module or pipeline component: * Extract fields from a document * Classify a document family * Convert or normalize document content * Run a reusable validation module * Enrich a document with structured outputs * Apply a processing module across many document families Use `SCRIPT` for inline business logic. Use `BRIDGE_CALL` for external API calls. Use `EXECUTION` for module-backed processing. ## Basic Shape ```json theme={null} { "slug": "extract-invoice", "type": "EXECUTION", "dependsOn": ["intake"], "moduleRef": "kodexa/invoice-extractor", "moduleOptions": { "schema": "invoice" }, "perDocument": true, "maxParallel": 4 } ``` | Step field | Description | | --------------- | ------------------------------------------------------------------- | | `moduleRef` | Module reference to execute | | `moduleOptions` | Module-specific options passed into the execution | | `perDocument` | Run once per document family instead of once for the Activity | | `maxParallel` | Limit parallel launches when `perDocument` is enabled | | `bypass` | Skip the step while keeping downstream dependency behavior explicit | The runtime requires `moduleRef` for an `EXECUTION` step. ## Single Execution Use a single execution when the module should process the Activity context as one unit. ```json theme={null} { "slug": "classify-packet", "type": "EXECUTION", "moduleRef": "kodexa/loan-packet-classifier", "moduleOptions": { "packetType": "{{inputs.packetType}}" } } ``` Kodexa creates an execution pipeline for the module, attaches the Activity and project context, and marks the step running until the execution completes. ## Per-Document Execution Use `perDocument` when each document family should be processed independently. ```json theme={null} { "slug": "extract-each-document", "type": "EXECUTION", "moduleRef": "kodexa/generic-extractor", "perDocument": true, "maxParallel": 5 } ``` This gives the Activity document-level progress. It also lets downstream review steps focus on only the document families that failed or produced exceptions. ```mermaid theme={null} flowchart TB Step["EXECUTION step"] --> D1["Document family A"] Step --> D2["Document family B"] Step --> D3["Document family C"] D1 --> Join["Step result"] D2 --> Join D3 --> Join ``` ## Parallelism `maxParallel` controls how many per-document executions can be launched at once. Keep it high enough for throughput and low enough for downstream systems, model limits, or module resource needs. Typical starting points: | Work type | Starting `maxParallel` | | -------------------------------- | ---------------------: | | Lightweight classification | 5-10 | | Extraction with model calls | 2-5 | | External-system-heavy enrichment | 1-3 | | Resource-intensive conversion | 1-2 | Tune parallelism from runtime evidence: queue depth, module duration, downstream throttling, and error rates. ## Knowledge Assessment Execution steps can run after document knowledge has been assessed and enriched. This matters when a module depends on document family classification, feature assignment, or project knowledge sets. Practical pattern: 1. Intake creates document families. 2. Knowledge assessment identifies document type or relevant features. 3. `EXECUTION` runs the right extractor or classifier. 4. `SCRIPT` or `LLM` routes the outcome. 5. `CREATE_TASK` handles exceptions. ## Downstream Dependencies Downstream steps should depend on the execution step once its automated work is complete. ```json theme={null} [ { "slug": "extract", "type": "EXECUTION", "moduleRef": "kodexa/invoice-extractor", "perDocument": true }, { "slug": "route", "type": "SCRIPT", "dependsOn": ["extract"], "scriptBody": "return { action: inputs.exceptionCount > 0 ? 'review' : 'post' };" } ] ``` Use action-qualified dependencies only when the upstream step emits explicit actions. An `EXECUTION` step usually completes or fails; a following `SCRIPT` step is often where branching belongs. ## Error Handling Design for failure paths. Extraction and classification modules can fail because of document quality, unsupported layouts, missing configuration, or model/provider errors. Good Activity Plans make those outcomes explicit: * Let the execution step fail when the module cannot produce a trustworthy result. * Use a downstream exception task for recoverable data issues. * Use logging and step results for operational troubleshooting. * Keep business routing in `SCRIPT` or `LLM` steps rather than hiding it inside a module when the decision should be visible to workflow owners. ## Checklist * `moduleRef` points to a module available to the project. * `moduleOptions` are explicit and stable. * `perDocument` is enabled only when document-level tracking is useful. * `maxParallel` is tuned for the real module and downstream systems. * The next step knows how to route success, failure, or partial results. * Human review is modeled as `CREATE_TASK`, not hidden inside the module. Route execution results with inline JavaScript. Add human review after automated processing. # Activity Plans Source: https://developer.kodexa.ai/guides/activity-plans/index Activity Plans define reusable automated workflows in Kodexa, with each Activity representing one run of the plan inside a project for document processing. Activity Plans describe how Kodexa should run a document-heavy business process. They are the reusable definition. An Activity is one run of that definition inside a project. Use Activity Plans when you need the platform to coordinate automated work, human review, external system calls, and audit history as one workflow. ## What an Activity Plan Defines An Activity Plan answers four questions: * **What starts the work?** A manual start, intake upload, task status change, another Activity completing, or a [Trigger](/guides/triggers/index). * **What inputs are required?** Document family IDs, project-specific values, routing hints, external reference IDs, or metadata supplied by the Trigger. * **What steps run?** A graph of CREATE\_TASK, SCRIPT, BRIDGE\_CALL, EXECUTION, LLM, AGENT, and APPROVAL steps. * **What result is produced?** Completed tasks, updated documents, external system updates, extracted data, logs, and step results. ```mermaid theme={null} flowchart LR Trigger["Trigger or manual start"] --> Plan["Activity Plan"] Plan --> Activity["Activity run"] Activity --> S1["EXECUTION step"] S1 --> S2["SCRIPT step"] S2 -->|needs review| T1["CREATE_TASK step"] S2 -->|clean| B1["BRIDGE_CALL step"] T1 --> B1 B1 --> Done["Completed Activity"] ``` ## Activity Plan vs Activity | Concept | Scope | What it means | | ------------- | ------------ | ---------------------------------------------------------------------------------------- | | Activity Plan | Organization | The reusable workflow definition: inputs, step graph, defaults, and metadata | | Activity | Project | One execution of an Activity Plan with concrete inputs, status, steps, logs, and results | | Step | Activity | One materialized unit of work inside an Activity run | | Task | Project | Human work created by an Activity when review, correction, or approval is needed | Activity Plans are organization-level resources so the same business process can be reused across projects. A project must bind an Activity Plan before it can start Activities from it. That binding is intentional: it says this project is allowed to run this process with its document stores, task templates, data definitions, data forms, modules, and service bridges. ## Basic Shape An Activity Plan is usually authored as resource configuration and deployed with the rest of your Kodexa project assets. ```json theme={null} { "name": "Invoice Intake", "slug": "invoice-intake", "description": "Classify, extract, review, and post invoice data", "inputsSchema": { "type": "object", "required": ["documentFamilyId"], "properties": { "documentFamilyId": { "type": "string" }, "sourceSystem": { "type": "string" } } }, "defaultTitleTemplate": "Invoice intake for {{documentFamilyId}}", "steps": [ { "slug": "extract", "type": "EXECUTION", "moduleRef": "kodexa/invoice-extractor", "options": { "documentFamilyId": "{{inputs.documentFamilyId}}" } }, { "slug": "route", "type": "SCRIPT", "dependsOn": ["extract"], "scriptActions": [ { "slug": "review", "name": "review" }, { "slug": "post", "name": "post" } ], "scriptBody": "return { action: inputs.needsReview ? 'review' : 'post' };" }, { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["route:review"], "taskTemplateRef": "invoice-review", "waitForCompletion": true }, { "slug": "post-to-erp", "type": "BRIDGE_CALL", "dependsOn": ["route:post", "analyst-review:approved"], "serviceBridgeRef": "finance-erp", "endpointName": "post-invoice", "requestBody": { "documentFamilyId": "{{inputs.documentFamilyId}}" } } ] } ``` ## Step Graphs Steps form a directed graph. Each step has a `slug`, a `type`, optional dependencies, and type-specific fields at the top level of the step. Dependencies can reference a step generally: ```json theme={null} { "dependsOn": ["extract"] } ``` They can also reference a specific action emitted by a branching step: ```json theme={null} { "dependsOn": ["route:review"] } ``` This is how a SCRIPT step, approval step, human task, or bridge call can decide which path the Activity follows next. ### Steps and Actions Are Keyed by Slug An Activity Plan is portable: steps and their dependencies key on slugs, so a plan means the same thing wherever it runs. When an Activity starts, Kodexa projects the plan's steps into fresh Activity-owned step rows and mints new internal identifiers for each one — nothing in `dependsOn` needs to know those runtime IDs. Because slugs are the only identity dependencies resolve against, they must be unambiguous. Kodexa validates this at Activity start: * Every step needs a `slug` and a `kind`. A step missing either fails the start. * **Step slugs must be unique within a plan.** A duplicate slug fails the start rather than silently misrouting every dependency that names it. * **Action slugs must be unique within a step.** Duplicate action slugs on one step fail the start. Author `slug` values you control and keep them stable — renaming a slug is a breaking change to every step that depends on it. ### dependsOn Forms `dependsOn` accepts four forms: | Form | Meaning | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `"slug"` | Run after the named step reaches `COMPLETED` or `SKIPPED`. | | `"slug?"` | Await-only dependency: also satisfied when the upstream step is `NOT_TAKEN` (its branch was not chosen). An await-only dependency requires a `conditionExpr` on the step — Kodexa fails the Activity start with `step "" has await deps (? suffix) but no conditionExpr` if one is absent (the only exception is an `ANY_BRANCH` join whose union supplies its own guard). | | `"slug:action"` | Run after the named step completes with that specific action. | | `"slug?:action"` | Await-only, action-qualified. The `?` may sit on either side of the colon (`"slug?:action"` and `"slug:action?"` are equivalent). | A dependency on an unknown step slug fails the Activity start with `step "" depends on unknown slug ""`. ### Action-Qualified Dependencies Resolve Against the Live Action Source The action qualifier in `"slug:action"` must name an action the source step can actually emit at runtime. Each step kind has a single live action source: | Step kind | Live action source | | ------------- | ------------------------------------------------- | | `SCRIPT` | `scriptActions` | | `BRIDGE_CALL` | `bridgeActions` | | `LLM` | `promptActions` | | `CREATE_TASK` | the referenced Task Template's `metadata.actions` | The qualifier can be an action's declared `slug` or, for legacy plans, its `uuid` value. References that could never fire are rejected at Activity start with a precise message — for example, an action declared only in a `CREATE_TASK` step's inline `actions` array (a documentation mirror; tasks render actions from the template), or an action a step kind does not execute. A qualifier that matches more than one action sharing a slug also fails as ambiguous. In earlier releases, an unresolvable action reference was stored verbatim and produced a silently dead branch that never fired. It now fails the Activity start loudly, so a mistyped or stale action reference surfaces immediately. ### Legacy `uuid` Identities Plans authored before slug portability carried a `uuid` on steps and actions. Kodexa treats an authored `uuid` as a **legacy alias of the slug** — the same identity space — so older plans and their `dependsOn` references keep working: * A step's authored `uuid` is never a database identity; every run mints fresh internal IDs. A `dependsOn` entry that names a step by its authored `uuid` still resolves to that step's slug. * An action declaring only a `uuid` (no `slug`) adopts the `uuid` value as its slug. Aliases obey the same uniqueness rules as slugs, and identity conflicts fail the Activity start: * Two steps declaring the same authored `uuid`, or an authored `uuid` that collides with another step's slug, fail the start. * An action declaring both a `uuid` and a `slug` with **different** values fails the start — `uuid` is the legacy spelling of `slug`, so keep the slug. Equal values are the accepted aliased form. New plans carry no `uuid` fields. When an Activity starts from a plan that still holds legacy `uuid` identities, Kodexa logs a warning noting how many step and action uuids remain. Resaving the plan in the plan editor rewrites those uuids as slugs and converges the plan to the portable form. ## Common Step Kinds | Step kind | Use it for | | ------------- | ------------------------------------------------------------------------------------------------- | | `CREATE_TASK` | Create human review, exception, approval, or correction work from a Task Template | | `SCRIPT` | Run inline JavaScript for routing, document inspection, data updates, enrichment, or custom logic | | `BRIDGE_CALL` | Call a configured Service Bridge as a first-class workflow step | | `EXECUTION` | Run a module or automated extractor/classifier | | `LLM` | Run a prompt or prompt template and map the result into the Activity | | `AGENT` | Delegate a bounded operation to an agent runtime | | `APPROVAL` | Pause for approval before continuing | ## A Practical Modeling Order 1. Start with the business process: what outcome does the organization need? 2. Define the documents and data model: what document families and data definitions are involved? 3. Identify the automation: extraction, classification, validation, enrichment, posting, and notifications. 4. Identify the human work: which decisions need a Task? 5. Identify external systems: which Service Bridges are needed? 6. Convert that model into an Activity Plan step graph. 7. Bind the Activity Plan and all referenced resources to the project that will run it. Automatically generate contextual titles and descriptions using AI. Learn each Activity Plan step kind and when to use it. Create human review, correction, and exception work from an Activity. Run module-backed extraction, classification, and processing work. Add JavaScript routing and business logic to an Activity Plan. Call external systems as first-class Activity Plan steps. Run bounded prompts and map model output into the workflow. Delegate bounded work to agent runtimes. Model authorization gates and approval outcomes. # LLM Steps Source: https://developer.kodexa.ai/guides/activity-plans/llm-steps Use LLM steps in Activity Plans to run bounded prompts, map model output, and make AI reasoning explicit, observable, and connected to the workflow graph. `LLM` steps run a prompt or prompt template as a first-class step in an Activity. Use them when model reasoning should be explicit, bounded, observable, and connected to the workflow graph. Use an `LLM` step for a prompt. Use an `AGENT` step when the work requires tool use, investigation, or multi-step execution. Some current runtime fields and stored item types still use the legacy name `AI_PROMPT`. In Activity Plan documentation, the concept is `LLM`: a prompt-backed step with inputs, model configuration, output mapping, and downstream actions. ## When to Use LLM Use `LLM` when the workflow needs model reasoning such as: * Summarizing an exception for a reviewer * Explaining why a document failed validation * Classifying a document against a bounded set of business categories * Extracting a small structured decision from already-prepared context * Drafting a recommendation that a human will approve * Mapping messy text into a constrained JSON shape Do not use `LLM` as an unbounded workflow controller. The Activity Plan should own the process. The LLM step should own one clear judgment or transformation. ## Basic Shape ```json theme={null} { "slug": "summarize-exception", "type": "LLM", "dependsOn": ["validate:exception"], "promptTemplateRef": "invoice-exception-summary", "llmModelName": "gpt-4.1", "includeDocument": true, "promptVariables": { "vendorName": "$.task.data.vendorName", "exceptionCodes": "$.steps.validate.mapped_output.exceptionCodes" }, "outputMapping": { "summary": "$.summary", "risk": "$.risk", "action": "$.recommendedAction" }, "promptActions": [ { "slug": "review", "name": "review" }, { "slug": "auto-clear", "name": "auto-clear" } ] } ``` | Step field | Description | | ------------------- | ---------------------------------------------------------- | | `promptBody` | Inline prompt text | | `promptTemplateRef` | Reusable prompt template reference | | `llmModelName` | Model to use when invoking the AI gateway | | `promptVariables` | JSONata expressions used to build prompt variables | | `includeDocument` | Include document content in the prompt context | | `enrichment` | Optional Service Bridge enrichment before prompt rendering | | `outputMapping` | JSONata mapping from model response into step output | | `promptActions` | Allowed downstream actions emitted by the step | | `perDocument` | Run the prompt separately for each document family | Use either `promptBody` or `promptTemplateRef`. Prompt templates are better when the prompt should be reviewed, versioned, and reused. ## Prompt Variables Prompt variables keep prompts readable and make input selection explicit. ```json theme={null} { "promptVariables": { "invoiceNumber": "$.task.data.invoiceNumber", "poNumber": "$.task.data.poNumber", "validationErrors": "$.steps.validate.mapped_output.errors" } } ``` These expressions are evaluated against the Activity context, including organization, task, inputs, document data, and prior step results. ## Output Mapping Output mapping turns the model response into stable workflow data. ```json theme={null} { "outputMapping": { "summary": "$.summary", "riskLevel": "$.risk.level", "action": "$.decision" } } ``` The mapped `action` can drive downstream dependencies: ```json theme={null} [ { "slug": "summarize-exception", "type": "LLM", "promptActions": [ { "slug": "review", "name": "review" }, { "slug": "auto-clear", "name": "auto-clear" } ] }, { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["summarize-exception:review"] }, { "slug": "clear-exception", "type": "SCRIPT", "dependsOn": ["summarize-exception:auto-clear"] } ] ``` Keep actions constrained. The Activity Plan should list every action the LLM step is allowed to emit. ## Including Documents Set `includeDocument` when the prompt needs document content. ```json theme={null} { "slug": "classify-letter", "type": "LLM", "promptTemplateRef": "classify-correspondence", "includeDocument": true, "outputMapping": { "category": "$.category", "confidence": "$.confidence" } } ``` For large documents, prefer preparing the exact context first with `EXECUTION` or `SCRIPT`, then prompting against the smaller extracted context. This makes model behavior easier to test and review. ## Per-Document LLM Steps Use `perDocument` when each document family needs its own model decision. ```json theme={null} { "slug": "summarize-each-claim-document", "type": "LLM", "promptTemplateRef": "claim-document-summary", "includeDocument": true, "perDocument": true } ``` Per-document steps produce document-level outputs that can be used by downstream routing and review tasks. ## Enrichment Before Prompting An LLM step can enrich context before rendering the prompt. Use enrichment when the model needs reference data from a system of record, but the model should not call that system directly. ```json theme={null} { "enrichment": [ { "serviceBridgeRef": "vendor-master", "endpointName": "lookup-vendor", "requestBody": { "vendorId": "$.task.data.vendorId" }, "outputKey": "vendor" } ] } ``` The Activity Plan stays explicit: bridge enrichment happens first, prompt rendering happens second, output mapping happens third. ## LLM vs AGENT | Need | Use | | ---------------------------------------------------- | ------------------------- | | One prompt with known inputs and mapped output | `LLM` | | A model-generated summary for a reviewer | `LLM` | | Tool use across project resources | `AGENT` | | Multi-step investigation with intermediate decisions | `AGENT` | | Workflow routing with strict allowed actions | Usually `SCRIPT` or `LLM` | ## Checklist * The prompt has a bounded purpose. * The prompt uses explicit variables instead of hidden context. * Model output is mapped into stable fields. * Downstream actions are declared and constrained. * Human review is used where model output should not be final. * The prompt is tested against real document examples and exception cases. Use agent runtimes for tool-using work. Route model results into human review. # Reprocessing an Activity Source: https://developer.kodexa.ai/guides/activity-plans/reprocessing Run part of an Activity again: retry the failures to resume after an error, or reprocess from a chosen step to deliberately redo completed work. An Activity can be run again in part. There are two operations, and the difference between them is how much already-completed work is redone: * **Retry failures** resumes an Activity that stopped on an error. It is the safe option: successful work at the failed step is kept. * **Reprocess from step** deliberately re-runs a step you choose, including work that had already completed there, and rewinds what the steps after it produced. Both are exposed on `POST /api/activities/{id}/retry` and both require the `activity:retry` permission on the Activity. ## Choosing Between Them | | Retry failures | Reprocess from step | | ---------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | How you invoke it | `POST /api/activities/{id}/retry` with no body | `POST /api/activities/{id}/retry` with `{"stepId": "..."}`, or **Reprocess** on a step in the Activity view | | Which steps are reset | Every `FAILED` and `CANCELLED` step | The step you selected | | Downstream steps | Reset and re-run, even where they had completed | Reset and re-run, even where they had completed | | Completed per-document work at that step | Kept | Re-run | | Content produced downstream | Rolled back | Rolled back | | Downstream review tasks | Deleted and recreated | Deleted and recreated | | Use it when | The Activity failed, the underlying problem is fixed, and you want to pick up where it stopped | The step's logic, module, prompt, or configuration changed and its earlier output is no longer the answer you want | Reprocess from step is destructive by design. Content produced after the selected step is deleted, and review tasks downstream of it are deleted along with the work recorded on them. Reach for **Retry failures** when the goal is only to resume after an error. ## Retry Failures Sending `POST /api/activities/{id}/retry` with no body resets every step in `FAILED` or `CANCELLED` state, plus everything transitively downstream of those steps. * **Cancelled steps are included.** When an Activity deadlocks on a failure, its still-pending steps are cancelled; a retry has to revive those too, or the plan cannot finish. * **The failed step keeps its successful documents.** On a per-document step, only the `FAILED`, `CANCELLED`, and in-flight document rows are reset. Documents that already completed there are left alone, so a retry does not redo work that succeeded. * **Downstream work re-runs regardless.** Anything after a reset step is re-run even where it had completed, because its input may now be different. * **The Activity resumes immediately.** It returns to `RUNNING`, its completion time and error details are cleared, its version is bumped, and the plan is advanced in the same transaction. The response reports the Activity's real state afterwards, which can already be terminal again if a reset step fails on its first attempt. * **A retry with nothing to revive is a no-op.** If no step is `FAILED` or `CANCELLED`, the Activity is left exactly as it is — a completed Activity is never restarted, and its completion is never re-announced. ## Reprocess From Step Select a step in the Activity view and choose **Reprocess**, or send the step's ID as `{"stepId": "..."}`. The option applies to a step in `COMPLETED`, `FAILED`, or `CANCELLED` state. Targeting a step explicitly means "run this again", so the contract is stronger than a retry: 1. **The selected step re-runs in full.** Its own per-document rows are reset even where they had `COMPLETED`, so the step actually executes instead of settling straight back to completed. 2. **Every step downstream re-runs**, transitively, following the plan's dependency graph. 3. **Downstream content is rolled back.** For each document in scope, Kodexa finds the content the rewound work started from and deletes every version derived from it, so the re-run reads the same input the first run did instead of consuming its own earlier output. The starting version itself is kept. 4. **Downstream review tasks are recreated.** A `CREATE_TASK` step after the selected step has its task deleted, and creates a fresh one when it runs again. 5. **Superseded runs are marked, not erased.** Executions the reprocess replaced are recorded with status `REPROCESSED`, so the history stays readable, and the workspace stops following them instead of waiting for results that will never arrive. ```mermaid theme={null} flowchart LR Extract["EXECUTION: extract
untouched"] --> Score["SCRIPT: score
re-runs (selected)"] Score --> Review["CREATE_TASK: review
task recreated"] Review --> Post["BRIDGE_CALL: post
re-runs"] ``` Reprocessing from `score` re-runs `score`, rolls back what `review` and `post` produced, deletes and recreates the review task, and leaves `extract` and its output untouched. ### Documents That Are Not Rolled Back The rollback is attributed per document, so it only touches documents the rewound steps actually ran over: * A document that none of the rewound runs processed keeps all of its content. Kodexa would rather leave such a document alone than rewind it to its original upload. * A document that a routing step sent down a different branch is not revived by reprocessing. Routing owns that decision, and it is re-evaluated when the step runs again. ### Routing on the Re-Run A routing step can reach a different decision on the re-run — for example when its condition reads document status or metadata that has since changed. Kodexa keeps the document rows honest in both directions: * A document that is now routed away is flipped to `NOT_TAKEN`, and its execution reference, error details, completed action, and content references are cleared. A not-taken document therefore shows no stale run, and its superseded output cannot be mistaken for a current result or used as the source of a later rollback. * A document that is eligible again — where the previous run had recorded `NOT_TAKEN` — is returned to `PENDING` with those same references cleared, so it is picked up by the launch path and starts from a clean state. ## After Reprocessing * Check the step's logs and result on the re-run rather than the values you saw before it; the earlier execution is now marked `REPROCESSED`. * A recreated review task is a new Task. Anything a reviewer had entered on the deleted one is gone, so tell reviewers before reprocessing a step whose downstream work is already in a queue. * If the Activity fails again at the same step, the cause is usually upstream of it: fix the input or the step configuration, then retry the failures rather than reprocessing further back. Step types, dependencies, routing, and per-document plans. How review tasks are created, and which documents they carry. # Script Steps in Activity Plans Source: https://developer.kodexa.ai/guides/activity-plans/script-steps Use SCRIPT steps in Activity Plans to run JavaScript for routing, enrichment, and document logic that is too specific for declarative steps. A `SCRIPT` step runs JavaScript inside an Activity. It is the right tool when the workflow needs custom logic that is too specific for a declarative step. Script steps are commonly used to inspect documents, normalize extracted data, assign knowledge features, call Service Bridges, and decide which branch of the Activity Plan should run next. ## When to Use a SCRIPT Step Use a `SCRIPT` step when you need to: * Route the Activity based on document content, metadata, extraction results, or task status * Make several checks before deciding whether human review is needed * Update document metadata, labels, tags, data objects, or attributes * Assign knowledge features to document families * Call one or more Service Bridges and combine their responses * Emit a named action for downstream dependencies Do not use a script just to make one external API call. Use a [Service Bridge step](/guides/activity-plans/service-bridge-steps) for that so the external call has its own step status, logs, retry behavior, and result. ## Step Configuration ```json theme={null} { "slug": "route-invoice", "type": "SCRIPT", "dependsOn": ["extract"], "scriptActions": [ { "slug": "review", "name": "review" }, { "slug": "post", "name": "post" }, { "slug": "reject", "name": "reject" } ], "scriptSidecars": ["acme-finance/invoice-script-helpers"], "scriptBody": "return { action: 'review' };" } ``` | Step field | Description | | ---------------- | ---------------------------------------------------- | | `scriptBody` | Inline JavaScript source | | `scriptActions` | Named actions the script can return | | `scriptSidecars` | JavaScript module refs loaded before the script runs | The script must return an object with an `action` property when downstream routing depends on the step. ```javascript theme={null} return { action: "review" }; ``` Downstream steps can depend on that action: ```json theme={null} { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["route-invoice:review"] } ``` **Actions are identified by their `slug`.** Downstream `dependsOn` entries use `"step:actionSlug"` to match a specific outcome. The action's `name` is the value the script returns (e.g. `return { action: "review" }`) and the display label the runtime surfaces in logs. Older plans use a `uuid` field on each action — that is the legacy spelling of `slug` and still resolves, but new authoring should use `slug`. ## Return Value The full return value can carry several optional pieces alongside `action`: ```javascript theme={null} return { action: "approve", features: [ { documentFamilyId: families[0].id, featureId: "fc_billing_ready" } ], taskProperties: { invoiceRegion: "EMEA", priorityCustomer: true }, nextActivity: { activityPlanRef: "activity-plan://acme-finance/billing-extraction", inputs: { reviewedBy: org.userEmail } } }; ``` | Field | Description | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `action` | Required. Must match one of the `scriptActions` names declared on the step. | | `features` | Optional. Attaches knowledge features to document families on the current Activity. | | `taskProperties` | Optional. An object stamped onto the `properties` of any task created by a CREATE\_TASK step that directly depends on this script step. | | `nextActivity` | Optional. Requests a follow-up Activity Plan to start when the current Activity completes. | `features`, `taskProperties`, and `nextActivity` are independent — return any combination, or none. ## Handing Properties to a Created Task A SCRIPT step that gates a CREATE\_TASK step is the last code to run with document context before the child task exists — so it's the natural place to compute values the task should carry from birth (grouping keys, routing hints, anything your task consumers need). Return them under `taskProperties`: ```javascript theme={null} var doc = loadDocument(families[0].id); var region = String(doc.findFirstDataObjectByPath("invoices") .getFirstAttributeValue("region") || "").trim(); return { action: "continue", taskProperties: region ? { invoiceRegion: region } : {} }; ``` When the downstream CREATE\_TASK materializes, these keys are merged into the new task's `properties` at creation time — atomically with the insert, so consumers never observe the task without them. They appear on every task API response and are queryable with the standard filter DSL (e.g. `filter=properties.invoiceRegion: 'EMEA'`). Semantics: * Only CREATE\_TASK steps that **directly depend on** the script step receive the properties — there is no transitive propagation. * Static `taskData.properties` defined on the CREATE\_TASK step apply first; script-supplied keys win on conflict. * If several upstream script steps supply `taskProperties`, they merge in completion order (the latest completion wins per key). * The mechanism is failure-soft: a non-object `taskProperties`, or a merged payload over 256KB, is dropped with a warning — task creation itself never fails because of it. * To update properties on a task that already exists, use `tasks.setProperties` instead; `taskProperties` is the creation-time counterpart. ## Spawning a Follow-Up Activity A SCRIPT step can request another Activity Plan to start automatically when the current Activity completes. Use this to chain related work, e.g. "after intake classifies an invoice, kick off the extraction plan." The spawned Activity: * Starts only after the **current Activity** reaches `COMPLETED` (not on individual step completion). * Inherits the current Activity's project. The target plan must already be bound to that project. * Inherits the current Activity's document families when `documentFamilyIds` is omitted. * Records its source in `triggerMetadata` (`sourceActivityId`, `sourceStepId`, `sourceActionUuid`, `sourceProjectId`) so audit trails work in both directions. * Has `triggerKind` set to `ACTIVITY_COMPLETED`. ### nextActivity shape ```javascript theme={null} return { action: "approve", nextActivity: { activityPlanRef: "activity-plan://acme-finance/billing-extraction", inputs: { reviewedBy: org.userEmail, source: "intake" }, title: "Billing for " + families[0].name, description: "Auto-spawned from invoice intake", documentFamilyIds: [families[0].id], features: [ { documentFamilyId: families[0].id, featureId: "fc_billing_ready" } ], priority: 5, triggerMetadata: { batchId: task.data.batchId } } }; ``` | Field | Required | Description | | ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | | `activityPlanRef` | Yes | Slug or `activity-plan://orgSlug/planSlug` of the plan to start. Must be bound to the current project. | | `inputs` | No | Object validated against the target plan's `inputOptions` at spawn time. | | `title` | No | Override the spawned Activity's title. Defaults to the plan's `defaultTitleTemplate`. | | `description` | No | Override the description. Defaults to the plan's `defaultDescriptionTemplate`. | | `documentFamilyIds` | No | Document families to link to the spawned Activity. Defaults to inheriting the current Activity's families. | | `features` | No | Knowledge features to attach to document families *before* the spawn fires, so the new plan's templates and scripts see them. | | `priority` | No | Priority for the spawned Activity. | | `triggerMetadata` | No | Free-form metadata. Server-controlled fields (`sourceActivityId`, etc.) always win over anything you set here. | ### Failure handling Spawn failures are **soft**. If the target plan doesn't exist, the plan is not bound to the current project, or inputs fail validation, the source Activity still finishes cleanly. The failure is recorded on the source step in `script_result.nextActivityError`. On success, `script_result.nextActivityId` is set to the new Activity's ID. ### Multiple spawns Each SCRIPT step in a plan may emit its own `nextActivity`. They fan out at the source Activity's completion in step insertion order. Within a single script return, only one `nextActivity` is supported. ### Same-project only A script can only spawn Activity Plans bound to the **current project**. Cross-project spawns are rejected. If you need fan-out across projects, model it through a Service Bridge call instead. ## Activity SCRIPT Runtime Activity Plan `SCRIPT` steps run in the server-side JavaScript runtime. The runtime exposes the same business objects the Activity is working on, plus helper namespaces for task state, document families, document content, Service Bridges, LLM calls, and knowledge features. | Object | Use | | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `task` | Snapshot of the Task that started the Activity, when the Activity is task-backed | | `families` | Snapshot array of document families attached to the Task | | `org` | Current organization `{ id, slug }` | | `inputs` | The Activity's materialized inputs object — the same values `BRIDGE_CALL` `requestBody` templates resolve against. Defaults to `{}` when the Activity has no inputs, so you can read `inputs.foo` directly without a `typeof` guard. | | `tasks` | Query and mutate task records in the current project | | `documents` | Query and mutate document family records in the current project | | `loadDocument(familyId)` / `documents.load(familyId)` | Load a KDDB document for content nodes, tags, data objects, metadata, labels, validations, and exceptions | | `lookupFeature*` | Find or create knowledge features for classification and routing | | `serviceBridge` | Call project-scoped Service Bridges from custom decision logic | | `llm` | Invoke configured LLM services with platform cost tracking | | `log` / `console` | Write script logs visible from the Activity step details | The current Activity Plan script context is centered on the Task and its document families. The Activity's materialized inputs are also available through the read-only `inputs` global, which defaults to `{}` when the Activity has no inputs — so you can read values such as `inputs.invoiceNumber`, `inputs.routeId`, or `inputs.correlationId` directly. This `inputs` global is a snapshot for reading and is distinct from the `nextActivity.inputs` spawn payload above. For prior step outputs, use declarative step mappings and pass the needed values into document metadata, task data, or downstream step configuration. ### Runtime Limits | Limit | Value | | ------------------------------------------ | ----------------------- | | Script timeout | 300 seconds (5 minutes) | | Document loads | 5 per script execution | | Task reads / document family reads | 50 per namespace | | Task mutations / document family mutations | 20 per namespace | | Task/document mutation payload | 256 KB per call | The 300 second (5 minute) script timeout is the maximum wall-clock time the `SCRIPT` body's JavaScript may run before it is aborted. Other step kinds use their own distinct timeouts: `BRIDGE_CALL` steps default to a 30 second HTTP timeout (author-overridable via the step's `timeout`, capped at 120 seconds), and `AI_PROMPT` steps allow up to 120 seconds for the LLM call. The script timeout and two related orchestrator budgets are operator-tunable through environment variables on the orchestrator. Authors do not set these per step; they are platform-wide defaults. | Environment variable | Bounds | Default | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `PLANNER_SCRIPT_TIMEOUT` | A single `SCRIPT` step's JavaScript execution | `300s` | | `PLANNER_ADVANCE_TIMEOUT` | The detached plan-advance that runs after a step result completes (which may run further steps under one budget) | `10m` | | `PLANNER_BRIDGE_CALL_TIMEOUT` | Only the `BRIDGE_CALL` request-shaping work (building the call's body, query, path, and headers) — **not** the outbound HTTP call, which is governed by the step's `timeout` above | `5s` | Plan-advance runs on its own detached budget, so a returning step-result request no longer cancels it and long `SCRIPT` steps are no longer stranded mid-advance. Because a `SCRIPT` step running inside plan-advance has its binding-side database and Service Bridge calls bounded by the advance budget, always keep `PLANNER_ADVANCE_TIMEOUT` greater than or equal to `PLANNER_SCRIPT_TIMEOUT` (the defaults preserve roughly 3x headroom). If the advance budget expires first, the script fails with `context deadline exceeded` before its own script timeout would fire. Document changes are saved after the script completes. Modified KDDB documents are persisted as new content object versions with the `SCRIPT_MODIFICATION` transition type. ### Task Namespace Use `tasks` when a script needs to inspect or update human review work inside the current project. | Method | Description | | --------------------------------------------- | ---------------------------------------------------------------------------------- | | `tasks.current()` | Load the current Task | | `tasks.parent()` | Load the current Task's parent, if any | | `tasks.subTasks()` | List subtasks of the current Task | | `tasks.subTasksOf(taskId)` | List subtasks for another Task in the same project | | `tasks.get(taskId)` | Load a Task by ID | | `tasks.query(filter)` | Query Tasks by `status`, `statusType`, `parentTaskId`, `templateSlug`, and `limit` | | `tasks.lookupStatus(slug)` | Resolve a project Task status | | `tasks.listStatuses()` | List project Task statuses | | `tasks.setStatus(taskId, statusSlug)` | Change Task status and trigger Activity advancement when the status type is `DONE` | | `tasks.setProperties(taskId, props)` | Merge properties into Task data | | `tasks.setMetadata(taskId, metadata)` | Merge Task metadata | | `tasks.setTitle(taskId, title)` | Update Task title | | `tasks.setDescription(taskId, description)` | Update Task description | | `tasks.lock(taskId)` / `tasks.unlock(taskId)` | Lock or unlock a Task | ### Document Family Namespace Use `documents` when a script needs to manage document family state without loading the full KDDB document. | Method | Description | | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `documents.taskFamilies()` | List document families attached to the current Task | | `documents.get(familyId)` | Load a document family summary | | `documents.getByPath(storeSlug, path)` | Find a document family by store and path | | `documents.query(filter)` | Query families by `storeSlug`, `path`, `pathContains`, `label`, `mixin`, `status`, `featureSlug`, `locked`, and `limit` | | `documents.contentObjects(familyId)` | List content object versions | | `documents.latestContentObject(familyId, contentType?)` | Get the latest content object, optionally `DOCUMENT` or `NATIVE` | | `documents.load(familyId)` | Load the KDDB document, equivalent to `loadDocument(familyId)` | | `documents.lookupStatus(slug)` / `documents.listStatuses()` | Resolve document statuses | | `documents.setMetadata(familyId, metadata)` | Merge metadata into the family | | `documents.addLabel(familyId, label)` / `documents.removeLabel(familyId, label)` | Manage family labels | | `documents.setStatus(familyId, statusSlug)` | Update document status. For a fixed status set on step completion, prefer the step-level [`setDocumentStatus`](/guides/activity-plans/steps#document-status) field; use this call when the status is conditional or computed | | `documents.lock(familyId)` / `documents.unlock(familyId)` | Lock or unlock the family | | `documents.attachToTask(familyId, taskId?)` / `documents.detachFromTask(familyId, taskId?)` | Manage Task-family links | ## Routing Example This script loads the first Task document, inspects its text, and routes the Activity to review, rejection, or straight-through posting. ```javascript theme={null} var family = families[0]; var doc = loadDocument(family.id); var text = doc.getRootNode().getAllContent(" ", true).toLowerCase(); if (text.indexOf("duplicate invoice") !== -1) { log.warn("Duplicate invoice language detected"); return { action: "reject" }; } var invoice = doc.findFirstDataObjectByPath("invoice"); var amount = invoice ? Number(invoice.getFirstAttributeValue("total_amount")) : 0; if (!amount || amount > 10000) { return { action: "review" }; } return { action: "post" }; ``` ## Updating Documents Document changes made by a script are persisted after the step completes. ```javascript theme={null} var family = families[0]; var doc = loadDocument(family.id); doc.setMetadata("scriptStep", "tag-total-lines"); doc.addLabel("invoice-intake"); var root = doc.getRootNode(); var matches = root.select("//line[contains(@content, 'TOTAL')]"); for (var i = 0; i < matches.length; i++) { matches[i].tag("invoice/total-line", { confidence: 0.95, value: matches[i].getContent() }); } return { action: "tagged" }; ``` Do not call `doc.close()` in Activity Plan scripts. Kodexa persists document changes after the script completes. ## Calling Service Bridges from a Script Use `serviceBridge.call()` inside a SCRIPT step when the decision requires more than a single declarative bridge call. ```javascript theme={null} var supplier = serviceBridge.call( "finance-reference-data", "lookup-supplier", { supplierId: task.data.supplierId } ); if (!supplier || supplier.status !== "active") { log.warn("Supplier is not active:", task.data.supplierId); return { action: "review" }; } var duplicate = serviceBridge.call( "finance-reference-data", "check-duplicate-invoice", { supplierId: task.data.supplierId, invoiceNumber: task.data.invoiceNumber } ); return { action: duplicate && duplicate.found ? "review" : "post" }; ``` Service bridge credentials stay in the platform. Scripts reference the configured bridge and endpoint; they do not handle secrets directly. ## Shared Script Sidecars Use `scriptSidecars` to pre-load reusable JavaScript helpers. ```json theme={null} { "slug": "normalize-fields", "type": "SCRIPT", "scriptSidecars": ["acme-finance/invoice-script-helpers"], "scriptActions": [{ "slug": "normalized", "name": "normalized" }], "scriptBody": "normalizeInvoiceFields(families[0].id); return { action: 'normalized' };" } ``` Sidecars are useful for shared validation functions, string cleanup, request mapping, and common routing decisions. Keep sidecars small and version them like any other project resource. ## Design Guidance * Declare every returned action in `scriptActions`. * Keep scripts focused on one decision or one transformation. * Prefer `BRIDGE_CALL` for a single external API call that operators should see as its own step. * Use logs at important decision points. * Make scripts idempotent when they can be retried. * Avoid long loops and repeated document loads. ## Next Steps See the full JavaScript context, document helpers, LLM calls, and logging reference. Learn when to model an external API call as its own Activity Plan step. # Service Bridge Steps in Activity Plans Source: https://developer.kodexa.ai/guides/activity-plans/service-bridge-steps Use BRIDGE_CALL steps in Activity Plans to call external systems through service bridges with first-class status, retries, logs, and audit trails. A `BRIDGE_CALL` step calls a configured Service Bridge from an Activity Plan. It is the right tool when an external system call is part of the business process and should have its own status, result, logs, retry behavior, and audit trail. Service Bridges keep authentication, target URLs, request rules, caching, and endpoint configuration outside of the Activity Plan. The Activity Plan references the bridge and endpoint, then supplies the request data for this workflow. ## When to Use a BRIDGE\_CALL Step Use a `BRIDGE_CALL` step when you need to: * Validate extracted data against a system of record * Fetch enrichment data before review * Post approved data to an ERP, loan origination, claims, KYC, or compliance system * Notify an external workflow when a Kodexa Activity completes * Keep the external call visible as its own monitored workflow step Use `serviceBridge.call()` inside a [SCRIPT step](/guides/activity-plans/script-steps) when the bridge call is only one part of a larger custom decision. ## Step Configuration ```json theme={null} { "slug": "post-approved-invoice", "type": "BRIDGE_CALL", "dependsOn": ["analyst-review:approved"], "serviceBridgeRef": "finance-erp", "endpointName": "post-invoice", "requestBody": { "documentFamilyId": "{{inputs.documentFamilyId}}", "sourceSystem": "{{inputs.sourceSystem}}" }, "treatAsError": "$.status != 'accepted'", "timeoutSeconds": 30, "disableCache": true } ``` | Step field | Description | | ------------------ | ----------------------------------------------------------------- | | `serviceBridgeRef` | Service Bridge slug or ref | | `endpointName` | Endpoint defined by the bridge | | `requestBody` | Static body, template, or expression that builds the JSON request | | `requestQuery` | Optional query-string template | | `requestPath` | Optional path substitution | | `requestHeaders` | Optional request headers, excluding secrets | | `requestScript` | Optional JavaScript preprocessor for complex request construction | | `bridgeActions` | Actions the bridge response can emit for downstream routing | | `treatAsError` | Predicate that marks a response as a failed step | | `timeoutSeconds` | Per-call timeout | | `disableCache` | Whether to bypass bridge-level caching for this call | ## How It Runs ```mermaid theme={null} sequenceDiagram participant Activity participant BridgeStep as BRIDGE_CALL step participant KodexaAPI as Kodexa API participant External as External system Activity->>BridgeStep: Start step with inputs BridgeStep->>KodexaAPI: Resolve serviceBridgeRef and endpointName KodexaAPI->>KodexaAPI: Inject configured auth and request rules KodexaAPI->>External: Send request External-->>KodexaAPI: Response KodexaAPI-->>BridgeStep: Parsed result BridgeStep-->>Activity: Complete, fail, or emit action ``` The step result is stored on the Activity step. Operators can inspect the response, failure details, and logs without digging through a script. ## Request Mapping Start with a static JSON body when possible: ```json theme={null} { "requestBody": { "documentFamilyId": "{{inputs.documentFamilyId}}", "projectId": "{{project.id}}" } } ``` Use `requestScript` when the request needs conditional shaping: ```json theme={null} { "requestScript": "return { invoiceId: inputs.invoiceId, amount: Number(inputs.amount || 0), urgent: Number(inputs.amount || 0) > 10000 };" } ``` Keep request scripts small. If you need several external calls or substantial document mutation, use a SCRIPT step. ## Response Routing A bridge call can emit actions so downstream steps depend on the external response. ```json theme={null} { "slug": "validate-supplier", "type": "BRIDGE_CALL", "serviceBridgeRef": "finance-reference-data", "endpointName": "validate-supplier", "requestBody": { "supplierId": "{{inputs.supplierId}}" }, "bridgeActions": [ { "slug": "valid", "name": "valid", "when": "$.valid = true" }, { "slug": "review", "name": "review", "when": "$.valid != true" } ] } ``` Then route downstream work: ```json theme={null} { "slug": "supplier-review", "type": "CREATE_TASK", "dependsOn": ["validate-supplier:review"], "taskTemplateRef": "supplier-exception-review" } ``` ## Per-Document Bridge Calls By default a `BRIDGE_CALL` step makes a single HTTP call for the whole Activity. Set `perDocument` to `true` to fan the step out into one call per routed document instead. ```json theme={null} { "slug": "post-each-invoice", "kind": "BRIDGE_CALL", "config": { "serviceBridgeRef": "finance-erp", "endpointName": "post-invoice", "perDocument": true, "requestBody": { "sourceSystem": "{{inputs.sourceSystem}}" }, "bridgeActions": [ { "name": "posted", "when": "$.status = 'accepted'" }, { "name": "review", "when": "$.status != 'accepted'" } ] } } ``` When `perDocument` is enabled, the step processes the routed documents one at a time. Each call's request context is augmented with that document's `documentFamilyId`, `extractedData` (the document family metadata), and `documentPath`, so the same request mapping can reference the current document. Per-document behavior: * **Each document resolves its own action.** Response routing runs per call, so different documents can take different downstream branches. * **Failures are isolated.** A request-build error, a bridge transport error, or a `treatAsError` match fails only that document; the remaining documents continue. (An output-mapping expression that fails to compile or evaluate is logged as a warning and skipped — it does not fail the document.) * **The step gets a consensus action only when every completed document resolves the same action.** If documents disagree, no step-level action is emitted, but each document still records the action it resolved. * **The join policy governs the step rollup.** The default is `ALL_SETTLED`; set `joinPolicy` to `ANY_BRANCH` to complete the step as soon as one branch is satisfied. * **Calls run sequentially.** `maxParallel` does not apply to per-document `BRIDGE_CALL` steps. The step result aggregates the per-document outcomes: ```json theme={null} { "perDocument": true, "documents": [ { "documentFamilyId": "...", "status": "COMPLETED", "action": "posted", "statusCode": 200 }, { "documentFamilyId": "...", "status": "FAILED", "statusCode": 502, "error": "..." } ] } ``` In the activity status dialog's Activity Steps tab, a per-document step shows individual document status pills when four or fewer documents are routed, and a completed/total progress bar for larger fan-outs. When routing is in effect, an activity-level banner reports any unrouted documents whose resolved action matched no outgoing branch. `perDocument` is only supported on `EXECUTION`, `LLM`, `SCRIPT`, and `BRIDGE_CALL` steps. Setting it on any other step kind is a plan validation error. ## Error Handling Use `treatAsError` when a successful HTTP response should still fail the step. ```json theme={null} { "treatAsError": "$.status = 'rejected' or $.error != null" } ``` The Activity can then be retried from the failed step after the upstream issue is fixed. Design bridge calls to be idempotent. Activities can be retried, and triggers can fire more than once in at-least-once workflows. External endpoints should tolerate duplicate request IDs or use a stable idempotency key from Activity inputs. ## Security and Binding Service Bridges are project resources. A `BRIDGE_CALL` step can only use bridges that are available to the project running the Activity. This gives the platform a clear boundary: * The Activity Plan defines the workflow. * The project binding defines which bridges are allowed. * The bridge configuration owns authentication and endpoint details. * The step owns request data, routing, and result handling for this business process. Do not put secrets in Activity Plan step config. Use Service Bridge authentication settings and organization secrets instead. ## BRIDGE\_CALL vs SCRIPT with serviceBridge.call() | Requirement | Better fit | | ----------------------------------------------------------------- | ------------- | | One external call should be visible and retryable as its own step | `BRIDGE_CALL` | | Several external calls feed one custom decision | `SCRIPT` | | Request mapping is straightforward | `BRIDGE_CALL` | | The call should mutate documents before deciding the branch | `SCRIPT` | | Operators need to see the external call status directly | `BRIDGE_CALL` | ## Next Steps See how bridge steps fit with CREATE\_TASK, SCRIPT, EXECUTION, and LLM steps. Use `serviceBridge.call()` from scripts, formulas, and event subscriptions. # Activity Plan Steps Source: https://developer.kodexa.ai/guides/activity-plans/steps Reference for all step kinds in a Kodexa Activity Plan, including how steps materialize at runtime and how status, timing, results, and logs are tracked. An Activity Plan is a graph of steps. Each step is materialized when an Activity starts, then tracked independently with status, timing, result, logs, and error details. ## Step Anatomy Every step has the same top-level shape: ```json theme={null} { "slug": "route-invoice", "type": "SCRIPT", "dependsOn": ["extract"], "conditionExpr": "inputs.sourceSystem != 'manual'" } ``` Each step's type-specific fields live at the **top level of the step**, alongside `slug`, `type`, `dependsOn`, etc. There is no `config:` sub-block. | Field | Description | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `slug` | Unique identifier for the step inside the Activity Plan | | `type` | The step type discriminator: `CREATE_TASK`, `SCRIPT`, `BRIDGE_CALL`, `EXECUTION`, `LLM`, `AGENT`, or `APPROVAL` | | `dependsOn` | Steps or step actions that must complete before this step can run | | `conditionExpr` | Optional JSONata predicate that must evaluate truthy for the step to run. In a per-document plan it is evaluated per document and can read a `document` object (`document.status`, `document.statusLabel`, `document.locked`, `document.labels`, `document.path`) | | `setDocumentStatus` | Optional. A document-status slug applied to the document family when the step completes (see [Document Status](#document-status)) | When an Activity starts, Kodexa copies the plan's step graph into Activity-owned step rows. After that, the running Activity uses the materialized steps. Editing the Activity Plan affects future Activities, not Activities already in flight. ## Dependency Patterns Run after another step completes: ```json theme={null} [ { "slug": "extract", "type": "EXECUTION" }, { "slug": "route", "type": "SCRIPT", "dependsOn": ["extract"] } ] ``` Run only after a specific action: ```json theme={null} { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["route:review"] } ``` Join multiple paths: ```json theme={null} { "slug": "finalize", "type": "BRIDGE_CALL", "dependsOn": ["auto-approve", "analyst-review:approved"] } ``` Use action-qualified dependencies when a step can produce more than one outcome. This keeps the graph explicit: the routing decision lives on the upstream step, and the downstream step declares which outcome it depends on. The token after the colon (`route:review`) is the upstream action's **slug** — the stable identity declared on `scriptActions[].slug`, `promptActions[].slug`, `bridgeActions[].slug`, or a CREATE\_TASK template's action slug. Older plans use a `uuid` field on the action with the same semantic value; that is the legacy spelling of `slug` and still resolves, but new authoring should use `slug`. ## Document Status Each document family carries a **status** (a slug such as `pending-review` or `completed`). The `setDocumentStatus` field sets that status when a step completes, and a per-document `conditionExpr` over the `document.*` context lets steps react to it — both declaratively, without writing a SCRIPT step. ### Setting status: `setDocumentStatus` Add `setDocumentStatus` to any step to stamp the document family's status when that step completes. On a per-document step it applies to the families that completed at that step; on a non-per-document step it applies to every document family in the Activity. ```json theme={null} { "slug": "mark-pending-review", "type": "SCRIPT", "dependsOn": ["extract"], "setDocumentStatus": "pending-review", "scriptBody": "return {};" } ``` This is the first-class equivalent of calling `documents.setStatus(familyId, statusSlug)` inside a SCRIPT — prefer the field for fixed, step-completion status changes, and the script call only when the status is conditional or computed. ### Skipping on status To skip a step based on the document's current status, gate it with a `conditionExpr` over the per-document `document.*` context. The step is not taken for matching documents, and the steps that depend on it are skipped too. ```json theme={null} { "slug": "prepare-document", "type": "EXECUTION", "perDocument": true, "conditionExpr": "$not(document.status in [\"pending-review\", \"reviewed\", \"completed\"])", "moduleRef": "kodexa/prepare" } ``` A common pattern is "skip to review": add a per-document SCRIPT **router** at the pipeline root that reads `documents.get(families[0].id).status` and returns one of two actions (e.g. `process` / `skip_to_review`). The processing pipeline depends on `router:process`, so already-processed documents are skipped; a separate root depends on `router:skip_to_review` to route them straight to the review task. (A direct `conditionExpr` on the root plus an inverse-gated second root works too.) `setDocumentStatus` and the `document.*` condition context operate per document, and are only populated in per-document Activity Plans — so a status-based `conditionExpr` has no effect in a plan that is not per-document. ## CREATE\_TASK Use `CREATE_TASK` when the workflow needs human judgment, correction, exception handling, or approval. ```json theme={null} { "slug": "analyst-review", "type": "CREATE_TASK", "dependsOn": ["route:review"], "taskTemplateRef": "invoice-review", "taskStatusSlug": "open", "waitForCompletion": true, "taskData": { "priority": "{{inputs.priority}}", "documentFamilyId": "{{inputs.documentFamilyId}}" } } ``` | Step field | Description | | ------------------- | -------------------------------------------------------------------------- | | `taskTemplateRef` | Task Template used to create the Task | | `taskStatusSlug` | Initial Task status | | `waitForCompletion` | Whether the Activity should pause until the Task reaches a terminal status | | `taskData` | Data copied onto the created Task | If `waitForCompletion` is true, the Activity waits for the reviewer to complete the Task. The Task's status or action can then drive the next step. See [Create Task Steps](/guides/activity-plans/create-task-steps) for the full human-work guide. ## SCRIPT Use `SCRIPT` when the workflow needs custom JavaScript logic. Good uses: * Route based on document content or metadata * Normalize values before review * Assign knowledge features * Call several Service Bridges and make a decision * Prepare inputs for another step ```json theme={null} { "slug": "route", "type": "SCRIPT", "dependsOn": ["extract"], "scriptActions": [ { "slug": "review", "name": "review" }, { "slug": "post", "name": "post" }, { "slug": "reject", "name": "reject" } ], "scriptBody": "return { action: inputs.needsReview ? 'review' : 'post' };" } ``` See [Script Steps](/guides/activity-plans/script-steps) for the full scripting guide. ## BRIDGE\_CALL Use `BRIDGE_CALL` when the workflow should call an external system as a first-class step. Good uses: * Validate extracted data against a system of record * Post approved data to an ERP, CRM, claims, or lending system * Fetch enrichment data needed by the next step * Notify a downstream system when a workflow completes ```json theme={null} { "slug": "post-to-erp", "type": "BRIDGE_CALL", "dependsOn": ["analyst-review:approved"], "serviceBridgeRef": "finance-erp", "endpointName": "post-invoice", "requestBody": { "documentFamilyId": "{{inputs.documentFamilyId}}" }, "treatAsError": "$.status != 'accepted'", "timeoutSeconds": 30 } ``` See [Service Bridge Steps](/guides/activity-plans/service-bridge-steps) for the full bridge-step guide. ## EXECUTION Use `EXECUTION` to run a module or automated processing component. ```json theme={null} { "slug": "extract", "type": "EXECUTION", "moduleRef": "kodexa/invoice-extractor", "options": { "documentFamilyId": "{{inputs.documentFamilyId}}" }, "perDocument": true, "maxParallel": 4 } ``` | Step field | Description | | ------------- | ------------------------------------------- | | `moduleRef` | Module to run | | `options` | Module-specific options | | `perDocument` | Whether to run once per document family | | `maxParallel` | Parallelism limit when running per document | See [Execution Steps](/guides/activity-plans/execution-steps) for the full module execution guide. ## LLM Use `LLM` for bounded prompt execution where the Activity Plan owns the prompt, inputs, and result mapping. ```json theme={null} { "slug": "summarize-exception", "type": "LLM", "dependsOn": ["route:review"], "promptTemplateRef": "invoice-exception-summary", "promptVariables": { "documentFamilyId": "{{inputs.documentFamilyId}}" }, "includeDocument": true } ``` Prefer an `LLM` step for direct prompt execution. Prefer an `AGENT` step when the work requires tool use or multi-step reasoning. See [LLM Steps](/guides/activity-plans/llm-steps) for the full prompt-step guide. ## AGENT Use `AGENT` when a bounded part of the Activity should be delegated to an agent runtime. Typical uses: * Draft a routine Activity Plan or Data Form * Investigate a document exception and propose next steps * Assemble test cases or validation checks * Use tools to inspect project resources before producing a result Keep agent steps constrained. The Activity Plan should still define the process boundary, inputs, expected output, and downstream routing. See [Agent Steps](/guides/activity-plans/agent-steps) for the full agent-step guide. ## APPROVAL Use `APPROVAL` when a workflow must explicitly pause for authorization before continuing. ```json theme={null} { "slug": "manager-approval", "type": "APPROVAL", "dependsOn": ["analyst-review:approved"], "approverRole": "finance-manager", "approvalCriteria": { "amountGreaterThan": 10000 } } ``` See [Approval Steps](/guides/activity-plans/approval-steps) for approval-gate modeling. ## Choosing Between SCRIPT and BRIDGE\_CALL | Need | Prefer | | ---------------------------------------------------------------------- | ------------------------------------- | | One configured external API call with clear input and output | `BRIDGE_CALL` | | Multiple calls, custom branching, or document mutation | `SCRIPT` using `serviceBridge.call()` | | Pure routing or validation logic inside Kodexa | `SCRIPT` | | External system update that should be visible as its own workflow step | `BRIDGE_CALL` | Make the graph explain the business process. Use first-class steps for work operators should monitor directly. Use scripts for logic that is naturally internal to a decision. # Author Metadata with Claude Code Source: https://developer.kodexa.ai/guides/claude-code/authoring-metadata Use Claude Code with the Kodexa Metadata Skills plugin to author, edit, review, and migrate Kodexa platform configuration files in a Git-backed repository. Claude Code is most useful when it works against real files in a metadata repository. The goal is not to generate a disconnected YAML snippet. The goal is to update the same files that `kdx sync` will validate and deploy. ## Start From A Synced Repository If the environment already contains resources, pull them first: ```bash theme={null} kdx sync pull --target my-org --env dev --discover --discover-dir kodexa-resources ``` Then commit the baseline: ```bash theme={null} git add sync-config.yaml manifests kodexa-resources .sync-state git commit -m "Sync Kodexa metadata baseline" ``` Starting from a committed baseline gives Claude Code and reviewers a clean diff. ## Give Claude Code The Right Scope Open Claude Code from the repository root: ```bash theme={null} cd my-kodexa-config claude ``` Start with a scoped instruction: ```text theme={null} We are modeling invoice intake as an Activity-first Kodexa workflow. Please inspect sync-config.yaml, manifests, and kodexa-resources. Then propose the smallest file changes needed to add: - an invoice data definition - a review task template - a review data form - an Activity Plan that extracts, creates the review task, waits for completion, and calls the finance ERP service bridge after approval Do not deploy anything. Do not create secrets. Keep changes reviewable. ``` This gives Claude Code the business goal, the desired Kodexa mental model, and the deployment boundary. ## Ask For A Plan Before Large Edits For non-trivial changes, ask Claude Code to describe the file plan first: ```text theme={null} Before editing files, list the resources you will create or modify, their file paths, and which existing resources they reference. ``` Review the plan for: * resource scope: organization-level versus project-scoped * filenames and slugs * references to existing task templates, data forms, modules, bridges, and knowledge resources * whether a trigger or project binding is needed * whether the manifest needs to include new files ## Useful Prompt Patterns ### Create A Data Definition ```text theme={null} Use the Kodexa data-definition skill to create a data definition for commercial invoices. Include invoice number, invoice date, purchase order number, vendor name, currency, subtotal, tax, total, and repeating line items. Put the file under kodexa-resources/data-definitions/invoice.yaml and add it to the manifest. ``` ### Create A Task And Form ```text theme={null} Create a reusable task template and V2 data form for invoice review. The task is the human review point inside the Activity, not the process itself. Bind the form to the invoice data definition and make the form suitable for field correction and exception review. ``` ### Create An Activity Plan ```text theme={null} Create an Activity Plan named invoice-intake. It should run extraction, create an invoice review task when review is required, wait for completion, branch on approval or rejection, and call the ERP service bridge only after approval. Use current Activity Plan step kinds and avoid legacy planTemplate fields. ``` ### Add Knowledge-Driven Behavior ```text theme={null} Use the knowledge-system skill to add vendor-specific extraction guidance. Create feature and item type YAML where needed, then create a knowledge set that applies an extraction prompt override when the vendor feature matches. ``` ### Review Existing Metadata ```text theme={null} Review the Activity Plan, task template, data form, and project binding files for broken references, legacy fields, missing manifest entries, and deployment risk. Return findings with file paths and exact changes to make. ``` ## Keep References Portable Prefer sync-friendly references over environment-specific UUIDs when the KDX sync resolver supports them. Examples: ```yaml theme={null} activityPlanRef: "activity-plan://${org}/invoice-intake" taskTemplateRef: "${taskTemplate.invoice-review}" dataFormRef: "${dataForm.invoice-review}" ``` The `${org}` contract is `${org}/`: the placeholder, a slash, then the resource slug. `kdx sync push` substitutes it with the destination organization slug in every pushed resource, including references embedded in a module's script. If any string still contains `${org}` after substitution — for example a malformed token such as `${org}-name` — the push stops and reports the resource and the field paths that still carry it, instead of deploying the literal text and letting it fail later at runtime. Portable references make the same repository usable across dev, staging, and production targets. ## Review The Diff After Claude Code edits files: ```bash theme={null} git diff ``` Ask Claude Code to explain the diff in Kodexa terms: ```text theme={null} Explain this diff as a Kodexa deployment review. Focus on new resources, references between resources, manifest changes, and risks before sync. ``` ## Avoid These Mistakes | Mistake | Better approach | | -------------------------------------------------------------- | --------------------------------------------------------------- | | Putting process orchestration inside a Task template | Create an Activity Plan and have it create or wait for Tasks | | Creating one-off project-specific copies of reusable resources | Define organization-level resources and bind them into projects | | Hard-coding environment UUIDs in files | Use slugs and sync reference placeholders where supported | | Writing API keys or vendor credentials into YAML | Use Kodexa secrets or environment-specific secret references | | Letting Claude Code deploy immediately after editing | Validate and dry-run first | | Ignoring `.sync-state/` | Commit sync state so conflict detection works | ## Before You Sync Run these checks before pushing configuration: ```bash theme={null} git diff --check find kodexa-resources -name '*.yaml' -print0 | xargs -0 -n1 kdx validate -f kdx sync push --target my-org --env dev --dry-run ``` If the dry run shows unexpected deletes, unrelated updates, or a target environment you did not intend, stop and inspect the manifest and sync configuration before pushing. # Working with Claude Code Source: https://developer.kodexa.ai/guides/claude-code/index Use Claude Code with Kodexa metadata skills to author configuration on disk, review it in Git, and sync it into Kodexa with the KDX CLI. Kodexa configuration works best when it is treated as code: reviewed in Git, validated before deployment, and promoted through environments with the same discipline as application changes. Claude Code can help with that workflow through the [Kodexa Metadata Skills](https://github.com/kodexa-ai/kodexa-metadata-skills) plugin. The plugin gives Claude Code resource-specific guidance for authoring Kodexa metadata on disk, including Activity Plans, task templates, data definitions, data forms, knowledge resources, project templates, modules, service bridges, prompt templates, and KDX CLI sync workflows. Use this workflow when your Kodexa configuration lives in a Git repository or you want it to. For one-off operations, use the [KDX CLI resource operations](/guides/kdx-cli/resource-operations) directly. ## The Workflow The normal loop is: ```mermaid theme={null} flowchart LR Platform["Kodexa environment"] -->|kdx sync pull| Repo["Git metadata repo"] Repo -->|Claude Code + Kodexa skills| Edit["Edit YAML, JSON, scripts, and modules"] Edit -->|review diff| PR["Pull request"] PR -->|kdx validate + dry run| Check["Validation"] Check -->|kdx sync push or deploy| Platform ``` Claude Code helps you work inside the repository. The KDX CLI is still the tool that talks to Kodexa. ## What Claude Code Should Help With Use the skills for resource authoring, cleanup, and review: | Work | Skill to use | Output | | ---------------------------------- | ------------------------------- | ---------------------------------------------------------------------- | | Model a business process | `activity-plan` | Activity Plan YAML with steps, inputs, dependencies, and actions | | Define human review work | `task-template` and `data-form` | Task template YAML and V2 data form JSON | | Model extracted business data | `data-definition` | Data definition YAML | | Adapt behavior by document context | `knowledge-system` | Feature types, item types, knowledge sets, and project knowledge items | | Connect to external systems | `service-bridge` | Service bridge YAML for API proxy endpoints | | Package custom logic | `module` | Module metadata and implementation layout | | Deploy and validate | `kdx-cli` | Sync, validate, dry-run, push, and deploy commands | For new workflow orchestration, start with **Activity Plans**. A Task is the human work item inside the process; the Activity Plan is the reusable process definition. ## What Stays In Git A typical metadata repository includes: ```text theme={null} my-kodexa-config/ ├── sync-config.yaml ├── manifests/ │ └── finance.yaml ├── kodexa-resources/ │ ├── activity-plans/ │ ├── data-definitions/ │ ├── data-forms/ │ ├── knowledge-sets/ │ ├── modules/ │ ├── project-templates/ │ ├── projects/ │ ├── service-bridges/ │ ├── task-statuses/ │ └── task-templates/ └── .sync-state/ ``` Commit the metadata files, manifests, `sync-config.yaml`, and `.sync-state/`. Do not commit API keys, secrets, generated logs, or local scratch files. ## Human Review Still Matters Claude Code should draft and modify configuration, but a developer or platform owner should still review: * Business meaning: Does the Activity Plan match the real process? * References: Do task templates, forms, data definitions, modules, bridges, and knowledge sets exist in the target organization? * Scope: Are changes limited to the intended project or reusable resource? * Safety: Are secrets referenced by name instead of written into YAML? * Deployment behavior: Does `kdx sync push --dry-run` or `kdx sync deploy --dry-run` show only the expected changes? ## Pages In This Section Install the Claude Code plugin and learn how skills are invoked. Use Claude Code to safely create and edit Kodexa configuration in a Git repository. Pull, validate, dry-run, push, and deploy metadata with the KDX CLI. Go deeper on sync configuration, manifests, conflict detection, and CI/CD. # Install the Kodexa Skills Source: https://developer.kodexa.ai/guides/claude-code/installing-skills Install the Kodexa Metadata Skills plugin in Claude Code and learn how to invoke the correct authoring skill for each Kodexa platform resource type. The Kodexa Metadata Skills plugin is a Claude Code plugin published at [kodexa-ai/kodexa-metadata-skills](https://github.com/kodexa-ai/kodexa-metadata-skills). It adds Kodexa-specific authoring guidance to Claude Code so it can work with platform metadata files instead of guessing schema shape from scratch. ## Prerequisites Before installing the plugin: * Install Claude Code. * Install the [KDX CLI](/guides/kdx-cli/installation). * Configure KDX authentication for at least one Kodexa environment. * Have a Git repository for Kodexa metadata, or plan to create one with `kdx sync pull --discover`. ## Install From The Marketplace Run these commands in your terminal: ```bash theme={null} claude plugin marketplace add kodexa-ai/kodexa-metadata-skills claude plugin install kodexa-metadata-skills ``` After installation, the skills are available in Claude Code sessions automatically. ## Use A Local Checkout For development or testing, load the plugin from a local clone: ```bash theme={null} git clone https://github.com/kodexa-ai/kodexa-metadata-skills.git claude --plugin-dir ./kodexa-metadata-skills ``` Use a local checkout when you are testing a new version of the skills or contributing improvements to the plugin itself. ## Let Claude Code Pick The Skill Most of the time you can describe the work naturally: ```text theme={null} Create an Activity Plan for invoice intake. It should extract invoice fields, create a review task when confidence is low, call the ERP service bridge after approval, and route rejected invoices to an exception status. ``` Claude Code should select the relevant Kodexa skill based on the task. For that example, it should use Activity Plan guidance, and may also draw on task template, data form, service bridge, and KDX CLI guidance depending on the files involved. ## Invoke A Skill Directly When you know the resource type, invoke the skill explicitly: ```text theme={null} /kodexa-metadata-skills:activity-plan /kodexa-metadata-skills:data-definition /kodexa-metadata-skills:data-form /kodexa-metadata-skills:task-template /kodexa-metadata-skills:knowledge-system /kodexa-metadata-skills:service-bridge /kodexa-metadata-skills:module /kodexa-metadata-skills:kdx-cli ``` Direct invocation is useful when you want Claude Code to stay focused on one class of metadata. ## Recommended Activity-First Usage For new business workflows, start from this order: 1. `data-definition`: model the structured data the workflow produces. 2. `task-template`: define the human work item. 3. `data-form`: build the review UI for the task. 4. `activity-plan`: define the automated process, including when it creates or waits for tasks. 5. `knowledge-system`: add context-dependent prompts, rules, examples, or configuration. 6. `project-template` or project bindings: bind reusable resources into the project. 7. `kdx-cli`: validate and sync the configuration. If you are maintaining older configuration, the skills repository may contain guidance for legacy resource shapes. For new orchestration work, use Activity Plans and triggers instead of putting process steps inside a Task template. ## Verify The Plugin Is Available Open Claude Code in your metadata repository and ask: ```text theme={null} List the Kodexa metadata skills you have available and explain when to use each one. ``` Then ask it to inspect the repository layout: ```text theme={null} Review this repository as a Kodexa metadata sync repo. Identify the sync-config, manifest files, resource directories, and the safest next validation command. ``` Claude Code should respond with Kodexa-specific terminology and should not suggest deploying until it has inspected the local files. # Sync Claude Code Changes with KDX Source: https://developer.kodexa.ai/guides/claude-code/syncing-with-kdx Validate, dry-run, push, and deploy Claude Code-authored Kodexa metadata to dev, staging, and production environments using the KDX CLI sync workflow. Claude Code edits files. KDX sync moves those files between Git and Kodexa. This page shows the operational loop for taking Claude Code-authored metadata through validation, review, and deployment. ## Pull Before You Edit Start every session by pulling the latest platform state and Git state: ```bash theme={null} git pull kdx sync pull --target my-org --env dev ``` Then inspect the diff: ```bash theme={null} git status --short git diff ``` If `kdx sync pull` changed files, commit or intentionally review those changes before asking Claude Code to make new edits. A clean baseline makes conflicts easier to reason about. ## Validate Local Files Validate changed resource files before sync: ```bash theme={null} kdx validate -f kodexa-resources/activity-plans/invoice-intake.yaml kdx validate -f kodexa-resources/data-definitions/invoice.yaml kdx validate -f kodexa-resources/data-forms/invoice-review.yaml ``` For a broad pass: ```bash theme={null} find kodexa-resources -name '*.yaml' -print0 | xargs -0 -n1 kdx validate -f ``` `kdx validate` checks YAML against the platform OpenAPI schema without sending the resource to the server. ## Dry-Run The Sync Always preview before pushing: ```bash theme={null} kdx sync push --target my-org --env dev --dry-run ``` Review: * created resources * updated resources * deleted resources * dependency ordering * unresolved references * target organization and environment If the dry run is not clean, ask Claude Code to inspect the output and the relevant files: ```text theme={null} Review this kdx sync dry-run output. Identify which local files caused the errors, which references are unresolved, and what edits are required before deployment. ``` ## Push To A Development Environment Once the dry run is correct: ```bash theme={null} kdx sync push --target my-org --env dev ``` Push also converges deletions inside the regions your files own: removing a key from a task template's `metadata.properties`, or removing a taxon — or a key inside one — from a data definition's `taxons`, updates the server to match the file. Only a region the file actually authors is compared, so omitting `metadata.properties` entirely leaves the server copy alone, while an explicit `metadata: {}` or `properties: {}` clears it. Deletions converge on Kodexa platform (V2) environments; legacy environments keep the previous behavior. Commit the deployed change and sync state: ```bash theme={null} git add sync-config.yaml manifests kodexa-resources .sync-state git commit -m "Add invoice intake Activity configuration" ``` Commit `.sync-state/` after pull and push operations. It records server change sequences used for conflict detection. ## Use Deploy For GitOps Promotion Use `sync deploy` when your repository has branch or tag mappings in `sync-config.yaml`: ```bash theme={null} kdx sync deploy --branch main --dry-run kdx sync deploy --branch main ``` For release-driven promotion: ```bash theme={null} kdx sync deploy --tag v2026.05.03 --dry-run kdx sync deploy --tag v2026.05.03 ``` For GitHub Actions, prefer the official [kdx-sync-action](https://github.com/kodexa-ai/kdx-sync-action). See [CI/CD Integration](/guides/kdx-cli/sync/ci-cd). ## Handling Conflicts If KDX reports stale server state: ```bash theme={null} kdx sync pull --target my-org --env dev git diff ``` Then decide whether to keep the server change, keep your local change, or merge both. Do not use `--force` until you understand which server-side change would be overwritten. Ask Claude Code to help with the merge, not to blindly force it: ```text theme={null} kdx sync push detected a conflict. Review the pulled server changes and my local changes. Propose a merged version that preserves the intended Activity behavior and avoids overwriting unrelated platform edits. ``` ## A Safe Claude Code + KDX Loop ```bash theme={null} # 1. Refresh local state git pull kdx sync pull --target my-org --env dev # 2. Let Claude Code edit files claude # 3. Review and validate git diff find kodexa-resources -name '*.yaml' -print0 | xargs -0 -n1 kdx validate -f kdx sync push --target my-org --env dev --dry-run # 4. Push only after the dry run is expected kdx sync push --target my-org --env dev # 5. Commit the files and sync state git add sync-config.yaml manifests kodexa-resources .sync-state git commit -m "Update Kodexa metadata" ``` ## Next Steps Understand the sync repository model. Learn command flags and operational behavior. See how `.sync-state/` protects against stale writes. Validate YAML against the platform schema before deployment. # Building Data Classes Source: https://developer.kodexa.ai/guides/data-definitions/building-data-classes Build Python data classes from Kodexa Data Definitions to apply business rules, validation, and transformations to extracted document data programmatically. One of the key aspects of working with Data Definitions and extraction models is the ability to build Python data classes. These classes let you take the structured output bound to a document and apply business rules to it. This is a powerful way in which you can work with the Kodexa LLM Data Labeling models. If you look at an LLM Data Labeling model in a pipeline (in Manage Project / Data Flows) you will see on the options you can set the external data. This will store the data captured by the LLM in a special format that includes lineage to the document. Building Data Classes 1 You can then use this structure if you want to apply rules or transform it. To do this we can use the Kodexa Python SDK. Install it with `pip install kodexa`. If you go to your project and find the Data Definition used by your LLM model, Developer Tools gives you a copy action for the JSON representation of that definition. Building Data Classes 2 Then paste the content into a file. In this example, we will call it `data-definition.json`. Building Data Classes 3 If you have the Kodexa Python SDK installed, you can open a terminal and run the command: ```bash theme={null} kodexa dataclasses data-definition.json ``` This will create a `dataclasses.py` file that will contain the structure of the objects. Building Data Classes 4 Always rename `dataclasses.py` since it might clash with the system package. You can now use the dataclasses to read the data that was set in your model in external data, for example: ```python theme={null} def infer(document): bank_statement = document.get_external_data()['bank_statement'][0] ``` If you wish to change a value, always update the normalized\_text not the value. # Data Types Source: https://developer.kodexa.ai/guides/data-definitions/data-types Reference for data types used by Data Definitions, including how data elements, data stores, and extraction workflows combine to produce structured output. Data types are used by the Extraction Engine when a workflow applies one or more Data Definitions and writes the resulting structured data into a data store. The final document is passed to the Extraction Engine which then builds the data objects and data attributes linked back to the labeled document. Data types impact only the Data Attribute, and a data attribute is designed to hold multiple representations of the piece of data. Currently, we have the following data types: | Type | Description | | ------------- | -------------------------------------------------------------------------------------------- | | String | The most basic data type that can hold any type of information as a string of characters | | Date | Supports capturing a date without a time element, the date is that defined to a local to UTC | | Date/Time | Supports capturing a date with a time element, the date is that defined to a local to UTC | | Phone Number | Tries to normalize a phone number | | Email Address | Tries to convert the labeled content to a valid email address | | Selection | Tries to match the value labeled to a list of available options | | Number | Tries to convert the labeled content to a number | | Currency | Tries to convert the labeled content to a valid currency (decimal) | | Boolean | Tries to convert the labeled content to a boolean value | ## Understanding Normalization (Coalescing) When we label text in a document, it is always a "string". This just means we are capturing text and not trying to standardize (or normalize) it in any way at all. However, most systems that will use the data from Kodexa will want to know that the data is a specific type. They would want things to be numbers or dates, etc. This process is handled when we try to set the data type on a data attribute. The Extraction Engine will take the text that is labeled in the document and try to coalesce the data into a specific form - for example it might take "1.0" as a string and turn it into a number. This is important since it means the system using the data from Kodexa knows the data is "valid" for that "Data Type". In that case, if the data type is a number, it will not allow "abc" for that data attribute. ## Algorithms for Coalescing In the following table we will break down how we coalesce the data from labeled data to the data type. | Data Type | Description | Algorithm | | ----------------- | -------------------------------------------------------------------------------------------------- | ------------------- | | Date or Date/Time | The extraction engine will use an NLP framework to try and convert the labeled text to a date/time | NLP conversion | | Boolean | If the text (in lowercase) is "true" then it is true, else it is false | Simple string match | | Currency | Attempt to convert to a decimal | Decimal conversion | | Email | Extract the email address | Regex validation | | Number | Parse as a decimal number | Numeric parsing | | Phone Number | Parse the phone number using Google's LibPhoneNumber | Library parsing | | Selectable Option | Nothing right now | N/A | ## How is Typed Data Stored? A data attribute has the ability to store multiple representations of a piece of extracted data, depending on the data type defined in the data structure one or more of the properties of the Data Attribute will be updated. | Property | Description | Applies to | | ------------ | --------------------------------------------------------------------- | -------------------------- | | value | This is the raw value that was captured from the label | All | | stringValue | This is the raw value as a string | Selectable Options, String | | dateValue | This is the date/time in ISO format (YYYY-MM-DD and YYYY-MM-DDThh:mm) | Date, Date/Time | | booleanValue | This is the boolean value | Boolean | | decimalValue | This is the number or currency value | Currency, Number | ## Content Source When we are extracting data from a document label we are capturing the text that is labeled. This is the "raw" value that we are capturing. This is the value that is stored in the `value` property of the data attribute. However, we also need to understand where that raw value comes from in the document. This is handled by the content source property on the data element. We support the following types of content source: | Content Source | Description | | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Value or All Content | This means that we will look at the label, and we will see if the label has been given a value. If so, we will use this. However, if the label does not have a specified value then we will take all the text that the label has been applied to and use that as the value | | Value Only | This means that we will look at the label, and we will see if the label has been given a value and use that, if the label did not specify a value we return null | | All Content | This means that we will look at the label, and we will take all the text that the label has been applied to and use that as the value | | Formula | This allows the user to define a formula that calculates the value from other fields | | Metadata | This allows the user to choose a metadata object that will be used as the value | | Review | This creates a review field that can be populated by human reviewers | # Event-Based Scripting Source: https://developer.kodexa.ai/guides/data-definitions/event-subscriptions Attach reactive JavaScript to Kodexa data definitions so data changes derive values, call service bridges, manage exceptions, and update related fields. Event-based scripting belongs to **Data Definitions**. It is how the data model reacts when values change. A Data Form renders the review experience for a Task. A Data Definition owns the structure and behavior of the business data being reviewed. Event subscriptions let that data model run JavaScript when an event occurs, such as an attribute changing on a data object. Use event scripts when a change to one field should update other fields, call an external system, create an exception, emit another event, or apply business logic that is too procedural for a formula. ## When To Use Event Scripts Use an event subscription when the data definition needs behavior, not just structure: * Derive several sibling attributes after a reviewer edits one value * Normalize data before formulas, validation, and selection rules run * Call a Service Bridge to enrich a row from an external system * Create or close data exceptions based on business logic * Copy or move data objects when a modeled relationship changes * Emit a named event that another subscription can handle Use a formula when one output value can be computed declaratively from other values. Use event scripting when the change has side effects or needs multiple reads and writes. ## Where Event Scripts Run Event subscriptions are attached to **group data elements** in a Data Definition. In configuration, those group elements are stored as `taxons`. At runtime, the script executes for one data object instance of that group. ```mermaid theme={null} flowchart LR Edit["Reviewer edits attribute"] --> Event["changed:dataAttribute:field_name"] Event --> Sub["Group event subscription"] Sub --> Script["Subscription script"] Script --> Writes["Update attributes or objects"] Writes --> Recalc["Formula, selection, validation, formatting"] ``` The script runs inside the recalculation flow. Writes made by the script are visible to downstream formula recalculation, selection option evaluation, validation, selection validation, and conditional formatting. ## Configuration Attach `eventSubscriptions` to a group data element: ```yaml theme={null} - name: shipment label: Shipment group: true eventSubscriptions: - name: compute-accessorial-total on: "changed:dataAttribute:(quantity|unit_price)" script: | if (!currentObject) return; var quantity = currentObject.getFirstAttributeValue("quantity") || 0; var unitPrice = currentObject.getFirstAttributeValue("unit_price") || 0; currentObject.setAttribute("line_total", quantity * unitPrice); children: - name: quantity label: Quantity taxonType: NUMBER - name: unit_price label: Unit Price taxonType: NUMBER - name: line_total label: Line Total taxonType: NUMBER ``` ### Attribute Change Shorthand For simple attribute-change subscriptions, `dependsOn` is a compact way to list the attributes that should trigger the script: ```yaml theme={null} eventSubscriptions: - name: compute-accessorial-total on: "changed:dataAttribute" dependsOn: - quantity - unit_price script: | currentObject.setAttribute("line_total", 0); ``` Kodexa expands that into the equivalent event pattern: ```yaml theme={null} on: "changed:dataAttribute:(quantity|unit_price)" ``` Use the explicit `on` form when you need to match multiple event types or a custom event pattern. ### Fields Unique name for the subscription within the owning group data element. The runtime uses this name for loop-control state and diagnostics. Regex pattern matched against the full event string. Kodexa anchors the pattern internally, so `changed:dataAttribute:amount` matches only that event string. Convenience field for `changed:dataAttribute`. When present with `on: "changed:dataAttribute"`, validation converts it to an `on` regex and clears `dependsOn`. JavaScript source executed for the subscription run. When `true`, the subscription remains in the Data Definition but is skipped at runtime. ## Supported Event Strings The subscription matcher uses full event strings. | Event string | How it is used | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `changed:dataAttribute:field_name` | Fired when a data attribute changes, including the first time it receives a value. This is the main automatic data-definition event. | | `created:dataObject` | Fired by the document runtime when a data object is created. | | `loaded:dataObject` | Fired after initial document load in the browser runtime. | | `formLoaded:form_ref` | Matches form-load events emitted by the host. | | `trigger:name` | Matches named events emitted by script or host code. | | `focus:dataAttribute:field_name` | Matchable event for focus-aware hosts. | | `blur:dataAttribute:field_name` | Matchable event for blur-aware hosts. | `changed:dataAttribute` fires when an attribute first receives a value, not only on later edits. Setting a value on a previously blank field is treated as a change, so a reviewer's first pick into an empty field runs the matching subscription just as a subsequent edit would. For common attribute-change subscriptions, use: ```yaml theme={null} on: "changed:dataAttribute:(shipper_city|shipper_state|postal_code)" ``` For a named event: ```yaml theme={null} on: "trigger:recalculate-rating" ``` For multiple event types: ```yaml theme={null} on: "(loaded:dataObject|trigger:recalculate-rating)" ``` ## Runtime Rules | Rule | Detail | | ---------------- | --------------------------------------------------------------------------------------- | | Runtime | JavaScript VM | | Timeout | 2 seconds per subscription execution | | VM lifetime | Fresh VM per subscription execution | | Async | No `async` / `await`; calls are synchronous | | Document scope | Use the prebound `document`; `loadDocument()` is not supported for subscription scripts | | Attribute writes | Use `currentObject.setAttribute(name, value)` | | Type safety | Typed setters validate against the attribute type | ## Execution Order When a data attribute changes, Kodexa processes the cascade in this order: ```text theme={null} 1. Mark the attribute dirty 2. Emit the attribute-changed event 3. Run matching event subscriptions 4. Recalculate dependent formulas 5. Re-evaluate affected selection option formulas 6. Evaluate affected validations 7. Validate selections 8. Evaluate affected conditional formats ``` This ordering matters. If an event script writes `tax_rate`, formulas and validations that depend on `tax_rate` see the value written by the script. ## Script Globals Every event subscription receives a small set of prebound globals. | Global | Available | Use | | --------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `currentObject` | All subscription scripts | The group data object instance that owns the event. May be `null`; guard before use. | | `document` | All subscription scripts | The active document containing `currentObject`. | | `event` | All subscription scripts | Event payload with the event string, changed attribute, old value, new value, and data object id. | | `serviceBridge` | Subscription runtimes with platform transport | Calls configured Service Bridges through the platform proxy. | | `taxon` | Browser subscription runtime | Looks up selection option labels from loaded Data Definitions. | | `bridge` | Browser subscription runtime | UI/event hooks such as notifications and explicit event firing. Guard before use for cross-runtime scripts. | | `log` | All subscription scripts | Structured logging with `debug`, `info`, `warn`, and `error`. | | `console` | All subscription scripts | Browser-style logging aliases. | ### `event` The `event` object describes why the script ran. | Property | Type | Description | | --------------- | ------ | ------------------------------------------------------------------------ | | `type` | string | Full event string, such as `changed:dataAttribute:amount` | | `eventKind` | string | Event prefix, such as `changed:dataAttribute`, when supplied by the host | | `attributeTag` | string | Changed attribute name for attribute events | | `attributePath` | string | Full attribute path when available | | `dataObjectId` | number | ID of the data object where the event occurred | | `oldValue` | any | Typed value before the change | | `newValue` | any | Typed value after the change | | `formName` | string | Form reference for `formLoaded:*` events | | `triggerName` | string | Trigger name for `trigger:*` events | Example: ```javascript theme={null} if (event.attributeTag === "amount") { log.info("Amount changed from", event.oldValue, "to", event.newValue); } ``` ## `currentObject` `currentObject` is the data object instance for the group data element where the event matched. ### Reading Values | Method | Returns | Use | | ------------------------------ | ---------------------------------------------------- | -------------------------------------------------- | | `getPath()` | string | Data object definition path | | `getIDString()` | string | Data object ID as a string | | `getParent()` | DataObject or `null` | Parent data object | | `getChildren()` | DataObject\[] | Child objects already attached to this object | | `getChildrenByPath(path)` | DataObject\[] | Direct children matching a path | | `getAttributeByName(name)` | DataAttribute or `null` | First matching attribute wrapper | | `getAttributesByName(name)` | DataAttribute\[] | All matching attribute wrappers | | `getFirstAttributeValue(name)` | string, number, boolean, string date, or `undefined` | First typed attribute value | | `getAttributeValues(name)` | string\[] | All matching values stringified | | `hasTaxonomy()` | boolean | Whether the object has a Data Definition reference | ```javascript theme={null} if (!currentObject) return; var city = currentObject.getFirstAttributeValue("shipper_city"); var state = currentObject.getFirstAttributeValue("shipper_state"); var path = currentObject.getPath(); log.debug("Changed object", path, city, state); ``` ### Writing Values Use `setAttribute(name, value)` for normal writes. It finds or creates the attribute, resolves the target type from the Data Definition when available, writes the typed value, persists the change, and notifies the recalculation system. ```javascript theme={null} var quantity = currentObject.getFirstAttributeValue("quantity") || 0; var price = currentObject.getFirstAttributeValue("unit_price") || 0; currentObject.setAttribute("line_total", quantity * price); ``` Supported value types are strings, numbers, booleans, and RFC 3339 date strings for date attributes. Type mismatches fail the script. For example, writing `"abc"` to a numeric data element raises a JavaScript error from the typed setter. ### Payloads For Service Bridges `payload(mapping)` builds a plain JavaScript object from attributes on `currentObject`. ```javascript theme={null} var body = currentObject.payload({ shipperCode: "shipper_code", shipperCity: "shipper_city", shipperState: "shipper_state" }); // { shipperCode: "ACME", shipperCity: "Chicago", shipperState: "IL" } ``` Missing attributes are returned as empty strings. This keeps Service Bridge request bodies stable. ### Creating Child Data Objects Use `getOrCreateChild(path, opts?)` or `addChild(opts)` when the event should create modeled child data. ```javascript theme={null} var accessorial = currentObject.getOrCreateChild("shipment/accessorials", { sourceOrdering: "0" }); accessorial.setAttribute("code", "LIFTGATE"); accessorial.setAttribute("amount", 75); ``` Use `copyAttributesFrom(sourceObj, mappings, ownerUri?)` when moving known attributes between objects: ```javascript theme={null} target.copyAttributesFrom(source, [ { src: "invoice_number" }, { src: "currency", dst: "amount_currency" } ]); ``` ## `DataAttribute` `getAttributeByName()` and `getAttributesByName()` return attribute wrappers for lower-level operations. | Method | Use | | ------------------------ | --------------------------------------------- | | `getTag()` | Attribute tag/name | | `getPath()` | Full attribute path | | `getOwnerUri()` | Audit owner URI | | `getValue()` | Raw value | | `getStringValue()` | Typed string value | | `getDecimalValue()` | Typed numeric value | | `getBooleanValue()` | Typed boolean value | | `getDateValue()` | Typed date value as RFC 3339 | | `getConfidence()` | Confidence value, or `0` when absent | | `getDataFeatures()` | Attribute data features | | `setStringValue(value)` | Persist typed string value | | `setDecimalValue(value)` | Persist typed numeric value | | `setBooleanValue(value)` | Persist typed boolean value | | `setDateValue(value)` | Persist typed date value from RFC 3339 string | | `setConfidence(value)` | Persist confidence | | `addFeature(key, value)` | Add a data feature | Prefer `currentObject.setAttribute(...)` for common writes. Use `DataAttribute` methods only when you need direct access to a specific attribute instance. ## `document` The `document` global is the active KDDB document. ### Data Object Access | Method | Use | | ------------------------------------ | -------------------------------------- | | `getAllDataObjects()` | Return all data objects | | `findDataObjectsByPath(path)` | Return all objects for a path | | `findFirstDataObjectByPath(path)` | Return the first object for a path | | `getOrCreate(path, opts?)` | Find or create a top-level data object | | `createDataObject(opts)` | Create a data object | | `deleteDataObject(id)` | Delete a data object | | `copyDataObject(opts)` | Copy a data object | | `moveDataObject(opts)` | Move a data object | | `batchCopyDataObjects(opts[])` | Copy several objects atomically | | `batchMoveDataObjects(opts[])` | Move several objects atomically | | `updateDataObject(obj)` | Persist in-memory object changes | | `getDataObjectsByParentId(parentId)` | Return direct children for a parent ID | ### Document Metadata And External Data | Method | Use | | -------------------------------------------------------- | ------------------------------------- | | `getUUID()` / `getVersion()` | Document identity | | `getLabels()` / `addLabel(label)` / `removeLabel(label)` | Document labels | | `getMetadata()` / `setMetadata(key, value)` | Document metadata map | | `getDocumentMetadataMap()` | Copy of document metadata | | `getDocumentMetadataValue(key)` | Single document metadata value | | `setDocumentMetadataValue(key, value)` | Set or delete document metadata value | | `getExternalData(key)` | External data entry | | `setExternalData(key, data)` | Write external data | | `getExternalDataKeys()` | External data keys | ### Content, Search, And Serialization | Method | Use | | ------------------------------------ | -------------------------------------------- | | `getRootNode()` / `getContentNode()` | Access the document content tree | | `select(selector, variables)` | Select content nodes | | `selectFirst(selector, variables)` | Select the first matching node | | `getAllTags()` | Unique document tags | | `searchContent(pattern, opts)` | Regex content search | | `searchLines(query, opts)` | Line search | | `getSearchableLines()` | Searchable line records | | `toJSON()` | Serialize document metadata and data objects | | `dataObjectsToJSON()` | Serialize data objects only | ### Exceptions And Validations | Method | Use | | ------------------------------------------- | ---------------------------- | | `createDataException(opts)` | Create a data exception | | `updateDataException(opts)` | Update a data exception | | `closeDataException(id, comment)` | Close a data exception | | `reopenDataException(id)` | Reopen a data exception | | `getAllDataExceptions()` | All data exceptions | | `getOpenDataExceptions()` | Open data exceptions | | `getDataExceptionById(id)` | One data exception | | `getDataExceptionsByDataObjectId(id)` | Exceptions for a data object | | `addValidation(opts)` | Add validation metadata | | `getValidations()` / `setValidations(opts)` | Read or replace validations | | `validateDocument()` | Run document validation | Example: ```javascript theme={null} var amount = currentObject.getFirstAttributeValue("amount"); if (!amount || amount <= 0) { document.createDataException({ message: "Amount must be greater than zero", exceptionType: "validation", severity: "error", dataObjectId: event.dataObjectId }); } ``` ## `serviceBridge` `serviceBridge.call(bridgeRef, endpointName, body?)` calls an external system through a configured Service Bridge. ```javascript theme={null} var result = serviceBridge.call( "acme-finance/vendor-lookup", "match-vendor", currentObject.payload({ vendorName: "vendor_name", postalCode: "postal_code" }) ); if (result && result.vendorId) { currentObject.setAttribute("vendor_id", result.vendorId); currentObject.setAttribute("vendor_match_status", "matched"); } ``` Bridge refs may be fully qualified as `orgSlug/bridgeSlug`. Some runtimes can supply a default organization slug, but fully qualifying the bridge keeps scripts portable. The bridge response is parsed as JSON. If the platform proxy returns a `result` envelope, the runtime returns the `result` value directly. If the proxy returns an `error` envelope, the script fails with that error. ## `taxon` In the browser subscription runtime, `taxon.optionLabel(taxonName, value)` returns the display label for a selection option value. ```javascript theme={null} var movementType = currentObject.getFirstAttributeValue("movement_type"); var label = taxon.optionLabel("movement_type", movementType); if (label) { currentObject.setAttribute("movement_type_label", label); } ``` Guard this helper if the same script may run in a runtime that has not loaded Data Definition bindings: ```javascript theme={null} if (typeof taxon !== "undefined") { var label = taxon.optionLabel("movement_type", value); } ``` ## `bridge` The browser subscription runtime exposes `bridge` for UI notifications and explicit event emission. | Method | Use | | ------------------------------------------------ | ------------------------------------------------------ | | `bridge.notify(title, message, severity?)` | Show a user notification. Severity defaults to `info`. | | `bridge.events.fire(eventString, dataObjectId?)` | Emit another event into the recalculation cascade. | ```javascript theme={null} if (typeof bridge !== "undefined") { bridge.notify("Vendor matched", "Vendor ID was populated.", "success"); } ``` Fire a follow-up event when another subscription should respond: ```javascript theme={null} if (typeof bridge !== "undefined") { bridge.events.fire("trigger:recalculate-rating", event.dataObjectId); } ``` Only emit events deliberately. Emitted events participate in the same loop-control rules as automatic events. ## Logging Use structured logging during development and troubleshooting: ```javascript theme={null} log.debug("event", event.type, "object", currentObject.getPath()); log.info("vendor response", JSON.stringify(result)); log.warn("missing postal code"); log.error("unexpected response shape", JSON.stringify(result)); ``` `console.log`, `console.warn`, and `console.error` are also available. ## Loop Control The recalculation service protects event scripts from runaway cascades: | Guard | Behavior | | --------------------- | ------------------------------------------------------------------------------------- | | Active frame guard | A subscription cannot re-enter itself while already running for the same data object. | | Cascade deduplication | A subscription runs at most once per data object in one user-driven cascade. | | Maximum depth | Nested event execution is capped at 8 levels. | | No-op suppression | Writes that do not change typed values are suppressed. | Design scripts to be idempotent. A script may run more than once over the life of a document, and retries should not duplicate data or re-open resolved work. ## Examples ### Normalize And Derive Sibling Values ```yaml theme={null} eventSubscriptions: - name: normalize-postal-code on: "changed:dataAttribute:postal_code" script: | if (!currentObject) return; var postalCode = currentObject.getFirstAttributeValue("postal_code"); if (!postalCode) return; currentObject.setAttribute("postal_code", String(postalCode).trim().toUpperCase()); currentObject.setAttribute("postal_code_prefix", String(postalCode).slice(0, 5)); ``` ### Service Bridge Enrichment ```yaml theme={null} eventSubscriptions: - name: lookup-vendor on: "changed:dataAttribute:(vendor_name|postal_code)" script: | if (!currentObject) return; var vendorName = currentObject.getFirstAttributeValue("vendor_name"); if (!vendorName) return; var result = serviceBridge.call( "acme-finance/vendor-lookup", "match-vendor", currentObject.payload({ vendorName: "vendor_name", postalCode: "postal_code" }) ); if (result && result.vendorId) { currentObject.setAttribute("vendor_id", result.vendorId); currentObject.setAttribute("vendor_match_status", "matched"); } else { currentObject.setAttribute("vendor_match_status", "review"); } ``` ### Create A Data Exception ```yaml theme={null} eventSubscriptions: - name: validate-amount on: "changed:dataAttribute:amount" script: | if (!currentObject) return; var amount = currentObject.getFirstAttributeValue("amount"); if (amount === undefined || amount === null || amount <= 0) { document.createDataException({ message: "Amount must be greater than zero", exceptionType: "validation", severity: "error", dataObjectId: event.dataObjectId }); } ``` ### Emit A Follow-Up Event ```yaml theme={null} eventSubscriptions: - name: vendor-change on: "changed:dataAttribute:vendor_id" script: | if (typeof bridge !== "undefined") { bridge.events.fire("trigger:recalculate-risk", event.dataObjectId); } - name: recalculate-risk on: "trigger:recalculate-risk" script: | if (!currentObject) return; var amount = currentObject.getFirstAttributeValue("amount") || 0; var vendorStatus = currentObject.getFirstAttributeValue("vendor_status"); currentObject.setAttribute( "risk_level", amount > 10000 || vendorStatus === "new" ? "review" : "standard" ); ``` ## Constraints Event subscriptions can only be declared on group data elements. Scripts run synchronously with a 2-second timeout. Keep external calls bounded and avoid large document scans. Subscription scripts are scoped to the active document. Use the prebound `document` global; do not call `loadDocument()`. ## Best Practices Name the exact business event the script responds to. Prefer specific `on` patterns such as `changed:dataAttribute:(quantity|unit_price)` over broad matches. Read and write values on `currentObject` when possible. Use `document` only when the script really needs broader document context. Write the same result for the same inputs. Avoid appending duplicate child objects or recreating exceptions without checking existing state. `getFirstAttributeValue` returns typed values. Preserve those types when writing with `setAttribute`. Wrap `bridge` and the `taxon` helper usage in `typeof ... !== "undefined"` checks when a script may also run outside the browser runtime. Use `log.debug` while building and keep production logs focused on decisions, external calls, and unexpected states. ## Related Define the data model that event scripts operate on Configure data elements, groups, validations, selection options, and event subscriptions Compute dynamic selection options from data and bridge calls Configure external API calls used by event scripts # Contract Data Definition Example Source: https://developer.kodexa.ai/guides/data-definitions/examples/contract Complete example data definition for extracting contract metadata, parties, financial terms, dates, obligations, and legal clauses from legal documents. ## Overview This example demonstrates a comprehensive data definition for extracting metadata and key terms from legal contracts. It covers contract identification, parties, financial terms, dates, obligations, and legal clauses. ## Use Cases * **Contract Management Systems**: Extract key contract terms for centralized tracking * **Legal Review Workflows**: Identify critical dates, obligations, and risk factors * **Compliance Monitoring**: Track renewal dates, termination clauses, and regulatory requirements * **Financial Analysis**: Extract payment terms, contract values, and financial obligations ## Complete Data Definition Structure ### YAML Configuration ```yaml theme={null} slug: contract-metadata name: Contract Metadata Extraction description: Extract key metadata, parties, terms, and obligations from legal contracts taxonomyType: CONTENT enabled: true taxons: # ========================================== # Document Metadata # ========================================== - name: document_metadata label: Document Metadata group: true children: - name: filename label: Original Filename taxonType: STRING valuePath: METADATA metadataValue: FILENAME description: Source document filename - name: processing_date label: Processing Date taxonType: DATE_TIME valuePath: METADATA metadataValue: PROCESSING_DATE description: When this contract was processed - name: document_hash label: Document Hash taxonType: STRING valuePath: METADATA metadataValue: CONTENT_HASH description: SHA-256 hash for document integrity verification # ========================================== # Contract Identification # ========================================== - name: contract_header label: Contract Header group: true children: - name: contract_type label: Contract Type taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The type of contract. Look for headers like "Service Agreement", "Master Services Agreement", "Purchase Agreement", "NDA", etc. selectionOptions: - label: "Service Agreement" description: "General service delivery contract" - label: "Master Services Agreement (MSA)" description: "Framework agreement for multiple projects" - label: "Statement of Work (SOW)" description: "Project-specific work definition" - label: "Purchase Agreement" description: "Contract for purchase of goods/services" - label: "Non-Disclosure Agreement (NDA)" description: "Confidentiality agreement" - label: "License Agreement" description: "Software or IP licensing contract" - label: "Employment Agreement" description: "Employee contract" - label: "Consulting Agreement" description: "Independent contractor agreement" - label: "Partnership Agreement" description: "Business partnership terms" - label: "Lease Agreement" description: "Property or equipment lease" - label: "Other" description: "Other contract type" - name: contract_number label: Contract Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The unique contract identifier. Look for "Contract No.", "Agreement No.", "Reference No.", or similar identifiers in the header or footer. - name: contract_title label: Contract Title taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The full title of the contract as it appears in the document header. - name: version label: Version taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The version number of the contract if specified (e.g., "v1.0", "Rev 2"). - name: replaces_contract label: Replaces Contract Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | If this contract replaces or supersedes a previous agreement, extract the reference to the previous contract number. # ========================================== # Parties # ========================================== - name: parties label: Contract Parties group: true description: All parties involved in the contract children: - name: party_a label: Party A (Primary) group: true children: - name: legal_name label: Legal Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The full legal name of the first party. Look for text near "hereinafter referred to as" or in signature blocks. - name: role label: Party Role taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The role of this party (e.g., Provider, Customer, Buyer, Seller)" selectionOptions: - label: "Provider" - label: "Customer" - label: "Buyer" - label: "Seller" - label: "Licensor" - label: "Licensee" - label: "Employer" - label: "Employee" - label: "Contractor" - label: "Partner" - label: "Lessor" - label: "Lessee" - name: address label: Business Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The business address of Party A" - name: jurisdiction label: Jurisdiction taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The state or country of incorporation/registration" - name: signatory_name label: Signatory Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Name of the authorized signatory for Party A" - name: signatory_title label: Signatory Title taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Title of the signatory (e.g., CEO, Director)" - name: party_b label: Party B (Secondary) group: true children: - name: legal_name label: Legal Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The full legal name of the second party" - name: role label: Party Role taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The role of this party" selectionOptions: - label: "Provider" - label: "Customer" - label: "Buyer" - label: "Seller" - label: "Licensor" - label: "Licensee" - label: "Employer" - label: "Employee" - label: "Contractor" - label: "Partner" - label: "Lessor" - label: "Lessee" - name: address label: Business Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The business address of Party B" - name: jurisdiction label: Jurisdiction taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The state or country of incorporation/registration" - name: signatory_name label: Signatory Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Name of the authorized signatory for Party B" - name: signatory_title label: Signatory Title taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Title of the signatory" - name: additional_parties label: Additional Parties group: true description: Other parties involved (guarantors, witnesses, etc.) children: - name: party_name label: Party Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: "Names of any additional parties mentioned in the contract" - name: party_role label: Party Role taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: "Roles of additional parties (e.g., Guarantor, Witness)" # ========================================== # Financial Terms # ========================================== - name: financial_terms label: Financial Terms group: true children: - name: contract_value label: Total Contract Value taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The total monetary value of the contract. Look for "Total Contract Value", "Maximum Liability", or similar financial commitments. - name: currency label: Currency taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The currency for all financial terms (USD, EUR, GBP, etc.)" - name: payment_structure label: Payment Structure taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT selectionOptions: - label: "Fixed Price" - label: "Time & Materials" - label: "Milestone-Based" - label: "Retainer" - label: "Subscription" - label: "Revenue Share" - label: "Cost Plus" - name: payment_terms label: Payment Terms taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | Payment timing and conditions (e.g., "Net 30", "50% upfront, 50% on completion", "Quarterly in advance"). - name: late_payment_penalty label: Late Payment Penalty taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Penalties for late payment (e.g., "1.5% per month", "10% penalty fee"). - name: deposit_amount label: Deposit Amount taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Any upfront deposit or retainer amount required" # ========================================== # Dates and Terms # ========================================== - name: dates_and_terms label: Dates and Terms group: true children: - name: execution_date label: Execution Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The date the contract was signed or executed. Look near signature blocks or "Dated as of" language. - name: effective_date label: Effective Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The date the contract becomes effective, which may differ from execution date. - name: expiration_date label: Expiration Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The date the contract expires if it has a fixed term. May be labeled "Term End Date" or "Termination Date". - name: initial_term label: Initial Term taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The initial contract duration (e.g., "12 months", "3 years", "Perpetual"). - name: renewal_terms label: Renewal Terms taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | How the contract renews (e.g., "Auto-renews annually unless terminated with 90 days notice", "Mutual agreement required for renewal"). - name: notice_period label: Notice Period for Termination taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | How much advance notice is required to terminate the contract (e.g., "30 days", "90 days written notice"). # ========================================== # Key Obligations and Deliverables # ========================================== - name: obligations label: Key Obligations group: true children: - name: scope_of_work label: Scope of Work Summary taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | A high-level summary of what services or deliverables are included. Look for "Scope of Work", "Services", or "Deliverables" sections. - name: deliverables label: Key Deliverables taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: | Specific deliverables or milestones mentioned in the contract. - name: service_levels label: Service Level Requirements taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Any SLA or performance requirements (e.g., "99.9% uptime", "Response within 4 hours"). - name: party_a_obligations label: Party A Key Obligations taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: "Key obligations or responsibilities of Party A" - name: party_b_obligations label: Party B Key Obligations taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: "Key obligations or responsibilities of Party B" # ========================================== # Legal and Compliance # ========================================== - name: legal_clauses label: Legal Clauses group: true children: - name: governing_law label: Governing Law taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The jurisdiction whose laws govern the contract (e.g., "State of California", "Laws of England and Wales"). - name: dispute_resolution label: Dispute Resolution Method taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT selectionOptions: - label: "Litigation" - label: "Arbitration" - label: "Mediation" - label: "Mediation then Arbitration" - label: "Not Specified" - name: arbitration_venue label: Arbitration Venue taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Location or rules for arbitration if applicable" - name: confidentiality_clause label: Confidentiality Clause Present taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Does the contract include confidentiality obligations?" - name: confidentiality_duration label: Confidentiality Duration taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | How long confidentiality obligations last (e.g., "3 years after termination", "Perpetual"). - name: liability_cap label: Liability Cap taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Maximum liability amount or percentage (e.g., "Total fees paid in prior 12 months", "$100,000"). - name: indemnification label: Indemnification Present taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Does the contract include indemnification clauses?" - name: insurance_requirements label: Insurance Requirements taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true multiValue: true semanticDefinition: | Required insurance coverage (e.g., "General Liability $2M", "Professional Indemnity $5M"). - name: intellectual_property label: IP Ownership Terms taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Summary of intellectual property ownership and rights provisions. # ========================================== # Termination Conditions # ========================================== - name: termination label: Termination Conditions group: true children: - name: termination_for_cause label: Termination for Cause Allowed taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Can the contract be terminated for cause (breach)?" - name: termination_for_convenience label: Termination for Convenience Allowed taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Can either party terminate without cause?" - name: termination_fee label: Early Termination Fee taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Any penalties or fees for early termination (e.g., "3 months fees", "Remaining contract value"). - name: post_termination_obligations label: Post-Termination Obligations taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true multiValue: true semanticDefinition: | Obligations that survive contract termination (e.g., "Return of materials", "Continued confidentiality"). # ========================================== # Risk Assessment # ========================================== - name: risk_assessment label: Risk Assessment group: true description: Computed risk indicators for contract review children: - name: risk_level label: Overall Risk Level taxonType: SELECTION valuePath: FORMULA semanticDefinition: | IF(contract_value > 1000000, "High", IF(contract_value > 100000, "Medium", "Low")) selectionOptions: - label: "Low" - label: "Medium" - label: "High" - label: "Critical" - name: requires_legal_review label: Requires Legal Review taxonType: BOOLEAN valuePath: FORMULA semanticDefinition: | contract_value > 100000 OR initial_term CONTAINS "year" OR liability_cap IS_EMPTY() - name: renewal_alert_date label: Renewal Alert Date taxonType: DATE valuePath: FORMULA semanticDefinition: | DATE_ADD(expiration_date, PARSE_DAYS(notice_period) * -1) description: "Date to send renewal reminder based on notice period" # ========================================== # Review and Approval # ========================================== - name: review_workflow label: Review Workflow group: true children: - name: reviewed_by label: Reviewed By taxonType: STRING valuePath: REVIEW userEditable: true description: "Person who reviewed this contract" - name: review_date label: Review Date taxonType: DATE valuePath: REVIEW userEditable: true - name: approval_status label: Approval Status taxonType: SELECTION valuePath: REVIEW userEditable: true selectionOptions: - label: "Pending Review" - label: "Approved" - label: "Approved with Conditions" - label: "Rejected" - label: "Requires Negotiation" - name: review_notes label: Review Notes taxonType: STRING valuePath: REVIEW userEditable: true description: "Notes from legal or business review" # ========================================== # Validation Rules # ========================================== validationRules: # Required Fields - name: "Contract type required" ruleFormula: "NOT_EMPTY(contract_header.contract_type)" messageFormula: '"Contract type must be specified"' overridable: false - name: "Parties required" ruleFormula: | NOT_EMPTY(parties.party_a.legal_name) AND NOT_EMPTY(parties.party_b.legal_name) messageFormula: '"Both contracting parties must be identified"' overridable: false - name: "Effective date required" ruleFormula: "NOT_EMPTY(dates_and_terms.effective_date)" messageFormula: '"Effective date is required"' overridable: false # Date Logic - name: "Execution before effective" ruleFormula: | IS_EMPTY(dates_and_terms.execution_date) OR IS_EMPTY(dates_and_terms.effective_date) OR dates_and_terms.execution_date <= dates_and_terms.effective_date messageFormula: '"Execution date should not be after effective date"' overridable: true - name: "Effective before expiration" ruleFormula: | IS_EMPTY(dates_and_terms.expiration_date) OR dates_and_terms.effective_date < dates_and_terms.expiration_date messageFormula: '"Effective date must be before expiration date"' overridable: false - name: "Future expiration" ruleFormula: | IS_EMPTY(dates_and_terms.expiration_date) OR dates_and_terms.expiration_date >= TODAY() messageFormula: '"Contract appears to be expired"' overridable: true # Financial Validation - name: "Contract value reasonable" ruleFormula: | IS_EMPTY(financial_terms.contract_value) OR financial_terms.contract_value > 0 messageFormula: '"Contract value must be positive"' overridable: false - name: "Currency specified" ruleFormula: | IS_EMPTY(financial_terms.contract_value) OR NOT_EMPTY(financial_terms.currency) messageFormula: '"Currency must be specified when contract value is present"' overridable: false - name: "Payment terms specified" ruleFormula: | IS_EMPTY(financial_terms.contract_value) OR NOT_EMPTY(financial_terms.payment_terms) messageFormula: '"Payment terms should be specified for contracts with financial value"' overridable: true - name: "Deposit not exceeding value" ruleFormula: | IS_EMPTY(financial_terms.deposit_amount) OR IS_EMPTY(financial_terms.contract_value) OR financial_terms.deposit_amount <= financial_terms.contract_value messageFormula: '"Deposit amount cannot exceed total contract value"' overridable: true # Legal Validation - name: "Governing law specified" ruleFormula: "NOT_EMPTY(legal_clauses.governing_law)" messageFormula: '"Governing law should be specified"' overridable: true - name: "High-value contracts need liability cap" ruleFormula: | IS_EMPTY(financial_terms.contract_value) OR financial_terms.contract_value <= 100000 OR NOT_EMPTY(legal_clauses.liability_cap) messageFormula: '"Contracts over $100,000 should include liability cap"' overridable: true - name: "Confidentiality duration for NDA" ruleFormula: | contract_header.contract_type != "Non-Disclosure Agreement (NDA)" OR NOT_EMPTY(legal_clauses.confidentiality_duration) messageFormula: '"NDAs should specify confidentiality duration"' overridable: true # Termination Validation - name: "Notice period for renewable contracts" ruleFormula: | IS_EMPTY(dates_and_terms.renewal_terms) OR NOT_EMPTY(dates_and_terms.notice_period) messageFormula: '"Contracts with renewal terms should specify notice period"' overridable: true # Party Validation - name: "Signatory information complete" ruleFormula: | (NOT_EMPTY(parties.party_a.signatory_name) AND NOT_EMPTY(parties.party_a.signatory_title)) AND (NOT_EMPTY(parties.party_b.signatory_name) AND NOT_EMPTY(parties.party_b.signatory_title)) messageFormula: '"Both parties should have complete signatory information"' overridable: true - name: "Party roles are different" ruleFormula: | IS_EMPTY(parties.party_a.role) OR IS_EMPTY(parties.party_b.role) OR parties.party_a.role != parties.party_b.role messageFormula: '"Parties should have different roles (e.g., Provider/Customer)"' overridable: true # Risk-based Validation - name: "High-value requires legal review" ruleFormula: | IS_EMPTY(financial_terms.contract_value) OR financial_terms.contract_value <= 100000 OR review_workflow.approval_status IN ["Approved", "Approved with Conditions"] messageFormula: '"Contracts over $100,000 require legal approval"' overridable: false - name: "Multi-year requires executive approval" ruleFormula: | NOT(dates_and_terms.initial_term CONTAINS "year") OR review_workflow.approval_status IN ["Approved", "Approved with Conditions"] messageFormula: '"Multi-year contracts require executive approval"' overridable: true # ========================================== # Conditional Formatting # ========================================== conditionalFormats: - name: "High-value contract" formula: "financial_terms.contract_value > 500000" backgroundColor: "#FEF3C7" textColor: "#92400E" fontWeight: "bold" icon: "dollar-sign" - name: "Expired contract" formula: | NOT_EMPTY(dates_and_terms.expiration_date) AND dates_and_terms.expiration_date < TODAY() backgroundColor: "#FEE2E2" textColor: "#991B1B" fontWeight: "bold" icon: "exclamation-triangle" - name: "Expiring soon (within 90 days)" formula: | NOT_EMPTY(dates_and_terms.expiration_date) AND dates_and_terms.expiration_date >= TODAY() AND dates_and_terms.expiration_date <= DATE_ADD(TODAY(), 90) backgroundColor: "#FED7AA" textColor: "#9A3412" icon: "clock" - name: "Pending approval" formula: | review_workflow.approval_status == "Pending Review" OR review_workflow.approval_status == "Requires Negotiation" backgroundColor: "#DBEAFE" textColor: "#1E40AF" icon: "hourglass" - name: "No liability cap (high risk)" formula: | financial_terms.contract_value > 100000 AND IS_EMPTY(legal_clauses.liability_cap) backgroundColor: "#FEE2E2" textColor: "#991B1B" fontWeight: "bold" icon: "warning" - name: "Auto-renewal contract" formula: | dates_and_terms.renewal_terms CONTAINS "auto" OR dates_and_terms.renewal_terms CONTAINS "automatic" backgroundColor: "#E0E7FF" textColor: "#3730A3" icon: "refresh" ``` ## Key Features ### Comprehensive Party Information * **Multiple Party Support**: Captures Party A, Party B, and additional parties * **Role Classification**: Categorizes parties as Provider, Customer, Buyer, Seller, etc. * **Signatory Details**: Tracks who signed and their authority * **Jurisdictional Information**: Important for legal compliance ### Financial Term Tracking * **Contract Value**: Total monetary commitment * **Payment Structure**: Fixed price, T\&M, milestone-based, etc. * **Payment Terms**: Net 30, upfront, quarterly, etc. * **Penalties and Deposits**: Late fees, advance payments ### Date Management * **Multiple Date Types**: Execution, effective, expiration dates * **Term Management**: Initial term, renewal terms, notice periods * **Automated Alerts**: Calculate renewal reminder dates based on notice period ### Legal Clause Extraction * **Governing Law**: Jurisdiction for legal disputes * **Dispute Resolution**: Litigation, arbitration, or mediation * **Confidentiality**: NDA provisions and duration * **Liability Caps**: Maximum exposure limits * **IP Ownership**: Intellectual property rights ### Risk Assessment * **Automated Risk Scoring**: Based on contract value and terms * **Review Requirements**: Flag contracts needing legal review * **Compliance Checks**: Insurance, indemnification, liability caps ### Workflow Integration * **Review Status**: Track approval workflow * **Reviewer Information**: Who reviewed and when * **Notes and Conditions**: Capture approval conditions or concerns ## Validation Strategy ### Critical Validations (Non-overridable) ```yaml theme={null} - Contract type must be specified - Both parties must be identified - Effective date required - Effective before expiration - Contract value must be positive - High-value contracts require legal approval ``` ### Quality Checks (Overridable) ```yaml theme={null} - Execution date should be before or equal to effective date - Payment terms should be specified - Governing law should be specified - Notice period for renewable contracts - Party roles should be different ``` ### Business Rules ```yaml theme={null} - Contracts > $100k should have liability caps - NDAs should specify confidentiality duration - Multi-year contracts require executive approval ``` ## Conditional Formatting Provides visual indicators for: * **High-value contracts** (> \$500k) - Gold highlight * **Expired contracts** - Red alert * **Expiring soon** (\< 90 days) - Orange warning * **Pending approval** - Blue information * **Missing liability cap** (high-value) - Red warning * **Auto-renewal contracts** - Purple indicator ## Usage Tips ### For Legal Teams 1. Focus on `legal_clauses` group for risk assessment 2. Use `risk_assessment.requires_legal_review` to prioritize review queue 3. Track `termination` conditions for exit strategy planning 4. Monitor `renewal_alert_date` for proactive contract management ### For Finance Teams 1. Extract `financial_terms` for budgeting and forecasting 2. Use `contract_value` and `payment_structure` for cash flow planning 3. Track `payment_terms` and `late_payment_penalty` for AR management ### For Procurement Teams 1. Monitor `parties` information for vendor management 2. Track `obligations.scope_of_work` for deliverable oversight 3. Use `service_levels` for SLA monitoring 4. Review `insurance_requirements` for compliance ### For Contract Managers 1. Use `dates_and_terms` for renewal calendar 2. Monitor `expiration_date` and `notice_period` for timely action 3. Track `approval_status` for workflow management 4. Review `post_termination_obligations` when ending contracts ## Best Practices 1. **Always Extract Key Dates**: Effective date and expiration date are critical for contract lifecycle management 2. **Capture Both Parties Fully**: Legal names, roles, and signatory information are essential for enforceability 3. **Document Financial Terms Clearly**: Include currency, payment structure, and terms to avoid disputes 4. **Track Legal Clauses**: Governing law, dispute resolution, and liability caps are critical risk factors 5. **Use Risk Assessment Fields**: Automated risk scoring helps prioritize review and approval workflows 6. **Enable Workflow Tracking**: Capture review status, approver, and notes for audit trail 7. **Monitor Renewal Dates**: Use computed `renewal_alert_date` to avoid unwanted auto-renewals 8. **Validate Party Roles**: Ensure parties have different roles (not both "Provider") 9. **Check Liability Caps on High-Value Contracts**: Important risk mitigation for large commitments 10. **Track Post-Termination Obligations**: These survive contract end and must be managed ## Integration Examples ### CRM Integration ```yaml theme={null} # Export party information to CRM externalName: account_name → parties.party_b.legal_name externalName: account_type → parties.party_b.role externalName: contract_value → financial_terms.contract_value ``` ### Contract Management System ```yaml theme={null} # Sync to contract repository externalName: contractNumber → contract_header.contract_number externalName: expiryDate → dates_and_terms.expiration_date externalName: renewalDate → risk_assessment.renewal_alert_date externalName: status → review_workflow.approval_status ``` ### Financial System ```yaml theme={null} # Export to accounting/ERP externalName: totalValue → financial_terms.contract_value externalName: paymentTerms → financial_terms.payment_terms externalName: currency → financial_terms.currency ``` ## Related Examples * [Invoice Data Definition](/guides/data-definitions/examples/invoice) - Financial document extraction * [Form Data Definition](/guides/data-definitions/examples/form) - Structured form processing * [Purchase Order Data Definition](/guides/data-definitions/examples/purchase-order) - Procurement document extraction # Form Data Definition Example Source: https://developer.kodexa.ai/guides/data-definitions/examples/form Example Kodexa data definition for processing structured forms, applications, surveys, and questionnaires with diverse field types and conditional logic. ## Overview This example demonstrates a comprehensive data definition for extracting data from structured forms such as applications, surveys, registrations, and questionnaires. It handles diverse field types, conditional logic, and workflow tracking. ## Use Cases * **Application Processing**: Job applications, loan applications, enrollment forms * **Survey Analysis**: Customer satisfaction, feedback forms, research questionnaires * **Registration Systems**: Event registration, membership signup, account creation * **Compliance Forms**: Consent forms, declarations, regulatory submissions * **Assessment Forms**: Evaluations, inspections, audits ## Complete Data Definition Structure ### YAML Configuration ```yaml theme={null} slug: form-data name: Form Data Extraction description: Extract structured data from forms, applications, and surveys taxonomyType: CONTENT enabled: true taxons: # ========================================== # Form Metadata # ========================================== - name: form_metadata label: Form Metadata group: true children: - name: form_type label: Form Type taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The type of form. Look for headers, titles, or form numbers that indicate the purpose (e.g., "Employment Application", "Customer Feedback Survey"). selectionOptions: - label: "Job Application" - label: "Loan Application" - label: "Enrollment Form" - label: "Registration Form" - label: "Survey" - label: "Feedback Form" - label: "Consent Form" - label: "Questionnaire" - label: "Inspection Form" - label: "Evaluation Form" - label: "Other" - name: form_number label: Form Number/ID taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The unique identifier for this form submission (e.g., "APP-2024-001234", "Form #5678"). May be in header or footer. - name: form_version label: Form Version taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The version of the form template (e.g., "v2.1", "Rev 3", "03/2024"). - name: submission_date label: Submission Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The date the form was submitted or received. Look for "Date Submitted", "Received Date", or date stamps. - name: filename label: Source Filename taxonType: STRING valuePath: METADATA metadataValue: FILENAME - name: processing_date label: Processing Date taxonType: DATE_TIME valuePath: METADATA metadataValue: PROCESSING_DATE # ========================================== # Applicant/Respondent Information # ========================================== - name: respondent label: Respondent Information group: true description: Information about the person completing the form children: - name: personal_info label: Personal Information group: true children: - name: full_name label: Full Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The full legal name of the person completing the form. May be split into first name, middle name, last name fields. - name: first_name label: First Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "First name if separated from full name" - name: middle_name label: Middle Name/Initial taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Middle name or initial if present" - name: last_name label: Last Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Last name/surname if separated" - name: date_of_birth label: Date of Birth taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Applicant's date of birth if requested on form" - name: age label: Age taxonType: NUMBER valuePath: FORMULA nullable: true semanticDefinition: "YEAR_DIFF(TODAY(), date_of_birth)" description: "Calculated age from date of birth" - name: gender label: Gender taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT nullable: true selectionOptions: - label: "Male" - label: "Female" - label: "Non-binary" - label: "Prefer not to say" - label: "Other" - name: ssn_or_id label: SSN/ID Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Social Security Number, National ID, or other government ID number if requested on form. Handle as sensitive data. - name: contact_info label: Contact Information group: true children: - name: email label: Email Address taxonType: EMAIL_ADDRESS valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | Primary email address. Look for fields labeled "Email", "E-mail", or "Email Address". - name: phone_primary label: Primary Phone taxonType: PHONE_NUMBER valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | Primary phone number. May be labeled "Phone", "Mobile", "Cell Phone", or "Primary Contact Number". - name: phone_alternate label: Alternate Phone taxonType: PHONE_NUMBER valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Alternate or secondary phone number if provided" - name: address label: Mailing Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Complete mailing address. May be combined or split across multiple fields (street, city, state, zip). - name: street_address label: Street Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Street address line 1" - name: address_line_2 label: Address Line 2 taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Apartment, suite, or unit number" - name: city label: City taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: state_province label: State/Province taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: postal_code label: Postal Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "ZIP code, postal code, or postcode" - name: country label: Country taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: emergency_contact label: Emergency Contact group: true nullable: true children: - name: name label: Contact Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Emergency contact person's name" - name: relationship label: Relationship taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Relationship to applicant (e.g., Spouse, Parent, Friend)" - name: phone label: Contact Phone taxonType: PHONE_NUMBER valuePath: VALUE_OR_ALL_CONTENT nullable: true # ========================================== # Form Questions/Fields # ========================================== - name: form_responses label: Form Responses group: true description: Answers to form questions children: # Text Questions - name: text_responses label: Text Responses group: true nullable: true children: - name: question_label label: Question taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: | The text of each question that expects a free-text response. Extract the question label exactly as it appears. - name: answer label: Answer taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: | The applicant's written response to each text question. # Multiple Choice Questions - name: multiple_choice label: Multiple Choice Responses group: true nullable: true children: - name: question_label label: Question taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: "Multiple choice question text" - name: selected_option label: Selected Option taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: | The option selected by the applicant. Look for checkmarks, highlighted options, or marked choices. # Yes/No Questions - name: yes_no_questions label: Yes/No Questions group: true nullable: true children: - name: question_label label: Question taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true - name: answer label: Answer taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: | Extract true for Yes/checked/agree, false for No/unchecked/disagree. # Numeric Responses - name: numeric_responses label: Numeric Responses group: true nullable: true children: - name: question_label label: Question taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true - name: value label: Numeric Value taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: "Extract numeric values (counts, ratings, quantities)" # Rating Scale Questions - name: rating_responses label: Rating Scale Responses group: true nullable: true children: - name: question_label label: Question taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true - name: rating_value label: Rating taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT multiValue: true semanticDefinition: | Extract the numeric rating (e.g., 1-5, 1-10). Look for selected stars, circled numbers, or marked scale positions. - name: scale_range label: Scale Range taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT multiValue: true nullable: true semanticDefinition: | The range of the rating scale (e.g., "1-5", "1-10", "Poor to Excellent") # ========================================== # Specific Use Case Fields # ========================================== # For Employment Applications - name: employment_info label: Employment Information group: true nullable: true description: For job/employment application forms children: - name: position_applied label: Position Applied For taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Job title or position the applicant is applying for" - name: desired_salary label: Desired Salary taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Expected or desired salary/compensation" - name: available_start_date label: Available Start Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Earliest date the applicant can start" - name: employment_type label: Employment Type Sought taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT nullable: true selectionOptions: - label: "Full-time" - label: "Part-time" - label: "Contract" - label: "Internship" - label: "Temporary" - name: resume_attached label: Resume Attached taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Is a resume/CV attached or referenced?" - name: references_provided label: References Provided taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Did the applicant provide professional references?" # For Loan/Financial Applications - name: financial_info label: Financial Information group: true nullable: true description: For loan/credit application forms children: - name: loan_amount_requested label: Loan Amount Requested taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "The amount of money being requested" - name: loan_purpose label: Loan Purpose taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Purpose of the loan (e.g., Home purchase, Auto, Education)" - name: annual_income label: Annual Income taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Applicant's annual gross income" - name: employment_status label: Employment Status taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT nullable: true selectionOptions: - label: "Employed Full-time" - label: "Employed Part-time" - label: "Self-employed" - label: "Unemployed" - label: "Retired" - label: "Student" - name: employer_name label: Employer Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: years_employed label: Years with Current Employer taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT nullable: true # For Event/Program Registration - name: registration_info label: Registration Information group: true nullable: true description: For event or program registration forms children: - name: event_name label: Event/Program Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Name of the event or program being registered for" - name: registration_type label: Registration Type taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT nullable: true selectionOptions: - label: "Individual" - label: "Group" - label: "Family" - label: "Student" - label: "Professional" - name: number_of_attendees label: Number of Attendees taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Total number of people being registered" - name: dietary_restrictions label: Dietary Restrictions taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true multiValue: true semanticDefinition: | Any dietary restrictions, allergies, or special meal requirements - name: accessibility_needs label: Accessibility Needs taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Special accessibility or accommodation requirements" # ========================================== # Agreements and Signatures # ========================================== - name: agreements label: Agreements and Signatures group: true children: - name: terms_accepted label: Terms and Conditions Accepted taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | Did the applicant check or sign the box agreeing to terms and conditions? Look for checkboxes near legal text or signature areas. - name: terms_text label: Terms Text Reference taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The actual terms text or a reference to where terms can be found (e.g., "See attached", "Available at www.example.com/terms") - name: consent_given label: Consent for Data Use taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Did the applicant consent to use of their data (marketing, communication, etc.)? - name: signature_present label: Signature Present taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | Is there a handwritten or electronic signature on the form? - name: signature_date label: Signature Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The date the form was signed. Look near signature areas for dates. - name: witness_signature label: Witness Signature Present taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Is there a witness signature if required?" - name: witness_name label: Witness Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Name of witness if signature was witnessed" - name: notarized label: Notarized taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Was the form notarized? Look for notary stamps/seals" # ========================================== # Processing and Status # ========================================== - name: processing_status label: Processing Status group: true description: Administrative fields for workflow tracking children: - name: status label: Processing Status taxonType: SELECTION valuePath: REVIEW userEditable: true selectionOptions: - label: "New" - label: "Under Review" - label: "Pending Additional Information" - label: "Approved" - label: "Rejected" - label: "On Hold" - label: "Completed" - name: assigned_to label: Assigned To taxonType: STRING valuePath: REVIEW userEditable: true nullable: true description: "Person or team responsible for reviewing this form" - name: priority label: Priority taxonType: SELECTION valuePath: REVIEW userEditable: true selectionOptions: - label: "Low" - label: "Normal" - label: "High" - label: "Urgent" - name: review_notes label: Review Notes taxonType: STRING valuePath: REVIEW userEditable: true nullable: true description: "Internal notes from reviewers" - name: follow_up_required label: Follow-up Required taxonType: BOOLEAN valuePath: REVIEW userEditable: true description: "Does this submission require follow-up action?" - name: follow_up_date label: Follow-up Date taxonType: DATE valuePath: REVIEW userEditable: true nullable: true description: "When to follow up with applicant" - name: completeness_score label: Completeness Score taxonType: NUMBER valuePath: FORMULA description: "Percentage of required fields completed" semanticDefinition: | Calculate percentage of non-empty required fields to measure form completeness # ========================================== # Validation Rules # ========================================== validationRules: # Required Fields - name: "Form type required" ruleFormula: "NOT_EMPTY(form_metadata.form_type)" messageFormula: '"Form type must be specified"' overridable: false - name: "Submission date required" ruleFormula: "NOT_EMPTY(form_metadata.submission_date)" messageFormula: '"Submission date is required"' overridable: false - name: "Respondent name required" ruleFormula: | NOT_EMPTY(respondent.personal_info.full_name) OR (NOT_EMPTY(respondent.personal_info.first_name) AND NOT_EMPTY(respondent.personal_info.last_name)) messageFormula: '"Respondent name is required (full name or first + last)"' overridable: false - name: "Contact information required" ruleFormula: | NOT_EMPTY(respondent.contact_info.email) OR NOT_EMPTY(respondent.contact_info.phone_primary) messageFormula: '"At least one contact method (email or phone) is required"' overridable: false - name: "Signature required" ruleFormula: "agreements.signature_present == true" messageFormula: '"Form must be signed"' overridable: false - name: "Terms must be accepted" ruleFormula: "agreements.terms_accepted == true" messageFormula: '"Terms and conditions must be accepted"' overridable: false # Date Logic - name: "Submission date not in future" ruleFormula: "form_metadata.submission_date <= TODAY()" messageFormula: '"Submission date cannot be in the future"' overridable: true - name: "Signature date matches or before submission" ruleFormula: | IS_EMPTY(agreements.signature_date) OR agreements.signature_date <= form_metadata.submission_date messageFormula: '"Signature date should not be after submission date"' overridable: true - name: "Birth date reasonable" ruleFormula: | IS_EMPTY(respondent.personal_info.date_of_birth) OR (respondent.personal_info.date_of_birth < TODAY() AND YEAR_DIFF(TODAY(), respondent.personal_info.date_of_birth) < 120) messageFormula: '"Date of birth appears invalid"' overridable: true - name: "Applicant of legal age" ruleFormula: | IS_EMPTY(respondent.personal_info.date_of_birth) OR YEAR_DIFF(TODAY(), respondent.personal_info.date_of_birth) >= 18 messageFormula: '"Applicant must be 18 years or older"' overridable: true # Contact Validation - name: "Email format validation" ruleFormula: | IS_EMPTY(respondent.contact_info.email) OR REGEX_MATCH(respondent.contact_info.email, "^[^@]+@[^@]+\\.[^@]+$") messageFormula: '"Email address format appears invalid"' overridable: true - name: "Phone format validation" ruleFormula: | IS_EMPTY(respondent.contact_info.phone_primary) OR LENGTH(DIGITS_ONLY(respondent.contact_info.phone_primary)) >= 10 messageFormula: '"Phone number should contain at least 10 digits"' overridable: true # Conditional Requirements - name: "Emergency contact required for minors" conditional: true conditionalFormula: | NOT_EMPTY(respondent.personal_info.date_of_birth) AND YEAR_DIFF(TODAY(), respondent.personal_info.date_of_birth) < 18 ruleFormula: | NOT_EMPTY(respondent.emergency_contact.name) AND NOT_EMPTY(respondent.emergency_contact.phone) messageFormula: '"Emergency contact required for applicants under 18"' overridable: false - name: "Employer info required if employed" conditional: true conditionalFormula: | financial_info.employment_status IN ["Employed Full-time", "Employed Part-time"] ruleFormula: "NOT_EMPTY(financial_info.employer_name)" messageFormula: '"Employer name required for employed applicants"' overridable: true - name: "Resume required for job applications" conditional: true conditionalFormula: 'form_metadata.form_type == "Job Application"' ruleFormula: "employment_info.resume_attached == true" messageFormula: '"Resume/CV must be attached for job applications"' overridable: true - name: "Witness required for notarized forms" conditional: true conditionalFormula: "agreements.notarized == true" ruleFormula: | agreements.witness_signature == true AND NOT_EMPTY(agreements.witness_name) messageFormula: '"Witness signature required for notarized forms"' overridable: false # Data Quality - name: "Postal code format (US)" ruleFormula: | IS_EMPTY(respondent.contact_info.postal_code) OR respondent.contact_info.country != "United States" OR REGEX_MATCH(respondent.contact_info.postal_code, "^\\d{5}(-\\d{4})?$") messageFormula: '"US postal code should be 5 digits or ZIP+4 format"' overridable: true - name: "Rating values within scale" ruleFormula: | ALL_VALUES(form_responses.rating_responses.rating_value) <= 10 AND ALL_VALUES(form_responses.rating_responses.rating_value) >= 1 messageFormula: '"Rating values should be between 1 and 10"' overridable: true # Business Rules - name: "Loan amount reasonable" conditional: true conditionalFormula: 'form_metadata.form_type == "Loan Application"' ruleFormula: | financial_info.loan_amount_requested > 0 AND financial_info.loan_amount_requested <= 1000000 messageFormula: '"Loan amount should be between $1 and $1,000,000"' overridable: true - name: "Income verification for large loans" conditional: true conditionalFormula: | form_metadata.form_type == "Loan Application" AND financial_info.loan_amount_requested > 50000 ruleFormula: "NOT_EMPTY(financial_info.annual_income)" messageFormula: '"Annual income required for loans over $50,000"' overridable: false # ========================================== # Conditional Formatting # ========================================== conditionalFormats: - name: "Incomplete form" formula: "processing_status.completeness_score < 80" backgroundColor: "#FEE2E2" textColor: "#991B1B" icon: "exclamation-circle" - name: "Urgent priority" formula: 'processing_status.priority == "Urgent"' backgroundColor: "#FEF3C7" textColor: "#92400E" fontWeight: "bold" icon: "exclamation-triangle" - name: "Follow-up overdue" formula: | processing_status.follow_up_required == true AND NOT_EMPTY(processing_status.follow_up_date) AND processing_status.follow_up_date < TODAY() backgroundColor: "#FED7AA" textColor: "#9A3412" icon: "clock" - name: "Missing signature" formula: "agreements.signature_present == false" backgroundColor: "#FEE2E2" textColor: "#991B1B" icon: "pen" - name: "Minor applicant" formula: | NOT_EMPTY(respondent.personal_info.date_of_birth) AND YEAR_DIFF(TODAY(), respondent.personal_info.date_of_birth) < 18 backgroundColor: "#DBEAFE" textColor: "#1E40AF" icon: "user" - name: "High-value loan application" formula: | form_metadata.form_type == "Loan Application" AND financial_info.loan_amount_requested > 100000 backgroundColor: "#FEF3C7" textColor: "#92400E" fontWeight: "bold" icon: "dollar-sign" - name: "Approved" formula: 'processing_status.status == "Approved"' backgroundColor: "#D1FAE5" textColor: "#065F46" icon: "check-circle" - name: "Rejected" formula: 'processing_status.status == "Rejected"' backgroundColor: "#FEE2E2" textColor: "#991B1B" icon: "times-circle" ``` ## Key Features ### Universal Form Structure * **Flexible Question Types**: Text, multiple choice, yes/no, numeric, rating scales * **Multi-Value Support**: Handle repeated questions or multi-select responses * **Conditional Fields**: Different field sets for different form types ### Comprehensive Contact Information * **Multiple Contact Methods**: Email, phone (primary and alternate), full address * **Structured Address**: Separate fields for street, city, state, postal code * **Emergency Contacts**: Capture backup contact information when needed ### Specialized Form Types **Employment Applications**: * Position applied for, desired salary, availability * Resume/references tracking * Employment type preferences **Loan Applications**: * Loan amount, purpose, financial details * Employment and income verification * Business rule validation for loan limits **Event Registration**: * Event details, attendee count * Dietary restrictions and accessibility needs * Group vs. individual registration ### Signature and Agreement Tracking * **Legal Acceptance**: Terms and conditions, consent checkboxes * **Signature Verification**: Presence, date, witness information * **Notarization**: Track notary stamps and requirements ### Workflow Management * **Status Tracking**: New, under review, approved, rejected, etc. * **Assignment**: Route to appropriate reviewers * **Priority Management**: Flag urgent submissions * **Follow-up Tracking**: Schedule and track required actions * **Completeness Scoring**: Automated calculation of form completion ## Validation Strategy ### Critical Validations (Non-overridable) ```yaml theme={null} - Form type must be specified - Respondent name required - At least one contact method required - Signature required - Terms must be accepted - Emergency contact for minors - Witness for notarized forms - Income verification for large loans ``` ### Quality Checks (Overridable) ```yaml theme={null} - Submission date not in future - Birth date reasonable - Email format validation - Phone format validation - Postal code format (US) - Rating values within scale ``` ### Conditional Business Rules ```yaml theme={null} - Employer info required if employed - Resume required for job applications - Loan amount reasonable limits - Income required for loans > $50k ``` ## Conditional Formatting Provides visual indicators for: * **Incomplete forms** (\< 80% complete) - Red alert * **Urgent priority** - Gold highlight * **Overdue follow-ups** - Orange warning * **Missing signatures** - Red alert * **Minor applicants** - Blue information * **High-value loans** (> \$100k) - Gold highlight * **Approved/Rejected** - Green/Red status ## Usage Tips ### For HR/Recruitment Teams 1. Use `employment_info` fields for job application processing 2. Track `resume_attached` and `references_provided` for completeness 3. Monitor `processing_status` for workflow management 4. Use `priority` field to handle urgent applications ### For Financial Services 1. Extract `financial_info` for loan processing 2. Validate income against loan amount for risk assessment 3. Use `employment_status` and `years_employed` for stability checks 4. Track `processing_status` for approval workflow ### For Event Organizers 1. Use `registration_info` for attendee management 2. Track `dietary_restrictions` and `accessibility_needs` for planning 3. Monitor `number_of_attendees` for capacity planning 4. Use `contact_info` for event communications ### For Compliance Officers 1. Verify `agreements.terms_accepted` for legal compliance 2. Check `signature_present` and `signature_date` for validity 3. Validate `notarized` and `witness_signature` when required 4. Track `consent_given` for data protection compliance ## Best Practices 1. **Capture All Question Types**: Use appropriate `taxonType` values for different question formats 2. **Handle Multi-Value Fields**: Enable `multiValue: true` for questions that may have multiple answers 3. **Validate Contact Information**: Use EMAIL\_ADDRESS and PHONE\_NUMBER types for automatic validation 4. **Track Signatures Carefully**: Signature presence and date are critical for legal validity 5. **Use Conditional Validation**: Apply rules based on form type or applicant characteristics 6. **Calculate Completeness**: Use formula fields to measure how complete submissions are 7. **Enable Workflow Tracking**: Administrative fields help route and prioritize form processing 8. **Respect Age Requirements**: Validate applicant age and require emergency contacts for minors 9. **Handle Sensitive Data**: Mark SSN/ID fields as sensitive and follow data protection rules 10. **Support Multiple Form Types**: Use conditional fields to handle different form scenarios ## Integration Examples ### CRM/Applicant Tracking ```yaml theme={null} externalName: leadSource → form_metadata.form_type externalName: fullName → respondent.personal_info.full_name externalName: email → respondent.contact_info.email externalName: status → processing_status.status ``` ### Marketing Automation ```yaml theme={null} externalName: contactEmail → respondent.contact_info.email externalName: optIn → agreements.consent_given externalName: submissionDate → form_metadata.submission_date ``` ### Loan Origination System ```yaml theme={null} externalName: loanAmount → financial_info.loan_amount_requested externalName: annualIncome → financial_info.annual_income externalName: employmentStatus → financial_info.employment_status externalName: applicationStatus → processing_status.status ``` ## Related Examples * [Invoice Data Definition](/guides/data-definitions/examples/invoice) - Financial document extraction * [Contract Data Definition](/guides/data-definitions/examples/contract) - Legal document metadata * [Purchase Order Data Definition](/guides/data-definitions/examples/purchase-order) - Procurement document extraction # Invoice Data Definition Example Source: https://developer.kodexa.ai/guides/data-definitions/examples/invoice Production-ready example data definition for invoice extraction in Kodexa, covering header fields, vendor details, line items, totals, and validations. ## Overview This example demonstrates a comprehensive invoice extraction data definition suitable for production use. It extracts header information, vendor details, customer information, line items, and totals with built-in validation. ## Complete Invoice Data Definition ```yaml theme={null} slug: invoice-extraction name: Invoice Data Extraction description: Extract structured data from invoices including header, vendor, customer, line items, and totals taxonomyType: CONTENT enabled: true taxons: # ========================================== # Document Metadata # ========================================== - id: auto-generated name: document_filename label: Document Filename taxonType: STRING valuePath: METADATA metadataValue: FILENAME enabled: true group: false userEditable: false notUserLabelled: true - id: auto-generated name: processing_date label: Processing Date taxonType: DATE_TIME valuePath: METADATA metadataValue: CREATED_DATETIME enabled: true group: false userEditable: false notUserLabelled: true typeFeatures: normalizeDate: true dateFormat: "yyyy-MM-dd HH:mm:ss" # ========================================== # Invoice Header # ========================================== - id: auto-generated name: invoice_number label: Invoice Number description: Unique identifier for this invoice taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false color: "#3B82F6" semanticDefinition: | The unique invoice number, typically found at the top right of the invoice. Look for labels like "Invoice #", "Invoice No.", "Invoice Number", or just "#". This is usually a combination of letters and numbers (e.g., "INV-2024-001"). validationRules: - name: Invoice number required description: Every invoice must have an invoice number disabled: false conditional: false ruleFormula: "NOT_EMPTY(invoice_number)" messageFormula: '"Invoice number is required"' detailFormula: '"Please verify the invoice has a visible invoice number"' overridable: false exceptionId: INV_NUMBER_REQUIRED typeFeatures: expected: true - id: auto-generated name: invoice_date label: Invoice Date description: The date the invoice was issued taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false color: "#10B981" semanticDefinition: | The date when the invoice was issued by the vendor. Look for labels like "Invoice Date", "Date", "Issue Date", or "Billing Date". This is different from the due date or payment date. typeFeatures: normalizeDate: true dateFormat: "yyyy-MM-dd" expected: true validationRules: - name: Invoice date required ruleFormula: "NOT_EMPTY(invoice_date)" messageFormula: '"Invoice date is required"' overridable: false exceptionId: INV_DATE_REQUIRED - name: Invoice date not in future ruleFormula: "invoice_date <= TODAY()" messageFormula: '"Invoice date cannot be in the future"' detailFormula: '"Invoice date: " + invoice_date + ", Today: " + TODAY()' overridable: true exceptionId: INV_DATE_FUTURE - id: auto-generated name: due_date label: Due Date description: Payment due date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false color: "#F59E0B" semanticDefinition: | The date by which payment is due. Look for labels like "Due Date", "Payment Due", "Pay By", or "Due By". typeFeatures: normalizeDate: true dateFormat: "yyyy-MM-dd" validationRules: - name: Due date after invoice date conditional: true conditionalFormula: "NOT_EMPTY(due_date)" ruleFormula: "due_date >= invoice_date" messageFormula: '"Due date must be on or after the invoice date"' detailFormula: '"Invoice: " + invoice_date + ", Due: " + due_date' overridable: false exceptionId: DUE_DATE_BEFORE_INV conditionalFormats: - name: Overdue invoice formula: "due_date < TODAY() AND status != 'PAID'" backgroundColor: "#FEE2E2" textColor: "#991B1B" fontWeight: bold - id: auto-generated name: purchase_order label: Purchase Order description: Reference PO number if applicable taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | The customer's purchase order number that this invoice is billing against. Look for labels like "PO #", "PO Number", "Purchase Order", "P.O.", or "Reference". May not be present on all invoices. nullable: true - id: auto-generated name: payment_terms label: Payment Terms description: Payment terms and conditions taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | The agreed payment terms for this invoice. Look for terms like "Net 30", "Due on Receipt", "COD", etc. selectionOptions: - label: "Due on Receipt" value: "DUE_ON_RECEIPT" id: "due_on_receipt" description: "Payment due immediately upon receipt" lexicalRelations: - type: SYNONYM value: "Immediate, Upon Receipt, COD, Cash on Delivery" - label: "Net 10" value: "NET_10" id: "net_10" description: "Payment due within 10 days" lexicalRelations: - type: SYNONYM value: "10 days, Within 10 days" - label: "Net 30" value: "NET_30" id: "net_30" description: "Payment due within 30 days" hint: "Most common B2B payment term" lexicalRelations: - type: SYNONYM value: "30 days, Within 30 days" - label: "Net 60" value: "NET_60" id: "net_60" description: "Payment due within 60 days" isConditional: true conditionalFormula: "total_amount > 10000" lexicalRelations: - type: SYNONYM value: "60 days, Within 60 days" - label: "Net 90" value: "NET_90" id: "net_90" description: "Payment due within 90 days" isConditional: true conditionalFormula: "total_amount > 50000" lexicalRelations: - type: SYNONYM value: "90 days, Within 90 days" - label: "2/10 Net 30" value: "2_10_NET_30" id: "2_10_net_30" description: "2% discount if paid within 10 days, otherwise net 30" hint: "Early payment discount — **2% off** if paid within 10 days" hintMarkdown: true lexicalRelations: - type: SYNONYM value: "2% 10 days, early payment discount" # ========================================== # Vendor Information # ========================================== - id: auto-generated name: vendor label: Vendor Information description: Details about the vendor/supplier enabled: true group: true children: - id: auto-generated name: name label: Vendor Name description: Legal business name of the vendor taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | The vendor's legal business name. Look in the top portion of the invoice, near "From", "Vendor", "Supplier", or "Bill From". This should be the company name, not an individual's name. typeFeatures: expected: true validationRules: - name: Vendor name required ruleFormula: "NOT_EMPTY(vendor.name)" messageFormula: '"Vendor name is required"' overridable: false exceptionId: VENDOR_NAME_REQUIRED - id: auto-generated name: address label: Address description: Vendor's business address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | The vendor's complete business address including street, city, state/province, and postal code. typeFeatures: longText: true maxTextRows: 4 - id: auto-generated name: tax_id label: Tax ID / VAT Number description: Vendor's tax identification number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | The vendor's tax identification number. In the US, look for "EIN", "Tax ID", or "Federal ID". In EU, look for "VAT Number", "VAT Reg", or "BTW". nullable: true - id: auto-generated name: email label: Email description: Vendor contact email taxonType: EMAIL_ADDRESS valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "Vendor's email address for correspondence" nullable: true - id: auto-generated name: phone label: Phone Number description: Vendor contact phone number taxonType: PHONE_NUMBER valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "Vendor's phone number for contact" nullable: true # ========================================== # Customer/Bill To Information # ========================================== - id: auto-generated name: customer label: Customer Information description: Details about the customer being billed enabled: true group: true children: - id: auto-generated name: name label: Customer Name description: Name of the customer/organization being billed taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | The customer's name or business name. Look near "Bill To", "Customer", "Sold To", or "Invoice To". typeFeatures: expected: true - id: auto-generated name: address label: Billing Address description: Customer's billing address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "The complete billing address for the customer" typeFeatures: longText: true maxTextRows: 4 # ========================================== # Line Items (Repeating Group) # ========================================== - id: auto-generated name: line_items label: Line Items description: Individual items or services being billed enabled: true group: true additionContexts: - type: RECORD_DEFINITION context: | Each line item represents a product or service being billed. Line items typically appear in a table with columns for description, quantity, unit price, and total. - type: RECORD_START_MARKER context: "Description, Item, Product" - type: RECORD_END_MARKER context: "Subtotal, Total" children: - id: auto-generated name: line_number label: Line Number description: Sequential line item number taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "The line number or position of this item in the invoice" nullable: true - id: auto-generated name: description label: Description description: Description of the item or service taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | Description of the product or service being billed. This may include product codes, part numbers, or detailed descriptions. typeFeatures: longText: true expected: true - id: auto-generated name: quantity label: Quantity description: Number of units taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "The quantity or number of units for this line item" typeFeatures: expected: true validationRules: - name: Quantity must be positive ruleFormula: "line_items.quantity > 0" messageFormula: '"Quantity must be greater than zero"' overridable: false exceptionId: QTY_NOT_POSITIVE - id: auto-generated name: unit_price label: Unit Price description: Price per unit taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "The price for one unit of this item" typeFeatures: expected: true - id: auto-generated name: line_total label: Line Total description: Total for this line (quantity × unit price) taxonType: CURRENCY valuePath: FORMULA enabled: true group: false semanticDefinition: "line_items.quantity * line_items.unit_price" validationRules: - name: Line total calculation check ruleFormula: "ABS(line_items.line_total - (line_items.quantity * line_items.unit_price)) < 0.01" messageFormula: '"Line total does not match quantity × unit price"' detailFormula: '"Expected: " + (line_items.quantity * line_items.unit_price) + ", Found: " + line_items.line_total' overridable: true exceptionId: LINE_TOTAL_MISMATCH # ========================================== # Totals and Amounts # ========================================== - id: auto-generated name: subtotal label: Subtotal description: Sum of all line items before tax taxonType: CURRENCY valuePath: FORMULA enabled: true group: false semanticDefinition: "SUM(line_items.line_total)" typeFeatures: expected: true - id: auto-generated name: tax_rate label: Tax Rate description: Applicable tax rate as percentage taxonType: PERCENTAGE valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "The tax rate applied to this invoice, expressed as a percentage" nullable: true - id: auto-generated name: tax_amount label: Tax Amount description: Total tax amount taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | The total tax amount charged. Look for labels like "Tax", "Sales Tax", "VAT", "GST", or "Tax Amount". nullable: true - id: auto-generated name: shipping_handling label: Shipping & Handling description: Shipping and handling charges taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "Shipping and handling fees, if applicable" nullable: true nullValue: "0.00" - id: auto-generated name: discount_amount label: Discount Amount description: Total discount applied taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: "Any discounts applied to the invoice total" nullable: true nullValue: "0.00" - id: auto-generated name: total_amount label: Total Amount Due description: Final amount to be paid taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false color: "#EF4444" semanticDefinition: | The final total amount due, including all taxes, fees, and discounts. This is the bottom-line number the customer must pay. Look for labels like "Total", "Total Due", "Amount Due", "Balance Due", or "Grand Total". typeFeatures: expected: true overrideWidth: true displayWidth: 150 validationRules: - name: Total amount required ruleFormula: "NOT_EMPTY(total_amount)" messageFormula: '"Total amount is required"' overridable: false exceptionId: TOTAL_REQUIRED - name: Total calculation verification conditional: true conditionalFormula: "NOT_EMPTY(subtotal) AND NOT_EMPTY(tax_amount)" ruleFormula: | ABS(total_amount - (subtotal + COALESCE(tax_amount, 0) + COALESCE(shipping_handling, 0) - COALESCE(discount_amount, 0))) < 0.01 messageFormula: | "Total amount does not match calculated total" detailFormula: | "Expected: " + (subtotal + COALESCE(tax_amount, 0) + COALESCE(shipping_handling, 0) - COALESCE(discount_amount, 0)) + ", Found: " + total_amount overridable: true exceptionId: TOTAL_CALC_MISMATCH - name: Unusually high amount warning ruleFormula: "total_amount < 100000" messageFormula: '"Invoice total exceeds $100,000 - please verify accuracy"' overridable: true exceptionId: HIGH_AMOUNT_WARNING conditionalFormats: - name: High value invoice formula: "total_amount > 10000" backgroundColor: "#FEF3C7" textColor: "#92400E" icon: warning # ========================================== # Payment Information # ========================================== - id: auto-generated name: payment_status label: Payment Status description: Current payment status taxonType: SELECTION valuePath: REVIEW enabled: true group: false semanticDefinition: | Current payment status for this invoice selectionOptions: - label: "Pending" id: "pending" description: "Payment not yet received" - label: "Paid" id: "paid" description: "Payment received and processed" - label: "Overdue" id: "overdue" description: "Payment past due date" isConditional: true conditionalFormula: "due_date < TODAY()" - label: "Cancelled" id: "cancelled" description: "Invoice cancelled" - id: auto-generated name: notes label: Notes / Comments description: Additional notes or special instructions taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT enabled: true group: false semanticDefinition: | Any special notes, instructions, or comments on the invoice. Look for sections labeled "Notes", "Comments", "Terms", or "Special Instructions". typeFeatures: longText: true maxTextRows: 6 markdown: true nullable: true ``` ## Usage ### Extracting Invoice Data 1. **Create or Update Data Definition**: Use this YAML structure in your Kodexa platform 2. **Process Invoices**: Upload invoice documents for extraction 3. **Review Results**: The data definition will extract all defined fields with validation 4. **Handle Exceptions**: Review and resolve any validation failures ### Validation Rules This data definition includes several validation rules: * **Required Fields**: Invoice number, date, vendor name, total amount * **Date Logic**: Due date must be after invoice date * **Calculations**: Line totals and invoice total verified * **Business Rules**: Warnings for high-value invoices * **Data Quality**: Phone and email format validation ### Customization Adapt this data definition by: * Adding industry-specific fields (e.g., project codes for professional services) * Modifying validation thresholds (e.g., high-value amount limit) * Adding custom payment terms or statuses * Including additional vendor or customer fields * Adding currency-specific formatting rules ## Next Steps Learn about data elements, groups, value sources, and configuration options Validation rule and conditional formatting reference Available formula functions Version control your data definitions # Purchase Order Data Definition Example Source: https://developer.kodexa.ai/guides/data-definitions/examples/purchase-order Example Kodexa data definition for extracting purchase orders, including buyer details, line items, shipping, pricing, and approval workflows. ## Overview This example demonstrates a comprehensive data definition for extracting data from purchase orders (POs). It covers buyer information, vendor details, line items, shipping, pricing, and approval workflows for procurement management systems. ## Use Cases * **Procurement Automation**: Extract PO data for automated order processing * **Vendor Management**: Track vendor performance and order history * **Inventory Management**: Capture ordered quantities for inventory planning * **Accounts Payable**: Match POs with invoices for three-way matching * **Spend Analytics**: Analyze purchasing patterns and spending by category * **Compliance Tracking**: Ensure orders follow approval policies and budgets ## Complete Data Definition Structure ### YAML Configuration ```yaml theme={null} slug: purchase-order name: Purchase Order Data Extraction description: Extract structured data from purchase orders for procurement and AP systems taxonomyType: CONTENT enabled: true taxons: # ========================================== # Document Metadata # ========================================== - name: document_metadata label: Document Metadata group: true children: - name: filename label: Original Filename taxonType: STRING valuePath: METADATA metadataValue: FILENAME - name: processing_date label: Processing Date taxonType: DATE_TIME valuePath: METADATA metadataValue: PROCESSING_DATE - name: document_hash label: Document Hash taxonType: STRING valuePath: METADATA metadataValue: CONTENT_HASH description: SHA-256 hash for document integrity # ========================================== # Purchase Order Header # ========================================== - name: po_header label: Purchase Order Header group: true children: - name: po_number label: PO Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The unique purchase order number. Look for "PO#", "Order Number", "Purchase Order No." in the header. This is the primary identifier. - name: po_date label: PO Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The date the purchase order was created or issued. Look for "PO Date", "Order Date", "Issue Date" in the header. - name: required_by_date label: Required By Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The date by which goods/services are needed. May be labeled "Delivery Date", "Need By", "Ship By", or "Required Date". - name: revision_number label: Revision Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Version or revision number if the PO has been amended (e.g., "Rev 1", "v2"). - name: original_po_number label: Original PO Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | If this is a revision or amendment, the original PO number it replaces. - name: requisition_number label: Requisition Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The requisition or request number that led to this PO. Look for "Req #", "PR #", "Request No." - name: buyer_name label: Buyer Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The name of the purchasing agent or buyer who created the PO. - name: department label: Department/Cost Center taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The department or cost center this order is charged to. - name: project_code label: Project Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Project or job number if this purchase is for a specific project. # ========================================== # Buyer/Company Information # ========================================== - name: buyer label: Buyer/Company Information group: true description: The organization issuing the purchase order children: - name: company_name label: Company Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The legal name of the buying organization. Usually in the header or "Bill To" section. - name: address label: Company Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The full address of the buying organization. - name: city label: City taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: state_province label: State/Province taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: postal_code label: Postal Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: country label: Country taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: phone label: Phone taxonType: PHONE_NUMBER valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: email label: Email taxonType: EMAIL_ADDRESS valuePath: VALUE_OR_ALL_CONTENT nullable: true # ========================================== # Vendor/Supplier Information # ========================================== - name: vendor label: Vendor/Supplier Information group: true description: The supplier fulfilling the order children: - name: vendor_name label: Vendor Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The legal name of the vendor or supplier. Look in "Vendor", "Supplier", or "Sold By" sections. - name: vendor_id label: Vendor ID/Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The buyer's internal vendor identification code or number. - name: address label: Vendor Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The vendor's business address" - name: city label: City taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: state_province label: State/Province taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: postal_code label: Postal Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: country label: Country taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: phone label: Phone taxonType: PHONE_NUMBER valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: email label: Email taxonType: EMAIL_ADDRESS valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: contact_person label: Contact Person taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Primary vendor contact for this order" # ========================================== # Ship To Information # ========================================== - name: ship_to label: Ship To Information group: true description: Delivery destination children: - name: location_name label: Location/Facility Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Name of the delivery location (e.g., "Warehouse A", "Main Office"). - name: attention_to label: Attention To taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Person or department to receive the shipment" - name: address label: Shipping Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The complete shipping address. Look for "Ship To" section on the PO. - name: city label: City taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: state_province label: State/Province taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: postal_code label: Postal Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: country label: Country taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: phone label: Phone taxonType: PHONE_NUMBER valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: delivery_instructions label: Delivery Instructions taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Special delivery instructions (e.g., "Loading dock hours: 8am-5pm", "Call before delivery"). # ========================================== # Bill To Information # ========================================== - name: bill_to label: Bill To Information group: true description: Invoicing destination (often same as buyer) children: - name: company_name label: Company Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The name for invoicing. Look for "Bill To" section. May match buyer info. - name: address label: Billing Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: city label: City taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: state_province label: State/Province taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: postal_code label: Postal Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: country label: Country taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true - name: ap_contact label: AP Contact taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Accounts payable contact person" - name: ap_email label: AP Email taxonType: EMAIL_ADDRESS valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Email for sending invoices" # ========================================== # Line Items # ========================================== - name: line_items label: Line Items group: true description: Ordered goods and services children: - name: line_number label: Line Number taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The line item number (1, 2, 3...). Usually the first column in the line items table. - name: item_code label: Item Code/SKU taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The product SKU, item number, or part number. Look for "Item #", "Part #", "SKU", or "Code" columns. - name: description label: Description taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The description of the product or service. This is usually the longest text field in the line items table. - name: quantity label: Quantity Ordered taxonType: NUMBER valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The quantity being ordered. Look for "Qty", "Quantity", or "Ordered" columns. - name: unit_of_measure label: Unit of Measure taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The unit of measure (e.g., "EA" for each, "BOX", "LB", "HR", "FT"). May be labeled "UOM", "Unit", or "U/M". - name: unit_price label: Unit Price taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The price per unit. Look for "Unit Price", "Price Each", or "Rate" columns. - name: line_total label: Line Total taxonType: CURRENCY valuePath: FORMULA semanticDefinition: "quantity * unit_price" description: "Calculated total for this line" - name: discount_percent label: Discount Percentage taxonType: PERCENTAGE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Discount percentage applied to this line item if any. - name: discount_amount label: Discount Amount taxonType: CURRENCY valuePath: FORMULA nullable: true semanticDefinition: "line_total * (discount_percent / 100)" description: "Calculated discount amount" - name: net_line_total label: Net Line Total taxonType: CURRENCY valuePath: FORMULA semanticDefinition: "line_total - COALESCE(discount_amount, 0)" description: "Line total after discount" - name: delivery_date label: Requested Delivery Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Delivery date for this specific line item if different from PO-level required by date. - name: gl_account label: GL Account Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The general ledger account code this expense should be charged to. - name: notes label: Line Notes taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Any special notes or comments for this line item" # ========================================== # Shipping Information # ========================================== - name: shipping label: Shipping Information group: true children: - name: shipping_method label: Shipping Method taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The shipping method or carrier. Look for "Ship Via", "Carrier", or "Shipping Method" fields. selectionOptions: - label: "Ground" - label: "Express" - label: "Overnight" - label: "2-Day" - label: "Freight" - label: "Customer Pickup" - label: "Will Call" - label: "Other" - name: shipping_terms label: Shipping Terms taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Who pays shipping costs. Look for "FOB", "Freight Terms", or "Shipping Terms". selectionOptions: - label: "FOB Origin" # Buyer pays shipping - label: "FOB Destination" # Seller pays shipping - label: "Prepaid" # Seller pays - label: "Collect" # Buyer pays - label: "Prepaid & Add" # Seller pays, adds to invoice - name: carrier label: Carrier Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The shipping carrier name (e.g., "FedEx", "UPS", "USPS", "DHL"). - name: account_number label: Carrier Account Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Buyer's account number with the carrier for billing purposes. - name: shipping_cost label: Shipping/Freight Cost taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The cost of shipping if specified on the PO. - name: handling_cost label: Handling Cost taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Any handling fees charged" # ========================================== # Payment Terms # ========================================== - name: payment label: Payment Terms group: true children: - name: payment_terms label: Payment Terms taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The payment terms agreed upon. Look for "Payment Terms", "Terms", or "Net" fields. Common values: "Net 30", "Net 60", "2/10 Net 30", "Due on Receipt". - name: payment_method label: Payment Method taxonType: SELECTION valuePath: VALUE_OR_ALL_CONTENT nullable: true selectionOptions: - label: "Check" - label: "ACH/Wire Transfer" - label: "Credit Card" - label: "Purchase Card" - label: "Other" - name: currency label: Currency taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: | The currency for all monetary values (USD, EUR, GBP, CAD, etc.). # ========================================== # Totals # ========================================== - name: totals label: Totals group: true children: - name: subtotal label: Subtotal taxonType: CURRENCY valuePath: FORMULA semanticDefinition: "SUM(line_items.net_line_total)" description: "Sum of all line items after discounts" - name: discount_total label: Total Discounts taxonType: CURRENCY valuePath: FORMULA nullable: true semanticDefinition: "SUM(line_items.discount_amount)" description: "Total of all line item discounts" - name: shipping_total label: Total Shipping & Handling taxonType: CURRENCY valuePath: FORMULA nullable: true semanticDefinition: | COALESCE(shipping.shipping_cost, 0) + COALESCE(shipping.handling_cost, 0) description: "Combined shipping and handling costs" - name: tax_rate label: Tax Rate taxonType: PERCENTAGE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | The sales tax rate if specified on the PO. - name: tax_amount label: Tax Amount taxonType: CURRENCY valuePath: FORMULA nullable: true semanticDefinition: | (subtotal + COALESCE(shipping_total, 0)) * (COALESCE(tax_rate, 0) / 100) description: "Calculated tax on subtotal plus shipping" - name: total_amount label: Total PO Amount taxonType: CURRENCY valuePath: FORMULA semanticDefinition: | subtotal + COALESCE(shipping_total, 0) + COALESCE(tax_amount, 0) description: "Grand total of the purchase order" # ========================================== # Special Instructions and Terms # ========================================== - name: instructions label: Special Instructions and Terms group: true children: - name: special_instructions label: Special Instructions taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true multiValue: true semanticDefinition: | Any special handling, packaging, or fulfillment instructions. Look for "Special Instructions", "Notes", or "Comments" sections. - name: terms_and_conditions label: Terms and Conditions taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Legal terms, return policies, or other contractual language. May be in footer or separate terms section. - name: cancellation_policy label: Cancellation Policy taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Rules for canceling or modifying the order. - name: warranty_terms label: Warranty Terms taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Warranty or guarantee information if specified" # ========================================== # Approval Workflow # ========================================== - name: approval label: Approval Workflow group: true children: - name: approved_by label: Approved By taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: | Name of the person who approved the PO. Look for signature areas or "Approved By" fields. - name: approval_date label: Approval Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Date the PO was approved" - name: signature_present label: Signature Present taxonType: BOOLEAN valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Is there an authorized signature on the PO?" - name: budget_code label: Budget Code taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT nullable: true semanticDefinition: "Budget or authorization code if required" - name: approval_level label: Approval Level Required taxonType: SELECTION valuePath: FORMULA semanticDefinition: | IF(totals.total_amount > 50000, "Executive", IF(totals.total_amount > 10000, "Manager", "Supervisor")) selectionOptions: - label: "Supervisor" - label: "Manager" - label: "Director" - label: "Executive" # ========================================== # Status and Tracking # ========================================== - name: status label: PO Status and Tracking group: true children: - name: po_status label: PO Status taxonType: SELECTION valuePath: REVIEW userEditable: true selectionOptions: - label: "Draft" - label: "Pending Approval" - label: "Approved" - label: "Sent to Vendor" - label: "Acknowledged" - label: "Partially Received" - label: "Fully Received" - label: "Invoiced" - label: "Closed" - label: "Cancelled" - name: sent_to_vendor_date label: Sent to Vendor Date taxonType: DATE valuePath: REVIEW userEditable: true nullable: true - name: vendor_confirmation_number label: Vendor Confirmation Number taxonType: STRING valuePath: REVIEW userEditable: true nullable: true description: "Vendor's order confirmation or acknowledgment number" - name: estimated_delivery label: Estimated Delivery Date taxonType: DATE valuePath: REVIEW userEditable: true nullable: true description: "Vendor's estimated delivery date" - name: notes label: Internal Notes taxonType: STRING valuePath: REVIEW userEditable: true nullable: true description: "Internal tracking notes" # ========================================== # Validation Rules # ========================================== validationRules: # Required Fields - name: "PO number required" ruleFormula: "NOT_EMPTY(po_header.po_number)" messageFormula: '"Purchase order number is required"' overridable: false - name: "PO date required" ruleFormula: "NOT_EMPTY(po_header.po_date)" messageFormula: '"PO date is required"' overridable: false - name: "Vendor name required" ruleFormula: "NOT_EMPTY(vendor.vendor_name)" messageFormula: '"Vendor name is required"' overridable: false - name: "At least one line item" ruleFormula: "COUNT(line_items) >= 1" messageFormula: '"PO must have at least one line item"' overridable: false - name: "Ship to address required" ruleFormula: "NOT_EMPTY(ship_to.address)" messageFormula: '"Shipping address is required"' overridable: false # Date Logic - name: "PO date not in future" ruleFormula: "po_header.po_date <= TODAY()" messageFormula: '"PO date should not be in the future"' overridable: true - name: "Required date after PO date" ruleFormula: | IS_EMPTY(po_header.required_by_date) OR po_header.required_by_date >= po_header.po_date messageFormula: '"Required by date should be on or after PO date"' overridable: true - name: "Approval date after or equal to PO date" ruleFormula: | IS_EMPTY(approval.approval_date) OR approval.approval_date >= po_header.po_date messageFormula: '"Approval date should be on or after PO date"' overridable: true # Line Item Validation - name: "Line quantities must be positive" ruleFormula: "ALL_VALUES(line_items.quantity) > 0" messageFormula: '"All line item quantities must be greater than zero"' overridable: false - name: "Unit prices must be positive" ruleFormula: "ALL_VALUES(line_items.unit_price) > 0" messageFormula: '"All unit prices must be greater than zero"' overridable: false - name: "Line numbers unique" ruleFormula: "UNIQUE(line_items.line_number)" messageFormula: '"Line numbers must be unique"' overridable: true # Financial Validation - name: "Total amount positive" ruleFormula: "totals.total_amount > 0" messageFormula: '"Total PO amount must be greater than zero"' overridable: false - name: "Currency specified" ruleFormula: "NOT_EMPTY(payment.currency)" messageFormula: '"Currency must be specified"' overridable: false - name: "Tax rate reasonable" ruleFormula: | IS_EMPTY(totals.tax_rate) OR (totals.tax_rate >= 0 AND totals.tax_rate <= 25) messageFormula: '"Tax rate should be between 0% and 25%"' overridable: true - name: "Payment terms specified" ruleFormula: "NOT_EMPTY(payment.payment_terms)" messageFormula: '"Payment terms should be specified"' overridable: true # Approval Validation - name: "High-value POs require approval" ruleFormula: | totals.total_amount <= 10000 OR NOT_EMPTY(approval.approved_by) messageFormula: '"POs over $10,000 require approval"' overridable: false - name: "Executive approval for large orders" ruleFormula: | totals.total_amount <= 50000 OR (NOT_EMPTY(approval.approved_by) AND approval.approval_level == "Executive") messageFormula: '"POs over $50,000 require executive approval"' overridable: true - name: "Signature required for approved POs" conditional: true conditionalFormula: 'status.po_status IN ["Approved", "Sent to Vendor", "Acknowledged"]' ruleFormula: "approval.signature_present == true" messageFormula: '"Approved POs must have an authorized signature"' overridable: false # Shipping Validation - name: "Carrier required for non-pickup orders" conditional: true conditionalFormula: 'shipping.shipping_method NOT IN ["Customer Pickup", "Will Call"]' ruleFormula: "NOT_EMPTY(shipping.carrier)" messageFormula: '"Carrier name required unless customer pickup"' overridable: true - name: "Shipping cost for collect terms" conditional: true conditionalFormula: 'shipping.shipping_terms == "Collect"' ruleFormula: "IS_EMPTY(shipping.shipping_cost)" messageFormula: '"Shipping cost should not be specified for collect terms"' overridable: true # Contact Validation - name: "Vendor contact information" ruleFormula: | NOT_EMPTY(vendor.phone) OR NOT_EMPTY(vendor.email) messageFormula: '"At least one vendor contact method (phone or email) required"' overridable: true # ========================================== # Conditional Formatting # ========================================== conditionalFormats: - name: "High-value order" formula: "totals.total_amount > 50000" backgroundColor: "#FEF3C7" textColor: "#92400E" fontWeight: "bold" icon: "dollar-sign" - name: "Urgent delivery" formula: | NOT_EMPTY(po_header.required_by_date) AND po_header.required_by_date <= DATE_ADD(TODAY(), 7) backgroundColor: "#FED7AA" textColor: "#9A3412" icon: "clock" - name: "Overdue for delivery" formula: | NOT_EMPTY(po_header.required_by_date) AND po_header.required_by_date < TODAY() AND status.po_status NOT IN ["Fully Received", "Closed", "Cancelled"] backgroundColor: "#FEE2E2" textColor: "#991B1B" fontWeight: "bold" icon: "exclamation-triangle" - name: "Pending approval" formula: 'status.po_status IN ["Draft", "Pending Approval"]' backgroundColor: "#DBEAFE" textColor: "#1E40AF" icon: "hourglass" - name: "Cancelled order" formula: 'status.po_status == "Cancelled"' backgroundColor: "#FEE2E2" textColor: "#991B1B" icon: "times-circle" - name: "Fully received" formula: 'status.po_status IN ["Fully Received", "Closed"]' backgroundColor: "#D1FAE5" textColor: "#065F46" icon: "check-circle" - name: "Missing approval for large order" formula: | totals.total_amount > 10000 AND IS_EMPTY(approval.approved_by) backgroundColor: "#FEE2E2" textColor: "#991B1B" fontWeight: "bold" icon: "warning" - name: "Multiple line items" formula: "COUNT(line_items) > 10" backgroundColor: "#E0E7FF" textColor: "#3730A3" icon: "list" ``` ## Key Features ### Comprehensive Header Information * **PO Identification**: Number, date, revision tracking * **Reference Numbers**: Requisition, project codes, department tracking * **Buyer Information**: Purchasing agent, cost center allocation ### Complete Party Details * **Buyer/Company**: Issuing organization information * **Vendor/Supplier**: Complete vendor details and contacts * **Ship To**: Delivery destination with instructions * **Bill To**: Invoicing destination for AP processing ### Detailed Line Items * **Product Information**: Item codes, descriptions, quantities * **Pricing**: Unit prices, discounts, calculated totals * **Allocation**: GL accounts, delivery dates per line * **Flexible UOM**: Support for various units of measure ### Shipping and Logistics * **Shipping Methods**: Ground, express, overnight, freight, pickup * **Shipping Terms**: FOB origin/destination, prepaid, collect * **Carrier Details**: Carrier name, account numbers * **Costs**: Shipping and handling fee tracking ### Financial Management * **Payment Terms**: Net 30, 2/10 Net 30, etc. * **Payment Methods**: Check, ACH, credit card, purchase card * **Currency Support**: Multi-currency handling * **Tax Calculation**: Automatic tax computation on subtotal + shipping ### Automated Calculations * **Line Totals**: Quantity × Unit Price * **Discounts**: Percentage-based discounts calculated automatically * **Net Totals**: Line totals after discounts * **Subtotal**: Sum of all line items * **Tax**: Calculated on subtotal + shipping * **Grand Total**: Complete PO amount ### Approval Workflow * **Approval Tracking**: Who approved and when * **Signature Verification**: Ensure authorized signatures * **Budget Codes**: Track authorization codes * **Dynamic Approval Levels**: Based on PO amount (Supervisor \< $10k, Manager < $50k, Executive ≥ \$50k) ### Status Tracking * **Lifecycle Management**: Draft → Approved → Sent → Received → Closed * **Vendor Communication**: Track confirmation numbers and delivery estimates * **Internal Notes**: Capture tracking and follow-up information ## Validation Strategy ### Critical Validations (Non-overridable) ```yaml theme={null} - PO number required - PO date required - Vendor name required - At least one line item - Ship to address required - Line quantities > 0 - Unit prices > 0 - Total amount > 0 - Currency specified - High-value POs require approval - Approved POs must have signature ``` ### Quality Checks (Overridable) ```yaml theme={null} - PO date not in future - Required date after PO date - Line numbers unique - Tax rate reasonable (0-25%) - Payment terms specified - Vendor contact information ``` ### Conditional Business Rules ```yaml theme={null} - Executive approval for POs > $50k - Carrier required for non-pickup orders - Shipping cost logic for collect terms ``` ## Conditional Formatting Provides visual indicators for: * **High-value orders** (> \$50k) - Gold highlight * **Urgent delivery** (\< 7 days) - Orange warning * **Overdue delivery** - Red alert * **Pending approval** - Blue information * **Cancelled orders** - Red indicator * **Fully received** - Green success * **Missing approval** (large orders) - Red warning * **Large orders** (> 10 line items) - Purple indicator ## Usage Tips ### For Procurement Teams 1. Use `po_header` fields for order tracking and management 2. Monitor `status.po_status` for lifecycle management 3. Track `approval` information for compliance 4. Use `totals.total_amount` for spend analysis ### For Accounts Payable 1. Extract `vendor` and `bill_to` information for invoice matching 2. Use `payment` terms for payment scheduling 3. Verify `totals` match invoice amounts (three-way matching) 4. Track `po_number` for invoice-to-PO reconciliation ### For Receiving/Warehouse 1. Focus on `ship_to` information for delivery routing 2. Use `line_items` for receiving verification 3. Track `shipping.delivery_instructions` for special handling 4. Monitor `po_header.required_by_date` for prioritization ### For Finance Teams 1. Extract `line_items.gl_account` for accounting allocation 2. Use `department` and `project_code` for cost center reporting 3. Track `totals` for budget monitoring 4. Monitor approval levels for spend compliance ## Best Practices 1. **Always Extract Complete Line Items**: Quantity, unit price, and description are critical 2. **Calculate Totals with Formulas**: Use FORMULA type for line totals, subtotals, tax, and grand total 3. **Track Approval Workflow**: Capture approver, date, and signature for audit trail 4. **Validate Financial Data**: Ensure quantities and prices are positive, totals are accurate 5. **Monitor Delivery Dates**: Flag urgent and overdue orders for priority handling 6. **Support Multiple Addresses**: Ship To may differ from Bill To or Buyer address 7. **Handle Revisions**: Track original PO numbers when processing amendments 8. **Capture Special Instructions**: Delivery, packaging, and handling notes are important 9. **Link to Requisitions**: Trace POs back to original purchase requests 10. **Enable Three-Way Matching**: Structure data to match PO → Receipt → Invoice ## Integration Examples ### ERP/Procurement System ```yaml theme={null} externalName: poNumber → po_header.po_number externalName: vendorId → vendor.vendor_id externalName: totalAmount → totals.total_amount externalName: status → status.po_status externalName: requiredDate → po_header.required_by_date ``` ### Accounts Payable System ```yaml theme={null} externalName: invoiceMatchPO → po_header.po_number externalName: vendorName → vendor.vendor_name externalName: paymentTerms → payment.payment_terms externalName: invoiceAmount → totals.total_amount ``` ### Inventory Management System ```yaml theme={null} externalName: itemCode → line_items.item_code externalName: quantityOrdered → line_items.quantity externalName: expectedDate → po_header.required_by_date externalName: receivingLocation → ship_to.location_name ``` ### Spend Analytics Platform ```yaml theme={null} externalName: supplier → vendor.vendor_name externalName: category → line_items.gl_account externalName: spend → totals.total_amount externalName: department → po_header.department ``` ## Related Examples * [Invoice Data Definition](/guides/data-definitions/examples/invoice) - For matching invoices to POs * [Contract Data Definition](/guides/data-definitions/examples/contract) - For master purchase agreements * [Form Data Definition](/guides/data-definitions/examples/form) - For purchase requisition forms # Data Definitions Guide Source: https://developer.kodexa.ai/guides/data-definitions/index Define the structure, validation rules, and extraction logic for document data in Kodexa using Data Definitions and configurable data elements. ## Overview Data definitions in Kodexa provide the structure and rules for extracting, validating, and processing information from documents. They define what data to extract, how to validate it, and how to present it to users. ## What Are Data Definitions? **Data definitions** are the blueprints for your document processing workflows. They specify: * **Structure**: What data elements exist and how they relate * **Types**: What kind of data each field contains (text, numbers, dates, etc.) * **Sources**: Where data comes from (document content, metadata, calculations, review) * **Validation**: Business rules and data quality checks * **Behavior**: Formulas, selection options, event scripts, and validation cascades that run when data changes Data elements, groups, sources, and extraction behavior Reactive JavaScript scripts that run when modeled data changes Calculation logic for derived and computed fields Business rules, exceptions, and reviewer-facing visual cues *** ## Core Concepts ### Data Structure **Data definitions** are hierarchical structures of data elements that define what to extract from documents. In configuration and API payloads, those elements are still stored under the `taxons` field. **Example use cases**: * Invoice data extraction (vendor, line items, totals) * Contract metadata (parties, dates, terms) * Form processing (applicant info, answers, signatures) Learn how data elements, groups, value sources, and extraction behavior fit together ### Data Types Kodexa supports rich data types for accurate extraction and validation: * **STRING** - Text of any length * **NUMBER** - Numeric values * **BOOLEAN** - True/false values * **DATE** - Calendar dates * **DATE\_TIME** - Dates with timestamps * **CURRENCY** - Monetary amounts with precision handling * **PERCENTAGE** - Percentage values * **EMAIL\_ADDRESS** - Email validation * **PHONE\_NUMBER** - Phone number formats * **URL** - Web addresses * **SELECTION** - Dropdown/categorical values with options * **SECTION** - Visual grouping without data storage * **Groups** - Containers for related fields (can repeat) ### Data Sources Define where each data element gets its value: Extract directly from document content using AI/ML models ```yaml theme={null} valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Extract the invoice total amount" ``` Pull from document properties and system fields ```yaml theme={null} valuePath: METADATA metadataValue: FILENAME ``` Calculate from other fields ```yaml theme={null} valuePath: FORMULA semanticDefinition: "quantity * unit_price" ``` Fields populated during human review ```yaml theme={null} valuePath: REVIEW userEditable: true ``` *** ## Common Patterns ### Invoice Processing Extract structured data from invoices: ```yaml theme={null} taxons: - name: invoice_number label: Invoice Number taxonType: STRING - name: invoice_date label: Invoice Date taxonType: DATE - name: vendor label: Vendor group: true children: - name: name - name: address - name: tax_id - name: line_items label: Line Items group: true children: - name: description - name: quantity - name: unit_price - name: total valuePath: FORMULA semanticDefinition: "quantity * unit_price" - name: total_amount label: Total Amount taxonType: CURRENCY ``` ### Contract Metadata Capture key contract information: ```yaml theme={null} taxons: - name: contract_type label: Contract Type taxonType: SELECTION selectionOptions: - label: "Service Agreement" - label: "Purchase Order" - label: "NDA" - name: parties group: true children: - name: party_a - name: party_b - name: key_terms group: true children: - name: effective_date taxonType: DATE - name: term_length - name: termination_notice ``` ### Form Data Process form submissions: ```yaml theme={null} taxons: - name: applicant group: true children: - name: full_name - name: email taxonType: EMAIL_ADDRESS - name: phone taxonType: PHONE_NUMBER - name: responses group: true children: - name: question_1 - name: question_2 - name: agree_to_terms taxonType: BOOLEAN ``` *** ## Validation and Quality ### Validation Rules Define business rules to ensure data quality: ```yaml theme={null} validationRules: - name: "Required field check" ruleFormula: "!isblank({invoice_number})" messageFormula: '"Invoice number is required"' overridable: false - name: "Date logic check" conditional: true conditionalFormula: "!isblank({due_date}) && !isblank({invoice_date})" ruleFormula: "isafterdate({due_date}, {invoice_date}) || {due_date} = {invoice_date}" messageFormula: '"Due date must be after invoice date"' overridable: false - name: "Total verification" ruleFormula: "abs({total_amount} - sum({line_items/total})) < 0.01" messageFormula: '"Total mismatch detected"' overridable: true ``` Learn the exact `validationRules` schema, conditional formatting schema, formula language, and runtime behavior. ### Conditional Formatting Apply visual cues based on data values: ```yaml theme={null} conditionalFormats: - type: backgroundColor condition: "isbeforedate({due_date}, datemath('today')) && {status} != 'PAID'" properties: color: "#FEE2E2" - type: icon condition: "{total_amount} > 10000" properties: icon: alert-circle-outline color: "#92400E" ``` *** ## Best Practices ### Design Principles Begin with core fields and add complexity as needed. Don't over-engineer initial data definitions. **Start with**: * Essential fields only * Basic data types * Simple validation **Add later**: * Computed fields * Complex validations * Conditional formatting Write clear, specific extraction prompts: **Good**: ```yaml theme={null} semanticDefinition: | The vendor's legal business name as it appears at the top of the invoice. Look near 'Bill To', 'From', or 'Vendor' labels. ``` **Avoid**: ```yaml theme={null} semanticDefinition: "vendor name" # Too vague ``` Use groups to: * Organize related fields logically * Handle repeating structures (line items, signatories) * Improve UI presentation **Single instance groups**: Organizational containers ```yaml theme={null} - name: vendor group: true children: [name, address, tax_id] ``` **Repeating groups**: Collections ```yaml theme={null} - name: line_items group: true children: [description, quantity, price] ``` **Critical validations** (non-overridable): * Required fields * Data type constraints * Business logic rules **Quality checks** (overridable): * Unusual values * Formatting issues * Threshold warnings ### Naming Conventions Use consistent naming across your data definitions: ```yaml Field Names (Internal) theme={null} name: vendor_name # Snake case name: invoice_date # Descriptive, unambiguous name: line_items # Plural for groups ``` ```yaml Display Labels theme={null} label: Vendor Name # Title case label: Invoice Date # Human-readable label: Line Items # Matches business terminology ``` ```yaml External Names (APIs) theme={null} externalName: vendorName # Camel case externalName: invoiceDate # Consistent with API conventions ``` *** ## Getting Started Analyze the documents you'll process: * What data needs to be extracted? * What's the document structure? * What validations are needed? Sketch out the data structure: * List all required fields * Group related fields * Identify repeating sections For each field, define: * Data type * Value source * Semantic definition * Validation rules Process sample documents: * Verify extraction accuracy * Refine semantic definitions * Adjust validation rules *** ## Learn More Data elements, groups, value sources, and configuration options Built-in functions for calculations and validations Complete guide to validation rules, conditional formats, and formula behavior Add reactive JavaScript behavior to the data model Programmatic access to data definition management *** ## Examples Complete invoice extraction example Contract metadata extraction example Form data processing example Purchase order extraction example # Selection Option Formulas Source: https://developer.kodexa.ai/guides/data-definitions/selection-option-formulas Compute dropdown options for SELECTION data elements dynamically using JavaScript formulas that evaluate per data object and react to attribute changes. ## Overview Selection option formulas let you compute dropdown options dynamically at runtime instead of defining a static list. When a user opens a document, the formula evaluates for each data object and populates the dropdown with context-aware options. When referenced attributes change, the formula re-evaluates automatically and the dropdown updates in real time. **Common use cases:** * Load options from an external reference system via service bridges * Filter options based on sibling attribute values (e.g., show subcategories for the selected category) * Compute options based on document context *** ## Configuration Two properties on a SELECTION data element enable formula-driven options: ```yaml theme={null} - name: department label: Department taxonType: SELECTION useSelectionOptionFormula: true selectionOptionFormula: | serviceBridgeCall("myorg/reference-data", "list-departments", { region: getAttribute("region") }) ``` ### Properties When `true`, the formula is used instead of the static `selectionOptions` list to populate the dropdown. JavaScript expression evaluated by the formula runtime. Must return an array of options (see [Return Format](#return-format) below). You can define both static `selectionOptions` and a `selectionOptionFormula` on the same data element. The formula takes precedence when `useSelectionOptionFormula` is `true`. This is useful for having a fallback list during development. *** ## Return Format The formula must return an array. Two formats are accepted: **Object format (recommended):** ```javascript theme={null} [ { label: "Engineering", value: "ENG" }, { label: "Marketing", value: "MKT" }, { label: "Human Resources", value: "HR" } ] ``` **String format (simple):** ```javascript theme={null} ["Engineering", "Marketing", "Human Resources"] ``` When using string format, both label and value are set to the string. Returning `null` or an empty array `[]` clears the dropdown options. The distinction is preserved: `null` means "no data available" while `[]` means "explicitly empty list." *** ## How It Works ```mermaid theme={null} flowchart TD A[Document Opens] --> B[RecalculationService initializes] B --> C[Build selection formula dependency graph] C --> D[Evaluate formulas for all data objects] D --> E[Store results on DataObject.selectionOptions] E --> F[UI seeds dropdown cache from WASM] G[User edits attribute] --> H[Detect dependency change] H --> I[Re-evaluate affected formulas] I --> J[Compare with previous options via canonical JSON] J -->|Changed| K[Persist to database] K --> L[Emit selectionOptions:computed event] L --> M[UI updates dropdown in real time] J -->|Unchanged| N[Skip — no update needed] ``` Key points: * Options are computed and **persisted on the DataObject** (not the attribute) -- they survive page reloads * Change detection uses canonical JSON comparison (sorted keys) to avoid unnecessary updates * During initial load, events are suppressed to prevent a flood -- the UI reads options from the WASM layer directly * After initial load, real-time changes emit `selectionOptions:computed` events that update the UI reactively *** ## Referencing Sibling Attributes Formulas can read the current data object's attribute values to parameterize API calls: ```javascript theme={null} serviceBridgeCall("myorg/reference-data", "get-subcategories", { category: getAttribute("category"), region: getAttribute("region"), active: true }) ``` When any referenced attribute changes, the formula automatically re-evaluates. *** ## Service Bridge Integration Most selection option formulas call a service bridge to fetch options from an external system: ```yaml theme={null} - name: subcategory label: Subcategory taxonType: SELECTION useSelectionOptionFormula: true selectionOptionFormula: | serviceBridgeCall("myorg/reference-data", "get-subcategories", { category: getAttribute("category"), active: true }) ``` The `serviceBridgeCall()` function is a convenience wrapper available in formula contexts. For the full service bridge API, see [Calling Service Bridges from Scripts](/guides/scripting/service-bridges). *** ## Grid Child Formulas When a SELECTION data element is a child of a group (grid row), the formula evaluates per data object instance. Each row can have different dropdown options based on its own attribute values: ```yaml theme={null} - name: line_items label: Line Items group: true children: - name: country label: Country taxonType: STRING - name: region label: Region taxonType: SELECTION useSelectionOptionFormula: true selectionOptionFormula: | serviceBridgeCall("myorg/geo-data", "regions-by-country", { country: getAttribute("country") }) ``` In this example, each line item row gets its own region dropdown based on its own country value. *** ## Examples Fetch a static reference list from an external system on document open. ```yaml theme={null} - name: currency label: Currency taxonType: SELECTION useSelectionOptionFormula: true selectionOptionFormula: | serviceBridgeCall("myorg/reference-data", "get-currencies", {}) ``` Filter team members based on a selected department. When the user changes the department, the contact dropdown updates automatically. ```yaml theme={null} - name: team_lead label: Team Lead taxonType: SELECTION useSelectionOptionFormula: true selectionOptionFormula: | serviceBridgeCall("myorg/directory", "get-team-members", { departmentId: getAttribute("department_id") }) ``` Use multiple attributes as dependencies and provide a fallback when the external call returns nothing. ```yaml theme={null} - name: priority_level label: Priority Level taxonType: SELECTION useSelectionOptionFormula: true selectionOptionFormula: | var amount = getAttribute("total_amount"); var region = getAttribute("region"); if (!amount || !region) return []; return serviceBridgeCall("myorg/rules-engine", "get-priority-levels", { amount: amount, region: region }) || [{ label: "Standard", value: "STD" }]; ``` *** ## Troubleshooting **Empty dropdown** Check that the service bridge is configured and the endpoint returns data. Use `log.debug(...)` in script steps to test the bridge response. **Options don't update** Verify that `useSelectionOptionFormula` is `true`. Check that the attribute names in `getAttribute()` match the data element names exactly. **Timeout errors** Selection option formulas have a 2-second timeout. If your external API is slow, consider caching results in the service bridge configuration. **Stale options after page reload** Options are persisted on the DataObject. If the external system's data changed, trigger a recalculation by editing one of the dependent attributes. # Data Definition Concepts Source: https://developer.kodexa.ai/guides/data-definitions/taxonomy-concepts Understand Data Definitions in Kodexa and how they model the structured data, groups, validation rules, and extraction targets for document-heavy workflows. Data Definitions describe the structured information Kodexa should extract, validate, review, and use in downstream Activity Plans. They turn a business document into a clear data model: the fields you care about, the groups those fields belong to, the types those fields should normalize into, and the rules that determine whether the extracted data is ready to use. In some APIs, SDKs, and configuration files, Data Definitions are still represented by the historical terms `taxonomy` and `taxon`. In user-facing documentation, think **Data Definition** for the overall model and **Data Element** for each field or group inside it. ## What Is a Data Definition? A Data Definition is a hierarchy of data elements. Each element represents either: * A piece of data to extract, validate, calculate, or review * A group that organizes related data elements * A repeating group, such as invoice line items or contract parties This hierarchy becomes the shared model used by extraction, validation, review forms, Activity steps, and downstream systems. ### Example Structure ```text theme={null} Invoice (Data Definition) ├── Invoice Number (data element) ├── Invoice Date (data element) ├── Vendor (group) │ ├── Name (data element) │ ├── Address (data element) │ └── Tax ID (data element) └── Line Items (repeating group) ├── Description (data element) ├── Quantity (data element) ├── Unit Price (data element) └── Total (calculated element) ``` *** ## Data Definition Roles Most business users think about Data Definitions as the final data they want from a document. Kodexa also uses Data Definition structures during processing so modules, Activity steps, and model outputs can share the same vocabulary. Defines the business-level data extracted from documents. This is the main model used for final output, review, validation, and downstream integrations. **Use for**: Business data extraction, final output structure Supports intermediate processing work. These structures can be provided by modules or Activity Plan steps and become available when those resources are bound into a project. **Use for**: Intermediate labels, routing signals, AI model support Comes from modules used for training or inference. These structures become available when you add a module to a project or reference it from an Activity Plan. **Use for**: ML module training, module-specific labels *** ## Key Concepts ### Data Elements In configuration, data elements are written under the API field `taxons`. Each element can be a simple field, a group, or a repeating group. **Simple Data Element**: ```yaml theme={null} taxons: - name: invoice_number label: Invoice Number taxonType: STRING ``` **Group Container**: ```yaml theme={null} taxons: - name: vendor label: Vendor Information group: true children: - name: name - name: address ``` **Repeating Group**: ```yaml theme={null} taxons: - name: line_items label: Line Items group: true allowsMultipleEntries: true children: - name: description - name: quantity ``` ### Hierarchy and Relationships Data Definitions use parent-child relationships to organize data: * **Root elements**: Top-level fields or groups * **Child elements**: Fields nested inside a parent group * **Sibling elements**: Fields at the same level in the model This structure helps Kodexa: * Organize related data logically * Mirror the way information appears in documents * Improve extraction and review accuracy * Produce output that downstream systems can understand *** ## Data Definition Lifecycle ### 1. Design Phase Define the model from the business problem: * What documents are involved? * What data must be extracted? * Which fields repeat? * Which values need review or validation? * Which downstream systems will consume the output? ### 2. Configuration Phase Set properties for each data element: * Data type * Value source * Semantic definition * Validation rules * Conditional formatting * Event-based scripts ### 3. Training and Testing Phase Use the Data Definition to: * Label representative documents * Train or evaluate extraction models * Refine semantic definitions * Test validation and review behavior ### 4. Production Phase Use the Data Definition in live workflows to: * Extract structured data from new documents * Validate extracted values * Present data to reviewers through Data Forms * Feed Activity Plan steps and downstream integrations *** ## Common Use Cases ### Invoice Processing ```yaml theme={null} taxons: - name: header group: true children: - name: invoice_number - name: invoice_date - name: due_date - name: vendor group: true children: - name: name - name: address - name: line_items group: true children: - name: description - name: quantity - name: unit_price ``` ### Contract Metadata ```yaml theme={null} taxons: - name: contract_type taxonType: SELECTION - name: parties group: true children: - name: party_a - name: party_b - name: key_dates group: true children: - name: effective_date - name: expiration_date ``` ### Form Data ```yaml theme={null} taxons: - name: applicant group: true children: - name: full_name - name: email - name: phone - name: application_details group: true children: - name: application_type - name: submission_date ``` *** ## Best Practices Create Data Definitions that can be reused across similar document types: * Use business names that make sense across teams * Factor out common structures * Keep repeated document patterns consistent Start with the fields that drive the workflow: * Begin with core data elements * Add groups only where they improve clarity * Introduce validation incrementally Align the hierarchy with how users understand the document: * Match visual organization where it helps * Follow natural reading order * Group related information Choose clear, stable names: * Use business terminology * Be specific and unambiguous * Follow consistent naming conventions *** ## Learn More Configure data elements, groups, value sources, and extraction behavior Detailed information about available data types and normalization Generate Python data classes from Data Definitions for programmatic access Overall Data Definition concepts and patterns *** ## Next Steps Read [Data Definition Structure](/guides/data-definitions/taxonomy-guide) for detailed configuration instructions. Check out [Data Definition examples](/guides/data-definitions/examples/invoice) for common document types. Create your first Data Definition in a Kodexa project and test it with sample documents. Use extraction results to improve semantic definitions, validation rules, and review behavior. # Data Definition Structure Source: https://developer.kodexa.ai/guides/data-definitions/taxonomy-guide Define and configure Kodexa Data Definitions for document extraction, including data elements, types, validation, formatting, and extraction logic. ## Overview Data definitions are the foundation of data extraction in Kodexa. They define the hierarchical structure of data elements you want to extract from documents, along with their types, validation rules, and extraction logic. ### What is a Data Definition? A **Data Definition** is a hierarchical structure of data elements. In configuration and API payloads, those elements are represented as `taxons`. A Data Definition defines: * **What data** to extract from documents * **Where** the data comes from (document content, metadata, formulas) * **How** to validate and format the data * **What type** of data it is (string, date, currency, etc.) ### Key Concepts Top-level container defining the complete data structure for extraction Individual field or group within a Data Definition Organizational container that groups related data elements without storing data itself Defines where the data element gets its value from (document, metadata, formula, etc.) *** ## Data Definition Structure ### Top-Level Configuration Every data definition has these core properties: ```yaml theme={null} slug: invoice-data name: Invoice Data Extraction description: Extract structured data from invoices taxonomyType: CONTENT enabled: true taxons: - name: vendor_information label: Vendor Information # ... data element configuration ``` #### Data Definition Properties | Property | Type | Default | Description | | -------------------------- | --------- | --------- | -------------------------------------------- | | `slug` | string | - | Unique identifier for the data definition | | `name` | string | - | Display name | | `description` | string | - | Description of the data definition's purpose | | `taxonomyType` | enum | `CONTENT` | Type of data definition (typically CONTENT) | | `enabled` | boolean | `true` | Whether the data definition is active | | `externalDataTaxonomyRefs` | string\[] | `[]` | References to external data definitions | | `taxons` | Taxon\[] | `[]` | Array of root-level data elements | *** ## Data Element Configuration Data elements are the individual fields and groups within a Data Definition. In configuration, each one is a `taxon` with extensive options organized into several categories. ### Basic Properties Every data element requires these fundamental properties: ```yaml theme={null} id: "auto-generated-uuid" name: vendor_name label: Vendor Name description: The name of the vendor or supplier enabled: true color: "#4F46E5" ``` Internal identifier (alphanumeric, hyphens, underscores only) Human-readable display name Detailed explanation of what this data element represents Whether this data element is active (disabled elements cascade to children) Hex color code for UI display (auto-generated if not specified) Auto-generate the internal `name` from the `label` Name used when publishing to external systems (auto-generated from label if not specified) *** ### Data Source (Value Path) The `valuePath` determines where the data element gets its value from: Extracts data directly from document content using AI/ML models or pattern matching. **When to use**: Standard document extraction (invoices, contracts, forms) **Configuration**: ```yaml theme={null} valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "Extract the vendor's business name as it appears on the invoice" ``` **Features**: * Uses semantic definition as extraction prompt * Can leverage document structure and layout * Supports AI-assisted extraction Pulls data from document metadata (filename, creation date, owner, etc.). **When to use**: Document properties, system fields, audit trail **Configuration**: ```yaml theme={null} valuePath: METADATA metadataValue: FILENAME # or CREATED_DATETIME, OWNER_NAME, etc. ``` **Available metadata values**: * `FILENAME` - Document filename * `TRANSACTION_UUID` - Unique transaction identifier * `CREATED_DATETIME` - Document creation timestamp * `DOCUMENT_LABELS` - Applied labels * `OWNER_NAME` - Document owner * `DOCUMENT_STATUS` - Processing status * `PAGE_NUMBER` - Current page number Calculates values using formulas that reference other data elements. **When to use**: Computed fields, calculations, aggregations **Configuration**: ```yaml theme={null} valuePath: FORMULA semanticDefinition: | sum({line_items/amount}) ``` **Features**: * Reference other data elements with `{field_name}` or `{group/field_name}` * Built-in functions such as `sum`, `average`, `if`, `isblank`, and `datemath` * Conditional logic support Generates review templates using Jinja2 templating. **When to use**: Human review interfaces, validation checklists **Configuration**: ```yaml theme={null} valuePath: REVIEW semanticDefinition: | ## Review Checklist - [ ] Vendor name matches PO: {{ vendor_name }} - [ ] Total amount is correct: {{ total_amount }} - [ ] All line items present: {{ line_items|length }} items ``` Placeholder for derived values (less common, use FORMULA instead). *** ### Data Types The `taxonType` defines how the data should be treated and validated: ```yaml theme={null} taxonType: STRING typeFeatures: longText: true # Multi-line text field maxTextRows: 10 # Maximum rows for display markdown: true # Enable markdown formatting expected: true # Field is expected to be present stringExtract: '\d' # Keep only matching characters (regex) stringReplace: '[-\s]' # Remove matching characters (regex) ``` **Use for**: Names, addresses, descriptions, any text content Use `stringExtract` and `stringReplace` to automatically clean extracted values. See [String Filters](#string-filters) below. ```yaml theme={null} taxonType: NUMBER typeFeatures: truncateDecimal: true # Round to fixed decimal places decimalPlaces: 2 # Number of decimal places ``` **Use for**: Quantities, counts, measurements ```yaml theme={null} taxonType: CURRENCY typeFeatures: preferTwoDecimalPlaces: true # Assume last 2 digits are decimal (1234 → 12.34) ``` **Use for**: Prices, totals, monetary amounts ```yaml theme={null} taxonType: DATE typeFeatures: normalizeDate: true # Normalize for display normalizeDateInExport: true # Normalize in exports dateFormat: "yyyy-MM-dd" # Target format ``` **Use for**: Invoice dates, due dates, any date without time ```yaml theme={null} taxonType: DATE_TIME typeFeatures: normalizeDate: true dateFormat: "yyyy-MM-dd HH:mm:ss" ``` **Use for**: Timestamps, creation dates with time SELECTION data elements present users with a dropdown of predefined options and guide AI extraction toward valid categorical values. #### Basic Example ```yaml theme={null} taxonType: SELECTION selectionOptions: - label: "Net 30" id: "net_30" description: "Payment due in 30 days" - label: "Net 60" id: "net_60" description: "Payment due in 60 days" ``` **Use for**: Dropdown selections, categorical data, classification #### Selection Option Properties Each item in `selectionOptions` supports these properties: | Property | Type | Description | | -------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `label` | string | **Required.** Display text shown to the user in the dropdown | | `id` | string | Unique identifier for the option (auto-generated if omitted) | | `value` | string | The value stored when this option is selected. Defaults to `label` if empty. Use this to separate display text from stored codes (e.g., label "Net 30" with value "NET\_30") | | `description` | string | Description text shown alongside the option | | `hint` | string | Additional help text displayed with the option | | `hintMarkdown` | boolean | When `true`, renders the `hint` as Markdown instead of plain text | | `disabled` | string | Set to `"true"` to disable the option. Disabled options are excluded from AI extraction requests but remain visible (struck through) in the UI. Useful for deprecating options without breaking existing data | | `isConditional` | boolean | Enables conditional visibility for this option | | `conditionalFormula` | string | Formula evaluated per data object to determine if this option appears. Only used when `isConditional` is `true` | | `lexicalRelations` | array | Semantic relationships that help AI/ML models understand option equivalences. See [Lexical Relations](#lexical-relations-on-selection-options) below | #### Value vs Label When `value` is set, the UI displays the `label` but stores the `value`. This is useful when you need human-readable display text but machine-friendly stored values: ```yaml theme={null} selectionOptions: - label: "United States Dollar" value: "USD" id: "currency_usd" - label: "Euro" value: "EUR" id: "currency_eur" ``` #### Hints Hints provide contextual help for individual options. Enable Markdown rendering for rich formatting: ```yaml theme={null} selectionOptions: - label: "Net 30" id: "net_30" hint: "Standard payment terms — **30 calendar days** from invoice date" hintMarkdown: true - label: "Due on Receipt" id: "due_receipt" hint: "Payment expected immediately upon receipt of invoice" ``` #### Disabled Options Disabled options are excluded from AI extraction prompts (so the model won't extract them from new documents) but remain visible in the UI for historical data: ```yaml theme={null} selectionOptions: - label: "Net 30" id: "net_30" disabled: "" # Active — included in AI requests - label: "Net 15" id: "net_15" disabled: "true" # Deprecated — excluded from AI requests, shown struck-through ``` The `disabled` field is a string, not a boolean. Use `"true"` to disable and `""` (empty string) or omit for enabled. #### Conditional Options Show or hide individual options based on the current data object's context using `isConditional` and `conditionalFormula`: ```yaml theme={null} selectionOptions: - label: "Standard" id: "priority_standard" - label: "Rush" id: "priority_rush" isConditional: true conditionalFormula: "{total_amount} > 10000" - label: "Emergency" id: "priority_emergency" isConditional: true conditionalFormula: "{total_amount} > 50000 && {is_critical} = true" ``` The `conditionalFormula` is evaluated against the current data object at runtime. Options where the formula evaluates to `false` are hidden from the dropdown. Options without `isConditional` always appear. #### Lexical Relations on Selection Options Lexical relations help AI models understand synonyms and related terms for each option, improving extraction accuracy when documents use varied terminology: ```yaml theme={null} selectionOptions: - label: "Net 30" id: "net_30" description: "Payment due in 30 days" lexicalRelations: - type: SYNONYM value: "30 days, thirty days, N30" - type: SIMILAR_TO value: "monthly payment" weight: 0.7 - label: "Due on Receipt" id: "due_receipt" lexicalRelations: - type: SYNONYM value: "immediate payment, payable on receipt, COD" - type: ANTONYM value: "deferred payment" ``` **Supported relation types**: `SYNONYM`, `ANTONYM`, `HYPERNYM` (more general), `HYPONYM` (more specific), `MERONYM` (part-of), `HOLONYM` (whole-of), `ENTAILMENT`, `SIMILAR_TO`, `OTHER` Each relation has: * `type` — The relationship type from the list above * `value` — The related term(s). Can be comma-separated for multiple terms * `weight` — Optional confidence/importance weight (0.0–1.0) #### Dynamic Options with Formulas Instead of a static list, you can compute selection options dynamically using a JavaScript formula. Enable this on the data element: ```yaml theme={null} taxonType: SELECTION useSelectionOptionFormula: true selectionOptionFormula: | serviceBridgeCall("myorg/reference-data", "get-currencies", { country: getAttribute("country_code") }) ``` The formula is evaluated by the [Goja scripting runtime](/guides/scripting) and must return an array of `{label, value}` objects or plain strings. Options re-evaluate automatically when referenced attributes change, and the results are persisted on the data object so they survive page reloads. For the full guide on dynamic selection options — including service bridge integration, grid child formulas, dependency tracking, and troubleshooting — see [Selection Option Formulas](/guides/data-definitions/selection-option-formulas). #### Complete Example ```yaml theme={null} - name: payment_terms label: Payment Terms taxonType: SELECTION semanticDefinition: "The payment terms specified on the invoice" selectionOptions: - label: "Net 10" value: "NET_10" id: "terms_net10" description: "Payment due in 10 days" lexicalRelations: - type: SYNONYM value: "10 days, N10" - label: "Net 30" value: "NET_30" id: "terms_net30" description: "Payment due in 30 days" hint: "Most common payment term for B2B invoices" lexicalRelations: - type: SYNONYM value: "30 days, N30, thirty days" - label: "Net 60" value: "NET_60" id: "terms_net60" description: "Payment due in 60 days" isConditional: true conditionalFormula: "total_amount > 10000" lexicalRelations: - type: SYNONYM value: "60 days, N60" - label: "Due on Receipt" value: "DUE_ON_RECEIPT" id: "terms_receipt" description: "Payment expected immediately" lexicalRelations: - type: SYNONYM value: "immediate, COD, cash on delivery, payable on receipt" - label: "2/10 Net 30" value: "2_10_NET_30" id: "terms_2_10_net30" description: "2% discount if paid within 10 days, otherwise net 30" hint: "Early payment discount — **2% off** if paid within 10 days" hintMarkdown: true disabled: "true" ``` ```yaml theme={null} taxonType: BOOLEAN ``` **Use for**: Yes/no fields, flags, checkboxes Additional specialized types: * `URL` - Website addresses * `EMAIL_ADDRESS` - Email addresses * `PHONE_NUMBER` - Phone numbers * `PERCENTAGE` - Percentage values * `SECTION` - Visual grouping (no data storage) *** ### Data Groups and Hierarchies Groups organize related data elements and can represent repeating structures: ```yaml theme={null} name: line_items label: Line Items group: true # This is a group, not a value children: - name: description label: Description taxonType: STRING - name: quantity label: Quantity taxonType: NUMBER - name: unit_price label: Unit Price taxonType: CURRENCY - name: total label: Total taxonType: CURRENCY valuePath: FORMULA semanticDefinition: "quantity * unit_price" ``` #### Group Configuration Mark as a group (container for other data elements) Array of child data elements nested under this group Define how many instances of this group can exist: ```yaml theme={null} cardinality: min: 1 # Minimum required instances max: 100 # Maximum allowed instances ``` Define unique identifiers for group instances: ```yaml theme={null} naturalKeys: - taxonRef: "invoice_number" - taxonRef: "line_number" ``` Attach reactive JavaScript scripts to a group data element. Event subscriptions can derive values, enforce business rules, call Service Bridges, create data exceptions, or emit follow-up events when modeled data changes. ```yaml theme={null} eventSubscriptions: - name: derive-total on: "changed:dataAttribute:(quantity|unit_price)" script: | if (!currentObject) return; var qty = currentObject.getFirstAttributeValue("quantity"); var price = currentObject.getFirstAttributeValue("unit_price"); if (qty && price) { currentObject.setAttribute("line_total", qty * price); } ``` For the full runtime guide, including the JavaScript objects available to scripts, see [Event-Based Scripting](/guides/data-definitions/event-subscriptions). *** ### Validation Rules Define business rules and data quality checks on the data element they apply to: ```yaml theme={null} validationRules: - name: "Total matches sum of line items" description: "Ensure calculated total matches the invoice total" disabled: false conditional: false ruleFormula: | abs({total_amount} - sum({line_items/total})) < 0.01 messageFormula: | concat( "Total mismatch: Invoice shows ", {total_amount}, " but line items sum to ", sum({line_items/total}) ) detailFormula: | "Check line item totals and invoice-level charges." overridable: true exceptionId: TOTAL_MISMATCH supportArticleId: "9117988" - name: "Due date after invoice date" conditional: true conditionalFormula: "!isblank({due_date}) && !isblank({invoice_date})" ruleFormula: | isafterdate({due_date}, {invoice_date}) || {due_date} = {invoice_date} messageFormula: | "Due date must be after invoice date" overridable: false ``` #### Validation Rule Properties | Property | Type | Description | | -------------------- | ------- | ------------------------------------------------------ | | `name` | string | Rule name | | `description` | string | Detailed explanation | | `disabled` | boolean | Temporarily disable this rule | | `conditional` | boolean | Only apply if condition is true | | `conditionalFormula` | string | Formula determining if rule applies | | `ruleFormula` | string | Formula that must be true (false = validation failure) | | `messageFormula` | string | Formula generating the error message | | `detailFormula` | string | Formula generating additional details | | `overridable` | boolean | Can users override this validation? | | `exceptionId` | string | Unique exception identifier | | `supportArticleId` | string | Link to help documentation | Read the complete guide for rule placement, exception lifecycle, conditional formatting schema, and the formula language. *** ### Conditional Formatting Apply visual formatting based on data values: ```yaml theme={null} conditionalFormats: - type: backgroundColor condition: "isbeforedate({due_date}, datemath('today')) && {status} != 'PAID'" properties: color: "#FEE2E2" - type: textColor condition: "isbeforedate({due_date}, datemath('today')) && {status} != 'PAID'" properties: color: "#991B1B" - type: icon condition: "{total_amount} > 10000" properties: icon: alert-circle-outline color: "#92400E" ``` *** ### Classification Features Help AI/ML models understand and classify content: Provides guidance for AI extraction: ```yaml theme={null} semanticDefinition: | The vendor's legal business name as registered with tax authorities. Look for names near "Bill To", "Vendor", or "From" sections. Should be a proper business name, not an individual's name. ``` **Best practices**: * Be specific about what to look for * Describe location hints * Clarify edge cases * Provide examples if helpful Helps with record-based chunking and classification: ```yaml theme={null} additionContexts: - type: RECORD_DEFINITION context: | Each line item represents a product or service being billed. Line items typically appear in a table format. - type: RECORD_START_MARKER context: "Item #" - type: RECORD_END_MARKER context: "Subtotal" ``` **Context types**: * `RECORD_DEFINITION` - Describes the record structure * `RECORD_START_MARKER` - Text indicating record start * `RECORD_END_MARKER` - Text indicating record end * `RECORD_SECTION_STARTER_MARKER` - Section start marker * `RECORD_SECTION_END_MARKER` - Section end marker Synonyms and antonyms for embedding-based classification: ```yaml theme={null} lexicalRelations: - type: SYNONYM value: "Supplier, Provider, Seller, Merchant" - type: ANTONYM value: "Customer, Buyer, Client" ``` **Use for**: * Improving classification accuracy * Handling terminology variations * Training embedding models *** ### Advanced Options #### Chunking Strategy `chunkingStrategy` controls how a document is divided into segments before extraction. It is set on **group** taxons (typically the root taxon) and is a **spatial-only** selector — it applies to spatial documents (and legacy callers that do not specify a document mixin). Setting it on a non-spatial document is a misconfiguration that the extraction resolvers reject. ```yaml theme={null} name: invoice taxonType: GROUP typeFeatures: chunkingStrategy: document # Treat the whole document as one chunk ``` **Valid values**: | Value | Effect | | ------------------------ | ---------------------------------------------------------------------------------- | | `document` | The whole document becomes a single chunk | | `page` | One chunk per page | | `firstNPages` | Chunk from the first N pages only | | `classifiedContent` | Chunk from content classified for this taxon | | `pageClassifiedContent` | Page-level chunking over classified content | | `groupClassifiedContent` | Group-level chunking over classified content | | `record` | Record-based chunking (see [Additional Context](#classification-features) markers) | **Defaults for spatial / legacy group taxons**: If a group taxon does not explicitly set `chunkingStrategy`, one is filled in automatically (existing values are never overridden): * If the planner has marked the group as non-embedded (its own chunk context), it defaults to `classifiedContent`. * Otherwise — including an unplanned or unconfigured taxonomy — it defaults to `document`, so the whole document is extracted as a single chunk. Because these defaults apply only to group taxons on spatial (or legacy) documents, a typical spatial taxonomy extracts without needing to set `chunkingStrategy` at all. Set it explicitly when you want a different segmentation than the default. If no enabled root taxon declares a `chunkingStrategy` and none is filled in by default (for example, a spatial taxonomy whose root taxons are not group taxons, so the automatic `document` default never applies), the extraction produces zero chunks and no data. This is a valid outcome for a blank taxonomy, so it is not an error — the extraction logs a warning instead. See [Extraction produces no data](#troubleshooting) if you expected output. #### Display Configuration Control how fields appear in the UI: ```yaml theme={null} typeFeatures: overrideWidth: true displayWidth: 300 # Width in pixels expected: true # Mark as required field showFullOnHover: true # Show the full cell value as a grid hover tooltip ``` When `showFullOnHover` is set, hovering over the field's cell in a data grid shows its complete value in a tooltip. The tooltip appears for **any non-empty cell** — it is not gated on whether the displayed text is visually truncated — so it is best reserved for prose-length fields such as AI explanations, notes, or comments, where the full value is often longer than the column. Empty cells show no tooltip. This flag composes with `longText` and `maxTextRows`, which control how the field renders when opened for editing. #### String Filters Automatically clean extracted values using regex patterns. **Extract** keeps only matching characters, **Replace** removes matching characters. If both are set, extract runs first. The original value is preserved separately. ```yaml theme={null} typeFeatures: stringExtract: '\d' # Keep only digits stringReplace: '[^a-zA-Z0-9]' # Remove non-alphanumeric characters ``` | Pattern | Effect | | --------------- | ------------------------------------------------------------------- | | `\d` | Keep digits only (use with `stringExtract`) | | `[a-zA-Z]` | Keep letters only (use with `stringExtract`) | | `[-\s]` | Strip dashes and spaces (use with `stringReplace`) | | `[^a-zA-Z0-9]` | Strip all non-alphanumeric (use with `stringReplace`) | | `[^a-zA-Z0-9 ]` | Strip special characters but keep spaces (use with `stringReplace`) | **Pro number cleanup** — remove special characters: ```yaml theme={null} name: pronumber taxonType: STRING typeFeatures: stringReplace: '[^a-zA-Z0-9 ]' ``` **Phone number — digits only:** ```yaml theme={null} name: phone taxonType: STRING typeFeatures: stringExtract: '\d' ``` **ID field — strip dashes and spaces:** ```yaml theme={null} name: tracking_id taxonType: STRING typeFeatures: stringReplace: '[-\s.]' ``` **Combined — extract digits then strip leading zeros:** ```yaml theme={null} name: account_number taxonType: STRING typeFeatures: stringExtract: '\d' stringReplace: '^0+' ``` #### User Interaction ```yaml theme={null} multiValue: true # Allow multiple values userEditable: true # User can edit in forms notUserLabelled: false # Show in labeling interface nullable: true # Allow null values nullValue: "N/A" # Display text for null ``` *** ## Common Patterns ### Invoice Extraction Complete example of a typical invoice data definition: ```yaml theme={null} slug: invoice-extraction name: Invoice Data Extraction taxonomyType: CONTENT enabled: true taxons: # Header Information - name: invoice_number label: Invoice Number taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The unique invoice number, typically at the top right" validationRules: - name: "Invoice number required" ruleFormula: "!isblank({invoice_number})" messageFormula: '"Invoice number is required"' overridable: false - name: invoice_date label: Invoice Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The date the invoice was issued" typeFeatures: normalizeDate: true dateFormat: "yyyy-MM-dd" - name: due_date label: Due Date taxonType: DATE valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The payment due date" # Vendor Information Group - name: vendor label: Vendor group: true children: - name: name label: Vendor Name taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT semanticDefinition: "The vendor's business name" - name: address label: Address taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT typeFeatures: longText: true - name: tax_id label: Tax ID taxonType: STRING valuePath: VALUE_OR_ALL_CONTENT # Line Items (Repeating Group) - name: line_items label: Line Items group: true children: - name: description label: Description taxonType: STRING - name: quantity label: Quantity taxonType: NUMBER - name: unit_price label: Unit Price taxonType: CURRENCY - name: line_total label: Line Total taxonType: CURRENCY valuePath: FORMULA semanticDefinition: "{quantity} * {unit_price}" # Totals - name: subtotal label: Subtotal taxonType: CURRENCY valuePath: FORMULA semanticDefinition: "sum({line_items/line_total})" - name: tax_amount label: Tax Amount taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT - name: total_amount label: Total Amount taxonType: CURRENCY valuePath: VALUE_OR_ALL_CONTENT validationRules: - name: "Total calculation check" ruleFormula: "abs({total_amount} - ({subtotal} + ifnull({tax_amount}, 0))) < 0.01" messageFormula: '"Total amount mismatch"' overridable: true ``` ### Contract Data Extraction ```yaml theme={null} slug: contract-extraction name: Contract Data Extraction taxons: - name: contract_metadata label: Contract Metadata group: true children: - name: contract_number label: Contract Number taxonType: STRING - name: contract_type label: Contract Type taxonType: SELECTION selectionOptions: - label: "Service Agreement" id: "service" - label: "Purchase Agreement" id: "purchase" - label: "NDA" id: "nda" - label: "License Agreement" id: "license" - name: parties label: Parties group: true children: - name: party_a label: Party A taxonType: STRING - name: party_b label: Party B taxonType: STRING - name: key_terms label: Key Terms group: true children: - name: effective_date label: Effective Date taxonType: DATE - name: term_length label: Term Length taxonType: STRING semanticDefinition: "Duration of the contract (e.g., '12 months', '2 years')" - name: auto_renewal label: Auto Renewal taxonType: BOOLEAN semanticDefinition: "Does the contract automatically renew?" - name: termination_notice label: Termination Notice Period taxonType: STRING semanticDefinition: "Required notice period for termination (e.g., '30 days')" - name: financial_terms label: Financial Terms group: true children: - name: total_value label: Total Contract Value taxonType: CURRENCY - name: payment_terms label: Payment Terms taxonType: STRING semanticDefinition: "Payment schedule and terms (e.g., 'Net 30', 'Monthly in advance')" ``` *** ## Best Practices ### Naming Conventions ```yaml Good theme={null} name: vendor_name # Snake case for internal names label: Vendor Name # Title case for display externalName: vendorName # Camel case for APIs ``` ```yaml Avoid theme={null} name: VendorName # Don't use capitals in internal names label: vendor_name # Don't use technical names for display ``` ### Semantic Definitions Write semantic definitions as if explaining to a human what to look for. Be specific about: * What the field represents * Where it typically appears * How to identify it * Edge cases to consider **Good example**: ```yaml theme={null} semanticDefinition: | The total amount due on the invoice, including all taxes and fees. Look for labels like "Total", "Amount Due", "Balance Due", or "Total Amount". This should be the final bottom-line number, not a subtotal. If multiple totals exist (e.g., by currency), extract the primary total. ``` **Avoid**: ```yaml theme={null} semanticDefinition: "The total" # Too vague ``` ### Group Structures ```yaml Repeating Data (Use Groups) theme={null} # Good: Line items are a repeating group - name: line_items label: Line Items group: true children: - name: description - name: quantity - name: price ``` ```yaml Single Instances (Use Groups for Organization) theme={null} # Good: Vendor info as an organizational group - name: vendor label: Vendor Information group: true children: - name: name - name: address - name: tax_id ``` ### Validation Strategy 1. **Start Simple**: Begin with basic "not empty" validations 2. **Add Business Rules**: Implement domain-specific validations 3. **Make Critical Rules Non-Overridable**: Block processing if essential data is wrong 4. **Allow Overrides for Quality Checks**: Let users override formatting or minor issues ```yaml theme={null} validationRules: # Critical: Don't allow override - name: "Invoice number required" ruleFormula: "!isblank({invoice_number})" overridable: false # Quality check: Allow override - name: "Total seems high" ruleFormula: "{total_amount} < 100000" messageFormula: '"Invoice total exceeds $100,000 - please verify"' overridable: true ``` ### Formula Usage ```yaml theme={null} semanticDefinition: "{quantity} * {unit_price}" ``` ```yaml theme={null} semanticDefinition: "sum({line_items/total})" ``` ```yaml theme={null} semanticDefinition: | if({total_amount} > 10000, "Requires Approval", "Auto-Approve") ``` ```yaml theme={null} semanticDefinition: "datemath({invoice_date}, 'days', 30)" ``` *** ## Troubleshooting ### Common Issues **Possible causes**: * `enabled: false` is set * Parent data element is disabled (disabling cascades to children) * `notUserLabelled: true` for labeling interfaces **Solution**: Check enabled status up the hierarchy **Check**: 1. Is `valuePath` correct for your use case? 2. Is `semanticDefinition` clear and specific? 3. Are you using the right `taxonType`? 4. Is the model trained for this document type? **Common mistakes**: * Referencing data elements that don't exist * Syntax errors in formula * Circular references **Test**: Use formula builder to validate syntax **Check**: * Is validation rule `disabled: false`? * Does `conditionalFormula` evaluate to true? * Is `ruleFormula` returning the expected boolean? *** ## Next Steps Reference guide for all supported data types Complete formula function reference Compute dropdown options dynamically using JavaScript and service bridges Attach reactive JavaScript behavior to group data elements Complete API reference for Kodexa JavaScript scripting Common validation rule and conditional formatting patterns # Validation and Conditional Formatting Source: https://developer.kodexa.ai/guides/data-definitions/validation-and-conditional-formatting Define validation rules and conditional formatting on Data Definition elements, using Kodexa formulas to evaluate business rules against document data. Validation rules and conditional formats are configured on **data elements** in a Data Definition. In configuration, those elements are stored under `taxons`. At runtime Kodexa applies those rules to the matching data objects and data attributes in each document. Use validation rules when the document data must satisfy a business rule. Use conditional formatting when reviewers need a visual cue while reviewing data. Both use the same formula language. You do not add `validationRules` or `conditionalFormats` directly to a stored data object or data attribute. You add them to the Data Definition element that models that object or attribute. Kodexa evaluates the rule wherever that element appears in document data. ## Where Rules Live Rules can be attached at two levels: | Level | Attach to | Use for | | -------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | Data attribute | A field element such as `invoice_number`, `due_date`, or `total_amount` | Required fields, field-level ranges, confidence checks, and conditional formatting on a form control | | Data object | A group element such as `invoice`, `vendor`, or `line_items` | Cross-field checks where the rule describes the whole object | Attribute-level rules are the most common. They bind cleanly to a field in the review UI and can create exceptions against a specific data attribute. Object-level rules are useful when the business rule is about the row or section rather than a single field. For example, an `invoice` object can validate that `subtotal + tax_amount` matches `total_amount`. ```yaml Attribute-level rule theme={null} taxons: - name: invoice group: true children: - name: invoice_number label: Invoice Number taxonType: STRING validationRules: - name: Invoice number required ruleFormula: "!isblank({invoice_number})" messageFormula: '"Invoice number is required"' exceptionId: INVOICE_NUMBER_REQUIRED overridable: false ``` ```yaml Object-level rule theme={null} taxons: - name: invoice group: true validationRules: - name: Invoice total matches components ruleFormula: | abs({total_amount} - ( ifnull({subtotal}, 0) + ifnull({tax_amount}, 0) + ifnull({shipping_amount}, 0) )) < 0.01 messageFormula: | concat( "Invoice total does not match the calculated total. Total: ", {total_amount} ) exceptionId: INVOICE_TOTAL_MISMATCH overridable: true children: - name: subtotal taxonType: CURRENCY - name: tax_amount taxonType: CURRENCY - name: shipping_amount taxonType: CURRENCY - name: total_amount taxonType: CURRENCY ``` ## Validation Rules Validation rules are stored in the `validationRules` array on a taxon. Each rule evaluates a formula. If the formula returns truthy, the data passes. If it returns falsy or errors, Kodexa treats the rule as failed. ```yaml theme={null} validationRules: - id: due-date-after-invoice-date name: Due date after invoice date description: The payment due date must not be earlier than the invoice date. disabled: false conditional: true conditionalFormula: "!isblank({due_date}) && !isblank({invoice_date})" ruleFormula: "isafterdate({due_date}, {invoice_date}) || {due_date} = {invoice_date}" messageFormula: '"Due date must be on or after the invoice date"' detailFormula: | concat("Invoice date: ", {invoice_date}, ". Due date: ", {due_date}, ".") exceptionId: DUE_DATE_BEFORE_INVOICE_DATE supportArticleId: "9117988" overridable: false ``` ### Rule Fields | Field | Type | Required | Behavior | | -------------------- | -------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `id` | string | No | Stable rule identifier in metadata. Use one when configuration is managed in Git. | | `name` | string | Recommended | Human-readable rule name shown in tooling and useful for derived exception keys. | | `description` | string | No | Explains why the rule exists. | | `disabled` | boolean | No | When `true`, Kodexa skips the rule. | | `conditional` | boolean | No | When `true`, Kodexa evaluates `conditionalFormula` before the rule. | | `conditionalFormula` | formula string | Required when `conditional` is `true` | If the formula is falsy or errors, Kodexa skips `ruleFormula`. | | `ruleFormula` | formula string | Yes | Must evaluate truthy for the data to pass. Empty formulas are skipped. | | `messageFormula` | formula string | Recommended | Builds the message for the exception when the rule fails. | | `detailFormula` | formula string | No | Builds additional details for the exception. | | `exceptionId` | string | Recommended | Stable exception type. If omitted, Kodexa derives a key from the taxon path and rule name. | | `supportArticleId` | string | No | Help article ID attached to the resulting exception. | | `overridable` | boolean | No | When `true`, a reviewer can override the resulting exception in the review workspace. Defaults to not overridable when omitted. | `overridable` and `supportArticleId` are carried onto every exception the rule raises, including on documents that were already processed, so a change to either reaches existing exceptions the next time the document is opened without a reprocess or a redeploy. When a reviewer overrides an exception it stays listed in a muted **Overridden by user** state rather than disappearing, and the override holds across later validation passes while the rule still fails. Only exceptions Kodexa itself closed are reopened when their rule fails again. ### Evaluation Flow 1. Kodexa finds the taxon for the changed data object or data attribute. 2. Disabled rules are skipped. 3. If `conditional` is `true` and `conditionalFormula` is present, Kodexa evaluates the condition. 4. If the condition is truthy, Kodexa evaluates `ruleFormula`. 5. Truthy results pass. Falsy results fail. 6. Formula errors are treated as failed validation and marked as evaluation errors so authors can diagnose bad rules. 7. Failing rules create or reopen data exceptions. Passing rules close matching open exceptions. Validation formulas are reactive. When a formula references `{subtotal}`, `{tax_amount}`, or another attribute, Kodexa tracks that dependency and re-evaluates the affected rule when the referenced value changes. ### Required Fields Use `isblank` for required field checks. Missing references resolve to `null`, so `isblank` handles missing, null, empty-string, and empty-list values. ```yaml theme={null} validationRules: - name: Vendor name required ruleFormula: "!isblank({vendor_name})" messageFormula: '"Vendor name is required"' exceptionId: VENDOR_NAME_REQUIRED overridable: false ``` ### Conditional Rules Set `conditional: true` when a rule should apply only for some records. ```yaml theme={null} validationRules: - name: PO number required for purchase-order invoices conditional: true conditionalFormula: "{invoice_type} = 'Purchase Order'" ruleFormula: "!isblank({purchase_order_number})" messageFormula: '"Purchase order number is required for PO invoices"' exceptionId: PO_NUMBER_REQUIRED overridable: false ``` If `conditional` is omitted or false, Kodexa evaluates `ruleFormula` regardless of `conditionalFormula`. ### Cross-Field Rules Attach cross-field rules to the field that should receive the exception, or to the group data element when the rule describes the full object. ```yaml theme={null} validationRules: - name: Line total matches quantity times unit price conditional: true conditionalFormula: | !isblank({quantity}) && !isblank({unit_price}) && !isblank({line_total}) ruleFormula: "abs({line_total} - ({quantity} * {unit_price})) < 0.01" messageFormula: | concat("Line total should be ", {quantity} * {unit_price}, " but was ", {line_total}) exceptionId: LINE_TOTAL_MISMATCH overridable: true ``` ### Repeating Groups When a formula references a child path that has multiple data objects, Kodexa returns an array of values. Math functions such as `sum`, `average`, `min`, and `max` can work across those arrays. ```yaml theme={null} validationRules: - name: Invoice total equals line item total ruleFormula: "abs({total_amount} - sum({line_items/line_total})) < 0.01" messageFormula: | concat( "Invoice total ", {total_amount}, " does not match line item total ", sum({line_items/line_total}) ) exceptionId: INVOICE_LINES_DO_NOT_SUM overridable: true ``` ### Formula Errors Formula errors are configuration problems. For example, an unknown function, invalid syntax, or unsupported comparison can cause the rule to fail with `evaluationErrored`. ```yaml Avoid theme={null} ruleFormula: "NOT_EMPTY({invoice_number})" ``` ```yaml Use theme={null} ruleFormula: "!isblank({invoice_number})" ``` The formula language is not SQL, JavaScript, or Excel. Use `&&` and `||` for logic, `=` for equality, `!` for not, and built-in functions such as `isblank`, `ifnull`, `datemath`, and `contains`. ## Conditional Formatting Conditional formats are stored in the `conditionalFormats` array on a taxon. Each format has: | Field | Type | Required | Behavior | | ------------ | -------------- | -------- | --------------------------------------------------------- | | `type` | string | Yes | Formatter type consumed by the review UI. | | `condition` | formula string | Yes | Formula that activates the format when truthy. | | `properties` | object | No | Formatter-specific properties, usually `color` or `icon`. | ```yaml theme={null} conditionalFormats: - type: backgroundColor condition: "isbeforedate({due_date}, datemath('today')) && {status} != 'PAID'" properties: color: "#FEE2E2" - type: textColor condition: "isbeforedate({due_date}, datemath('today')) && {status} != 'PAID'" properties: color: "#991B1B" - type: icon condition: "{total_amount} > 10000" properties: icon: alert-circle color: "#D97706" ``` ### Supported Formatter Types | Type | Properties | Result | | ----------------- | --------------- | ------------------------------------------------------- | | `backgroundColor` | `color` | Applies a background color to the attribute control. | | `textColor` | `color` | Applies a text color to the attribute control. | | `outlineColor` | `color` | Applies a colored outline around the attribute control. | | `icon` | `icon`, `color` | Displays a review icon for the attribute. | Conditional formatting is attribute-oriented. Add `conditionalFormats` to the field taxon whose control should be styled. ```yaml Attribute styling theme={null} taxons: - name: invoice group: true children: - name: total_amount label: Total Amount taxonType: CURRENCY conditionalFormats: - type: backgroundColor condition: "{total_amount} > 10000" properties: color: "#FEF3C7" - type: icon condition: "{total_amount} > 10000" properties: icon: alert-circle-outline color: "#92400E" ``` Older examples may use `formula`, `backgroundColor`, `textColor`, or `icon` at the top level of a conditional format. The current Kodexa document runtime evaluates `condition` and returns active `{ type, properties }` records, so new configuration should use the canonical shape shown above. ### How Conditional Formats Recalculate Kodexa builds a dependency graph from every `condition`. When any referenced value changes, the affected conditional formats are evaluated for that data object. * A truthy condition activates the format. * A falsy condition deactivates the format. * An empty `condition` is skipped. * A condition that errors is treated as inactive. * Kodexa caches active/inactive state and emits conditional format change events only when the state changes. ## Formula Language Validation rules and conditional formats use Kodexa's KEXL formula language. The same syntax is used by formula data elements and other Data Definition features. ### Literals ```typescript theme={null} 123 123.45 "Invoice total is required" 'PAID' true false null [1, 2, 3] ``` Strings can be single-quoted or double-quoted. Use backslash escapes such as `\"`, `\'`, `\n`, and `\t`, or double the quote character inside the same quote style. ### Attribute References Use curly braces to read values from the current data object or related data objects. ```typescript theme={null} {invoice_number} // Attribute on the current data object {line_items/line_total} // Child data object path, often returns an array {../invoice_number} // Attribute on the parent data object {../../customer_name} // Attribute two levels up {./status} // Explicit self-relative attribute reference ``` References are case-insensitive when matching existing attributes. The formula service also understands data element `externalName` mappings, so generated data can use external names without changing the underlying element names. Parent references are supported in validation rules as well as in formula elements. `{../field}` and `{../../field}` resolve inside `conditionalFormula`, `ruleFormula`, `messageFormula`, and `detailFormula` on every evaluation path: extraction, recalculation after a reviewer edit, document reload, and Data Definition save. A reference that walks up more levels than the data object has ancestors, such as `{../../../vendor_name}` on a top-level data object, is an evaluation error rather than an empty match. The rule is reported as a configuration error, the same as any other formula error, so the bad reference is visible instead of silently passing. ### Operators | Operator | Use | | -------------------- | --------------------------- | | `+`, `-`, `*`, `/` | Arithmetic | | `^` | Power | | `&` | String concatenation | | `=` | Equality | | `!=` | Inequality | | `<`, `<=`, `>`, `>=` | Ordered comparison | | `&&` | Logical and, short-circuits | | `\|\|` | Logical or, short-circuits | | `!` | Logical not | | `[]` | Array index access | | `()` | Grouping | ```typescript theme={null} ({subtotal} + {tax_amount}) = {total_amount} !isblank({invoice_number}) && {total_amount} > 0 concat("Invoice ", {invoice_number}) & " is ready" ``` Use `=` for equality. Do not use `==`. Use `&&` and `||`; uppercase `AND` and `OR` are not formula operators. ### Truthy and Falsy Values Kodexa uses truthiness when deciding whether a validation passes or a conditional format is active. | Value | Truthiness | | ---------------- | ---------- | | `true` | truthy | | `false` | falsy | | Non-zero number | truthy | | Zero | falsy | | Non-empty string | truthy | | Empty string | falsy | | Non-empty array | truthy | | Empty array | falsy | | `null` | falsy | ### Function Reference Function names are case-insensitive, but examples should use lower-case names for consistency. | Function | Example | Notes | | ------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | `sum` | `sum({line_items/line_total})` | Adds numeric values. Arrays are summed. | | `average` | `average({scores/value})` | Mean of numeric values. Returns zero when there are no numeric values. | | `min` | `min({line_items/amount})` | Lowest numeric value. | | `max` | `max({line_items/amount})` | Highest numeric value. | | `abs` | `abs({expected} - {actual})` | Absolute value. Arrays are summed first. | | `ceil` | `ceil({amount})` | Round toward positive infinity. | | `floor` | `floor({amount})` | Round toward negative infinity. | | `round` | `round({amount})` | Round to nearest integer. | | `stddeviation` | `stddeviation(1, 2, 3)` | Standard deviation. | | `decimalplaces` | `decimalplaces({amount}, 2, "round_half_up")` | Decimal rounding. Default mode is floor. | | `concat` | `concat("PO ", {purchase_order_number})` | Converts arguments to strings and joins them. | | `substring` | `substring({code}, 0, 3)` | String slice using start and end indexes. | | `lowercase` | `lowercase({vendor_name})` | Returns lower-case string values. | | `uppercase` | `uppercase({vendor_name})` | Returns upper-case string values. | | `trim` | `trim({vendor_name})` | Trims whitespace. | | `replace` | `replace({phone}, "-", "")` | Replaces all occurrences. | | `split` | `split({csv_value}, ",")` | Returns an array. | | `strlen` | `strlen({invoice_number})` | Length of string value. | | `length` | `length({line_items/line_total})` | Length of string, array, map, or object representation. | | `len` | `len({line_items/line_total})` | Alias for `length`. | | `if` | `if({amount} > 0, "debit", "credit")` | Conditional result. Arguments are evaluated before the function runs. | | `ifnull` | `ifnull({tax_amount}, 0)` | Returns fallback for `null` or empty arrays. | | `sumifs` | `sumifs({amounts}, {types}, "freight")` | Sums values where criteria match. | | `countifs` | `countifs({statuses}, {statuses}, "open")` | Counts values where criteria match. | | `isnull` | `isnull({field})` | True for null or empty arrays. | | `isblank` | `isblank({field})` | True for null, empty arrays, or whitespace-only strings. | | `contains` | `contains({description}, "freight")` | Substring check. | | `startswith` | `startswith({code}, "INV")` | Prefix check. | | `endswith` | `endswith({code}, "-US")` | Suffix check. | | `datemath` | `datemath({invoice_date}, "days", 30)` | Adds days, weeks, months, or years. `datemath("today")` returns today's date. | | `isdate` | `isdate({invoice_date})` | True when the value can be parsed as a date. | | `isbeforedate` | `isbeforedate({invoice_date}, {due_date})` | Date comparison, ignoring time. | | `isafterdate` | `isafterdate({due_date}, {invoice_date})` | Date comparison, ignoring time. | | `daysbetween` | `daysbetween({invoice_date}, {due_date})` | Full days between two dates. | | `weeksbetween` | `weeksbetween({start_date}, {end_date})` | Full weeks between two dates. | | `monthsbetween` | `monthsbetween({start_date}, {end_date})` | Month difference between two dates. | | `validatedate` | `validatedate({invoice_date})` | True when the date parses. | | `formatdate` | `formatdate({invoice_date}, "yyyy-MM-dd")` | Formats a date using Java date-pattern syntax. | | `regex` | `regex({invoice_number}, "^[A-Z0-9-]+$")` | Go regular expression match. | | `matches` | `matches({invoice_number}, "^[A-Z0-9-]+$")` | Alias for `regex`. | | `confidence` | `confidence("invoice_number")` | Reads confidence for an attribute on the current data object. | | `count` | `count(1, 2, 3)` | Counts function arguments. | | `selectioncontains` | `selectioncontains({available_codes}, {code})` | Checks string values or `{ label, value }` option objects. | | `getvalue` | `getvalue("../invoice_number")` | Resolves a path supplied as a string. | | `servicebridgecall` | `servicebridgecall("VendorLookup", "lookup", "name", {vendor_name})` | Calls a configured Service Bridge when available in the runtime. | ### Eager and Short-Circuit Evaluation Function arguments are evaluated before the function runs. That means `if` does not protect an invalid branch from being evaluated. ```typescript Avoid theme={null} if({denominator} = 0, 0, {numerator} / {denominator}) ``` Use logical operators when you need short-circuit behavior. ```typescript Use theme={null} {denominator} != 0 && ({numerator} / {denominator}) > 10 ``` ### Common Formula Patterns ```typescript Required value theme={null} !isblank({invoice_number}) ``` ```typescript Optional date ordering theme={null} isblank({due_date}) || isblank({invoice_date}) || isafterdate({due_date}, {invoice_date}) || {due_date} = {invoice_date} ``` ```typescript Numeric tolerance theme={null} abs({expected_total} - {actual_total}) < 0.01 ``` ```typescript Date window theme={null} !isblank({due_date}) && isafterdate({due_date}, datemath("today", "days", -1)) && isbeforedate({due_date}, datemath("today", "days", 31)) ``` ```typescript Selection-like text theme={null} {status} = "Approved" || {status} = "Approved with Conditions" ``` ```typescript Dynamic path theme={null} getvalue("../" & {field_name}) = "Complete" ``` ## Recommended Authoring Pattern 1. Put field-level validation and formatting on the field taxon. 2. Put object-level validation on the group data element. 3. Use stable `exceptionId` values for customer-facing exceptions. 4. Use `conditional: true` only when the rule has a real gating condition. 5. Write formulas with explicit braces around every data reference. 6. Prefer `isblank`, `ifnull`, `abs`, `datemath`, and date comparison functions over hand-rolled string checks. 7. Keep conditional formatting visual and non-blocking; use validation rules for business conditions that must be resolved. ## Related Start with the overall data definition model. Use scripts when a rule needs side effects or procedural logic. Compute dropdown options dynamically from document context. See the broader formula guide and examples. # Bridge API & External Services Source: https://developer.kodexa.ai/guides/data-forms/bridge-api Use the Bridge API in Kodexa data forms for platform data access, HTTP requests, and service bridge integration directly from sandboxed scripts. The Bridge API is the interface between scripts running in the QuickJS sandbox and the Kodexa platform. Scripts access it through the `kodexa.*` namespace, where each sub-namespace corresponds to a capability gated by the form's bridge permissions. ## Bridge Permissions The `bridge` property on a data form configures what scripts are allowed to do. The `permissions` array lists capability gates -- a script that attempts to call a method without the required permission will receive a `Permission denied` error. | Permission | Grants access to | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `data:read` | Read data objects, attributes, tag metadata, taxonomies | | `data:write` | Modify data objects and attributes | | `document:read` | Access the document proxy (`kodexa.document`) | | `navigation` | Focus attributes, switch pages, rotate pages, jump to sections and exceptions, switch views (`kodexa.navigation`) | | `viewer` | Scroll, pan, zoom, show page regions, fit the page, copy the selection, and pop out / re-dock the spatial document viewer (`kodexa.viewer`) | | `formState` | Get and set form state values, access node refs | | `http:get` | HTTP GET requests via `kodexa.http` | | `http:post` | HTTP POST requests via `kodexa.http` and service bridge calls | Additional bridge configuration: * **`apiBaseUrl`** -- Base URL for `kodexa.http` and `kodexa.serviceBridge` calls. Defaults to `"/api"`. * **`maxExecutionMs`** -- Script execution timeout in milliseconds. Defaults to `1000`. ```json theme={null} { "bridge": { "permissions": ["data:read", "data:write", "navigation", "http:post"], "apiBaseUrl": "https://api.example.com", "maxExecutionMs": 2000 } } ``` ## kodexa.data Requires `data:read`. Methods that modify data also require `data:write`, and the two focused-field methods that only move focus or reveal values require `navigation` -- each method's gate is listed in the table. | Method | Parameters | Returns | Permission | | ------------------------------------------- | -------------------------------------------------- | --------------------- | ------------ | | `getDataObjects(filter?)` | `{ path?: string, parentId?: string }` | `DataObject[]` | `data:read` | | `getDataObject(uuid)` | `uuid: string` | `DataObject \| null` | `data:read` | | `getAttributes(dataObjectUuid)` | `dataObjectUuid: string` | `Attribute[]` | `data:read` | | `getAttribute(dataObjectUuid, path)` | `dataObjectUuid: string, path: string` | `any` | `data:read` | | `setAttribute(dataObjectUuid, path, value)` | `dataObjectUuid: string, path: string, value: any` | `void` | `data:write` | | `addDataObject(parentUuid, path)` | `parentUuid: string, path: string` | `DataObject` | `data:write` | | `deleteDataObject(uuid)` | `uuid: string` | `void` | `data:write` | | `getTagMetadata(path)` | `path: string` | `TagMetadata \| null` | `data:read` | | `getTaxonomies()` | -- | `Taxonomy[]` | `data:read` | | `clearFocusedValue()` | -- | `void` | `data:write` | | `deleteFocusedValue()` | -- | `void` | `data:write` | | `addDataGroup()` | -- | `void` | `data:write` | | `deleteDataGroup()` | -- | `void` | `data:write` | | `blurField()` | -- | `void` | `navigation` | | `showInDocument()` | -- | `void` | `navigation` | ```javascript theme={null} // Read all line items under a parent object const items = kodexa.data.getDataObjects({ parentId: parentUuid }); for (const item of items) { const amount = kodexa.data.getAttribute(item.uuid, "LineItem/Amount"); kodexa.log.debug("Amount: " + amount); } // Set a computed total kodexa.data.setAttribute(summaryUuid, "Invoice/Total", calculatedTotal); ``` ### Typed writes with `setAttribute` `setAttribute` stores the value in the column that matches the attribute's own type rather than keeping everything as text, so the write survives a save and reload and is picked up by the field's editor, formulas, and validation rules: * **Numeric attributes** -- a JavaScript number, or a numeric string with grouping commas such as `"1,234.56"`, is stored as the numeric value. * **Date attributes** -- an ISO date-time is stored as the date value, and a date-only literal such as `"2026-01-01"` is accepted and stored as midnight on that day. * **Boolean attributes** -- `true` / `false` set the boolean value. * **Text and untyped attributes** -- the value is stored as text; a number written to a text attribute is stored in its string form. A value that cannot be parsed as a number for a numeric attribute falls back to the text column and logs a warning to the browser console, so scripts that pre-formatted numbers or dates to work around values not saving no longer need to. ```javascript theme={null} // Each of these lands in the attribute's own typed column kodexa.data.setAttribute(uuid, "Invoice/Total", "1,234.56"); // numeric kodexa.data.setAttribute(uuid, "Invoice/IssuedOn", "2026-01-01"); // date kodexa.data.setAttribute(uuid, "Invoice/Approved", true); // boolean ``` The update goes through the same audited edit path as a reviewer's own edit, so it is persisted, survives reload, and appears in the change history. The attribute's original extracted text is never overwritten -- `setAttribute` changes the current value only. When the data object has no attribute at `path` yet, one is created from the supplied value, provided the form's taxonomy metadata resolves that path. If it does not, the call logs a warning to the browser console and makes no change. ### Focused-field methods The last six methods take **no arguments** and act on whichever field the reviewer currently has focused, so one form-declared shortcut works across every field in the form: * **`clearFocusedValue()`** blanks the field's value while keeping the attribute record. * **`deleteFocusedValue()`** removes the attribute entirely. Both leave the field looking blank, but an absent attribute is not an empty one -- the distinction can matter for export and validation. * **`addDataGroup()`** adds a sibling row after the focused field's data group. * **`deleteDataGroup()`** deletes the data group the focused field belongs to. * **`blurField()`** drops focus from the field without changing any data. * **`showInDocument()`** reveals the focused field's value in the document viewer. All six are safe no-ops when no field has focus -- a warning is logged to the browser console and no error is thrown. This is the common case rather than an edge case: a form-declared shortcut fires whether or not focus is in a field. The four methods that change data require `data:write`; `blurField` and `showInDocument` need only `navigation`, so a read-only form can still move focus and reveal values. These methods exist to back data-entry keyboard shortcuts. Bind them declaratively as form `shortcuts` entries (see [Keyboard Shortcuts](/guides/data-forms/shortcuts)) whose scripts call, for example, `kodexa.data.clearFocusedValue()`. ## kodexa.navigation Requires `navigation`. Spatial-viewer methods route to the document viewer for the form's first document family by default; pass an explicit `documentFamilyId` when a form is bound to more than one document. | Method | Parameters | Returns | Description | | -------------------------------------------------------- | ---------------------------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------------------- | | `focusAttribute(dataObjectUuid, attributePath, viewId?)` | `dataObjectUuid: string, attributePath: string, viewId?: string` | `void` | Highlight an attribute in the document viewer (and broadcast to sidecars) | | `setPage(page, documentFamilyId?)` | `page: number, documentFamilyId?: string` | `void` | Navigate the spatial viewer to a **1-based** page number | | `nextPage(documentFamilyId?)` | `documentFamilyId?: string` | `void` | Step the spatial viewer forward one page. Clamped to the last page -- a no-op at the end of the document | | `previousPage(documentFamilyId?)` | `documentFamilyId?: string` | `void` | Step the spatial viewer back one page. Clamped to the first page -- a no-op at the start of the document | | `rotatePage(direction, documentFamilyId?)` | `direction: "left" \| "right", documentFamilyId?: string` | `void` | Rotate the spatial viewer's **current** page by 90° in the given direction | | `getCurrentPage(documentFamilyId?)` | `documentFamilyId?: string` | `number \| undefined` | Current 1-based page of the spatial viewer | | `getPageCount(documentFamilyId?)` | `documentFamilyId?: string` | `number \| undefined` | Total page count of the spatial viewer's document | | `nextSection()` | -- | `boolean` | Scroll the form to the next visible section and focus its first input | | `nextException(documentFamilyId?)` | `documentFamilyId?: string` | `boolean` | Scroll the form to the next open validation exception | | `scrollToNode(ref)` | `ref: string` | `void` | Scroll the document viewer to a content node *(planned)* | | `switchView(viewName)` | `viewName: string` | `void` | Switch to a different form view *(planned)* | Note that `focusAttribute` takes `dataObjectUuid` as the first parameter and `attributePath` as the second. `rotatePage` behaves differently from the absolute `setPage`: * **Rotation is relative.** Each call rotates by ±90° from the page's *current* rotation -- it is not an absolute angle. Calling `rotatePage("right")` twice leaves the page at 180°; a following `rotatePage("left")` returns it to 90°. * **It affects only the current page.** Rotating every page of the document remains a separate viewer menu action; the bridge method touches just the page the viewer is currently showing. * **It requires the `navigation` permission** and accepts an optional `documentFamilyId` to target a specific open document when the form is bound to more than one. A per-document rotation does not bleed across documents. An invalid `direction` (anything other than `"left"` or `"right"`) or a missing viewer is a no-op -- a warning is logged to the browser console and no error is thrown. ```javascript theme={null} // Jump to page 3 of the form's document kodexa.navigation.setPage(3); // Step one page at a time -- these clamp at the document edges, // so calling nextPage() on the last page (or previousPage() on // the first) is a silent no-op rather than an error. kodexa.navigation.nextPage(); kodexa.navigation.previousPage(); // Read the current page after a navigation const current = kodexa.navigation.getCurrentPage(); kodexa.log.debug("Now on page " + current + " of " + kodexa.navigation.getPageCount()); // Target a specific document when the form is bound to multiple kodexa.navigation.setPage(1, "doc-family-uuid"); // Rotate the current page of the form's document clockwise kodexa.navigation.rotatePage("right"); // Rotate a specific open document's current page counter-clockwise kodexa.navigation.rotatePage("left", "doc-family-uuid"); ``` These rotate actions are typically wired to keyboard shortcuts rather than called directly. The `alt+R` / `alt+shift+R` bindings are not hard-coded -- they are authored declaratively as form `shortcuts` entries whose script calls `kodexa.navigation.rotatePage(...)`: ```yaml theme={null} shortcuts: - key: "alt+r" description: "Rotate the current page right" scriptRef: rotate-page-right - key: "alt+shift+r" description: "Rotate the current page left" scriptRef: rotate-page-left ``` The `rotate-page-right` script calls `kodexa.navigation.rotatePage("right")` and `rotate-page-left` calls `kodexa.navigation.rotatePage("left")`, so a `shortcuts:` block round-trips through the data form schema. ### Section and exception jumps `nextSection()` and `nextException()` walk a reviewer through the form rather than the document: * **`nextSection()` scrolls the form to the next visible section and focuses its first input.** Sections are the form's visible `v2:panel` components; each call advances one section, and after the last section the jump wraps back to the top of the form. * **`nextException(documentFamilyId?)` scrolls the form to the next open validation exception.** The anchor is the field's attribute editor when one is rendered; for an exception on a grid row, it is the grid containing the row. Like `nextSection`, it cycles back to the top after the last anchor. Pass the optional `documentFamilyId` to scope the exception scan to a specific open document when the form is bound to more than one. * **Both return `false` when the window has nothing to act on** -- no form pane in the current window (for example a popped-out viewer tab), or, for `nextException`, no open exceptions. A warning is logged to the browser console and no error is thrown. Both require the `navigation` permission. Bind them as form `shortcuts` entries to give reviewers keyboard-driven section-by-section or exception-by-exception review: ```yaml theme={null} shortcuts: - key: "alt+shift+n" description: "Jump to the next section" scriptRef: next-section - key: "alt+shift+e" description: "Jump to the next exception" scriptRef: next-exception ``` The `next-section` script calls `kodexa.navigation.nextSection()` and `next-exception` calls `kodexa.navigation.nextException()`. ## kodexa.viewer Requires `viewer`. The `viewer` surface manipulates the spatial document viewport -- its scroll and pan position, its zoom level, and whether the viewer is docked in the workspace or popped out into its own browser tab -- rather than the page state. That separation is why it is a distinct permission from `navigation`. | Method | Parameters | Returns | Description | | --------------------------------------- | --------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------- | | `scroll(direction, documentFamilyId?)` | `direction: "up" \| "down" \| "left" \| "right", documentFamilyId?: string` | `void` | Nudge the spatial viewer \~¼ of its visible size in the given direction | | `zoom(direction, documentFamilyId?)` | `direction: "in" \| "out", documentFamilyId?: string` | `void` | Zoom the spatial viewer in or out one step, matching the toolbar zoom buttons | | `showRegion(region, documentFamilyId?)` | `region: "top" \| "middle" \| "bottom", documentFamilyId?: string` | `void` | Reset zoom and scroll the named region of the current page into view | | `fit(direction, documentFamilyId?)` | `direction: "width" \| "height", documentFamilyId?: string` | `void` | Zoom so the page fills the viewport's width or height | | `copySelection(documentFamilyId?)` | `documentFamilyId?: string` | `void` | Copy the current document selection to the clipboard | | `detach()` | -- | `void` | Pop the viewer out into a new browser tab, mirroring the viewer's "Open in new tab" button | | `dock()` | -- | `void` | Dock a popped-out viewer back into the workspace, mirroring the viewer's "Dock back to workspace" button | * **`scroll` direction is one of `"up"`, `"down"`, `"left"`, `"right"`.** Any other value is a no-op -- a warning is logged to the browser console and no error is thrown. `"up"` / `"down"` scroll the viewer vertically; `"left"` / `"right"` pan the zoom transform (the spatial image is CSS-transformed, so left/right movement pans rather than scrolls). Both feel like a small "arrow key" nudge, not a full-screen jump. * **`zoom` steps one increment in or out.** `"in"` and `"out"` map to the toolbar's zoom-in / zoom-out buttons -- one call is one step. Any other value is a no-op with a browser-console warning and no error thrown. * **`showRegion` resets zoom and brings a region of the current page into view.** The region is one of `"top"`, `"middle"`, or `"bottom"`; any other value is a no-op with a browser-console warning. The region names are looser than they sound -- `"middle"` scrolls to roughly a quarter down the page, matching long-standing viewer behavior. * **`fit` zooms so the page fills the viewport.** `"width"` fits the page to the viewport's width, `"height"` to its height; any other value is a no-op with a browser-console warning. * **`copySelection` copies the current document selection to the clipboard.** It deliberately stays out of the way while you are typing in a field -- when focus is inside an input, the method does nothing, so a copy shortcut bound to it coexists with native copy in form fields. * **`detach` pops the viewer out into a new browser tab.** This is the same action as the viewer's "Open in new tab" button. It is only meaningful from the main window -- calling it from inside the popped-out tab is a no-op (a warning is logged). If the pop-out tab is already open, `detach` focuses that tab rather than opening a second one. It takes no arguments. * **`dock` returns a popped-out viewer to the workspace.** This mirrors the viewer's "Dock back to workspace" button and works from either the main window or the popped-out tab. When no viewer is currently detached it is a no-op with a warning. It takes no arguments. * **`scroll`, `zoom`, `showRegion`, `fit`, and `copySelection` can target one document.** With no argument they act on the form's default (first) document family; pass an optional `documentFamilyId` to target a specific open document. All five apply to the viewer whether it is docked in the workspace or popped out into its own tab. `detach` and `dock` always act on the workspace's pop-out viewer and take no target. * **Requires the `viewer` permission.** Calling any `kodexa.viewer` method without `"viewer"` in `bridge.permissions` throws a `Permission denied: viewer` error. ```javascript theme={null} // Nudge the viewer down, then pan right kodexa.viewer.scroll("down"); kodexa.viewer.scroll("right"); // Zoom in one step, then back out kodexa.viewer.zoom("in"); kodexa.viewer.zoom("out"); // Pop the viewer out into its own tab, then dock it back kodexa.viewer.detach(); kodexa.viewer.dock(); // scroll and zoom accept a documentFamilyId to target a specific open document kodexa.viewer.scroll("up", "doc-family-uuid"); kodexa.viewer.zoom("in", "doc-family-uuid"); ``` Like the rest of the Bridge API, these actions are usually wired to keyboard shortcuts rather than called directly. Bindings are authored declaratively as form `shortcuts` entries (see [Keyboard Shortcuts](/guides/data-forms/shortcuts)); zoom, detach, and dock fit the `"zoom"` help-dialog group. The keys below are only examples chosen by the form author -- there are no fixed hotkeys for these actions: ```yaml theme={null} shortcuts: - key: "control+=" description: "Zoom the document in" group: "zoom" scriptRef: zoom-in - key: "control+-" description: "Zoom the document out" group: "zoom" scriptRef: zoom-out - key: "control+9" description: "Open the viewer in a new tab" group: "zoom" scriptRef: detach-viewer - key: "control+w" description: "Dock the viewer back to the workspace" group: "zoom" scriptRef: dock-viewer ``` Each `scriptRef` points at a script that calls the matching method -- for example `detach-viewer` calls `kodexa.viewer.detach()` and `dock-viewer` calls `kodexa.viewer.dock()`. ## kodexa.form Requires `formState`. | Method | Parameters | Returns | Description | | ----------------- | ------------------------- | --------------------- | -------------------------------------------- | | `get(key)` | `key: string` | `any` | Read a form state value | | `set(key, value)` | `key: string, value: any` | `void` | Write a form state value | | `getNodeRef(ref)` | `ref: string` | `{ setProps(props) }` | Get a UINode by ref for dynamic prop updates | Form state is ephemeral -- it persists for the lifetime of the form session but is not saved to the server. Use it for UI-only concerns like toggling visibility, tracking selection state, or passing values between scripts. ```javascript theme={null} // Toggle a detail panel const expanded = kodexa.form.get("detailExpanded") || false; kodexa.form.set("detailExpanded", !expanded); // Dynamically update a node's props const node = kodexa.form.getNodeRef("statusLabel"); node.setProps({ text: "Validated", variant: "success" }); ``` ## kodexa.document Requires `document:read`. The writable snapshot also requires `data:write`. | Method | Returns | Description | | -------------------- | --------------------- | ------------------------------------------------------------ | | `snapshot()` | `ScriptDocumentProxy` | Read-only proxy over the current document data | | `writableSnapshot()` | `ScriptDocumentProxy` | Writable proxy -- mutations flow through the workspace store | The `ScriptDocumentProxy` exposes `getAllDataObjects()`, `getDataObjectByUUID(uuid)`, and `getDataObjectsByPath(path)`. Each returns `ScriptDataObjectProxy` instances with methods like `getAttributes()`, `getAttribute(label)`, `getChildren()`, and `getPath()`. Writable proxies additionally support `addAttribute()`, `addChild()`, and `setValue()` on attributes. This API is separate from `loadDocument()` available in inline or named scripts. `kodexa.document` provides Bridge API context tied to the current workspace session; `loadDocument()` is for standalone script execution. ## kodexa.http Requires `http:get` for GET requests, `http:post` for POST requests. Both are async. | Method | Parameters | Returns | | ------------------- | -------------------------- | -------------- | | `get(path)` | `path: string` | `Promise` | | `post(path, body?)` | `path: string, body?: any` | `Promise` | Requests are sent to `apiBaseUrl + path`. The base URL defaults to `"/api"` if not configured. ```javascript theme={null} // Call an external validation endpoint const result = await kodexa.http.post("/validate", { invoiceNumber: invoiceNum, vendorId: vendorId }); if (!result.valid) { kodexa.log.warn("Validation failed: " + result.reason); } ``` ## kodexa.serviceBridge Requires `http:post`. | Method | Parameters | Returns | | ---------------------------- | ------------------------------------------- | -------------- | | `call(ref, endpoint, body?)` | `ref: string, endpoint: string, body?: any` | `Promise` | Service bridges are named proxy endpoints that connect the platform to external APIs with centralized authentication. The `ref` is the bridge slug (e.g., `"acme-logistics/carrier-lookup"`), and `endpoint` is the endpoint name defined in the bridge YAML. The bridge manages an `X-Bridge-Context` header for session caching. On the first call, no context header is sent; the server runs any configured `initScript` and returns context in the response header. Subsequent calls attach the cached context, skipping re-initialization. Context expires after a configurable TTL (default 3600 seconds). ```javascript theme={null} const carriers = await kodexa.serviceBridge.call( "acme-logistics/carrier-lookup", "lookup-carrier", { scac: scacCode } ); ``` ## kodexa.log No permission required. | Method | Parameters | | ---------------- | ----------------- | | `debug(message)` | `message: string` | | `warn(message)` | `message: string` | | `error(message)` | `message: string` | All log output is prefixed with `[DataFormV2]` and routed to the browser console. ## Service Bridges on Panels In addition to imperative `kodexa.serviceBridge.call()` from scripts, panels support declarative service bridge integration through the `serviceBridge` prop on `v2:panel`. This allows a component to declare an external API dependency without writing script code. ### ServiceBridgeConfig | Property | Type | Description | | ----------------- | ------------------------------ | ---------------------------------------------------------- | | `ref` | `string` | Bridge reference (e.g., `"acme-logistics/carrier-lookup"`) | | `endpoint` | `string` | Endpoint name from the bridge YAML | | `method` | `"GET" \| "POST"` | HTTP method, defaults to `POST` | | `requestMapping` | `Record` | Maps data attribute paths to request fields | | `responseMapping` | `ServiceBridgeResponseMapping` | Controls how the response maps back to UI or data | | `triggerOn` | `string[]` | Attribute paths that trigger a re-call when values change | ### ServiceBridgeResponseMapping | Property | Type | Description | | ------------- | --------------------- | ---------------------------------------------------------------------------------------------------- | | `value` | `string` | Path to extract option value from each response item | | `label` | `string` | Path or expression for the option label (supports concatenation like `"code + ' - ' + description"`) | | `description` | `string` | Path or expression for hint text shown below the label in dropdowns | | `autoSelect` | `string` | Auto-select a single best-match field from the response | | `attributes` | `Array<{ from, to }>` | Map response fields to data attributes (`from`: response path, `to`: attribute path) | ### How It Works When any attribute listed in `triggerOn` changes, the panel reads the current values from `requestMapping`, calls the service bridge endpoint, and maps the response back using `responseMapping`. This creates a reactive loop: user edits a field, the bridge fetches updated data, and dependent fields populate automatically. ```json theme={null} { "component": "v2:panel", "props": { "title": "Carrier Details", "serviceBridge": { "ref": "acme-logistics/carrier-lookup", "endpoint": "lookup-carrier", "requestMapping": { "scac": "Shipment/CarrierSCAC" }, "responseMapping": { "value": "carrierId", "label": "scac + ' - ' + carrierName", "attributes": [ { "from": "carrierName", "to": "Shipment/CarrierName" }, { "from": "dotNumber", "to": "Shipment/DOTNumber" } ] }, "triggerOn": ["Shipment/CarrierSCAC"] } } } ``` In this example, when the user enters a SCAC code, the panel calls the carrier lookup endpoint and auto-populates the carrier name and DOT number fields from the response. ## v2:serviceBridgeView A container component that calls a service bridge endpoint and makes the response available to its children through the data context. Unlike the panel `serviceBridge` prop (which maps responses back to attributes), `v2:serviceBridgeView` is designed for **read-only display** -- rendering bridge responses as tables, markdown, labels, or any combination of child components. ### How It Works 1. The component resolves the bridge slug to an ID via the platform's `/api/resolve` endpoint. 2. It POSTs the `params` to the bridge proxy endpoint (`/api/service-bridges/{id}/proxy/{endpoint}`). 3. If a `transform` expression is provided, the response is reshaped using [JSONata](https://jsonata.org/). 4. The result is injected into a scoped `DataContextV2` as `ctx.$bridgeResult`, along with `ctx.$bridgeLoading` and `ctx.$bridgeError`. 5. Children render using bindings that reference these context variables. The component handles loading and error states automatically -- children are only rendered once data is available. ### Props | Prop | Type | Default | Description | | --------- | ------ | -------- | ---------------------------------------------------------------------------------------- | | bridgeRef | string | required | Service bridge URI (e.g., `"service-bridge://acme/rate-lookup"` or `"acme/rate-lookup"`) | | endpoint | string | required | Endpoint name defined in the bridge configuration | | params | Record | -- | Request body sent to the bridge (typically provided via `bindings`) | | transform | string | -- | JSONata expression applied to the response before injecting into context | ### Context Variables Children of `v2:serviceBridgeView` receive these additional context variables: | Variable | Type | Description | | -------------------- | --------- | ------------------------------------------------------ | | `ctx.$bridgeResult` | `any` | The bridge response (after optional JSONata transform) | | `ctx.$bridgeLoading` | `boolean` | `true` while the request is in flight | | `ctx.$bridgeError` | `string` | Error message if the request failed | All existing context variables (`ctx.dataObjects`, `ctx.tagMetadataMap`, etc.) remain available -- the bridge context is additive. ### Reactive Parameters When `params` is provided via `bindings`, the component re-calls the bridge whenever the bound values change. This creates a reactive chain: the user edits an attribute, the binding expression re-evaluates, new params are sent to the bridge, and children re-render with fresh data. When referencing attributes in binding expressions, the `tag` property is the **leaf name** (e.g., `originZip`), not the full taxonomy path (`shipment/originZip`). The full path is available as `path` on the attribute. Use `a.tag === 'originZip'` or `a.path === 'shipment/originZip'` depending on which you need. ### Example: Table from Bridge Response Call a rate lookup endpoint, transform the response with JSONata, and render the results as a filterable AG Grid table: ```yaml theme={null} component: v2:serviceBridgeView props: bridgeRef: "service-bridge://acme/rate-lookup" endpoint: "get-rates" transform: | results.{ "Carrier": carrier, "Rate": "$" & $string(rate), "Transit": transit & " days" } bindings: params: | { originZip: ctx.dataObjects[0]?.attributes?.find( a => a.tag === 'originZip' )?.stringValue, destZip: ctx.dataObjects[0]?.attributes?.find( a => a.tag === 'destZip' )?.stringValue, weight: ctx.dataObjects[0]?.attributes?.find( a => a.tag === 'weight' )?.numericValue } children: - component: v2:dataTable bindings: rows: "ctx.$bridgeResult" props: filterable: true columns: - field: Carrier title: Carrier Name - field: Rate title: Rate width: 120 - field: Transit title: Transit Time width: 120 ``` ### Example: Markdown Summary Fetch a report from a bridge and render it as markdown: ```yaml theme={null} component: v2:serviceBridgeView props: bridgeRef: "service-bridge://acme/analysis" endpoint: "get-report" bindings: params: | { invoiceId: ctx.dataObjects[0]?.attributes?.find( a => a.tag === 'id' )?.stringValue } children: - component: v2:markdown bindings: content: "ctx.$bridgeResult?.report" ``` ### Example: Mixed Content Combine multiple child components to render different parts of the bridge response: ```yaml theme={null} component: v2:serviceBridgeView props: bridgeRef: "service-bridge://acme/vendor-check" endpoint: "validate" bindings: params: | { vendorCode: ctx.dataObjects[0]?.attributes?.find( a => a.tag === 'vendorCode' )?.stringValue } children: - component: v2:label bindings: text: "'Vendor: ' + (ctx.$bridgeResult?.vendorName ?? 'Unknown')" - component: v2:dataTable bindings: rows: "ctx.$bridgeResult?.recentOrders" props: filterable: true columns: - field: orderNumber title: Order # - field: date title: Date - field: amount title: Amount - component: v2:markdown bindings: content: "ctx.$bridgeResult?.notes" ``` ### JSONata Transform Reference The `transform` prop accepts any valid [JSONata](https://jsonata.org/) expression. Common patterns: | Pattern | Expression | Description | | ---------------------- | ------------------------------------------------------------- | -------------------------------------- | | Extract a nested array | `response.data.items` | Drill into the response structure | | Reshape objects | `items.{ "Name": name, "Total": "$" & $string(price * qty) }` | Create new fields from existing ones | | Filter rows | `items[status = "active"]` | Only include rows matching a condition | | Aggregate | `$sum(items.amount)` | Compute totals or other aggregations | | Sort | `items^(>amount)` | Sort results by a field | | String formatting | `items.{ "Display": firstName & " " & lastName }` | Concatenate fields | If the transform is omitted, the raw response is passed through as `ctx.$bridgeResult`. The JSONata transform runs client-side after the response is received. For large responses, consider using the bridge's `postReplyScript` to filter server-side before the data reaches the browser. ### Comparison: Panel serviceBridge vs serviceBridgeView | Feature | Panel `serviceBridge` prop | `v2:serviceBridgeView` | | ----------------- | --------------------------------------- | ---------------------------------------------- | | Purpose | Map bridge response to data attributes | Display bridge response as read-only content | | Trigger | Attribute path changes (`triggerOn`) | Binding expression changes (reactive params) | | Response handling | `responseMapping` writes to attributes | Children render from `ctx.$bridgeResult` | | Rendering | No built-in display | Children render tables, markdown, labels, etc. | | Script required | No | No | | Use case | Auto-populate fields from external data | Show reference data, reports, lookup tables | # Data Binding & Context Source: https://developer.kodexa.ai/guides/data-forms/data-binding Bind data form components to extracted document data in Kodexa using context variables, expressions, and scoped data for multi-instance documents. Data form components display values that originate from a document's extracted data -- the data objects and attributes produced by the taxonomy and extraction pipeline. At render time, the form renderer assembles these into a `DataContextV2` object and injects it via Vue's `provide`/`inject` mechanism so every component in the tree can access it as `ctx`. ## Context Variables Binding expressions receive a `ctx` object with the following properties: | Variable | Type | Description | | -------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------- | | `ctx.dataObjects` | `any[]` | All data objects in the current scope | | `ctx.$item` | `any` | Current item when inside a `for` loop | | `ctx.$index` | `number` | Current index when inside a `for` loop | | `ctx.$parent` | `DataContextV2` | Parent data context (for nested scopes such as `groupTaxon`) | | `ctx.$root` | `DataContextV2` | Root data context, always the top-level context | | `ctx.tagMetadataMap` | `Map` | Taxonomy metadata definitions keyed by tag path | | `ctx.formValues` | `Record` | Shared record for cross-field value exchange | | `ctx.$bridgeResult` | `any` | Service bridge response (only inside `v2:serviceBridgeView`, see [Bridge API](/guides/data-forms/bridge-api)) | | `ctx.$bridgeLoading` | `boolean` | `true` while a bridge request is in flight | | `ctx.$bridgeError` | `string` | Error message if the bridge request failed | The full TypeScript interface: ```typescript theme={null} interface DataContextV2 { dataObjects?: any[]; $item?: any; $index?: number; $parent?: DataContextV2; $root?: DataContextV2; tagMetadataMap?: Map; formValues?: Record; [key: string]: any; } ``` ## Props vs Bindings Every `UINode` can set values on its component through two mechanisms: * **props** -- static values set at definition time. They do not change unless the form definition itself changes. * **bindings** -- JavaScript expressions evaluated against `ctx` at render time. They re-evaluate automatically whenever the underlying data context changes. When both `props` and `bindings` define the same key, the binding result takes precedence. ```json theme={null} { "component": "card:label", "props": { "label": "Invoice" } } ``` ```json theme={null} { "component": "card:label", "bindings": { "label": "ctx.dataObjects[0]?.attributes?.find(a => a.path === 'invoice/vendor')?.value || 'Untitled'" } } ``` The first example always displays "Invoice". The second evaluates against live data and updates when the data objects change. See the [V2 Data Binding](/guides/data-forms/data-binding) guide for full expression syntax and the reactivity model. ## Tag Metadata Components such as `v2:attributeEditor` use a `tagPath` prop to look up the corresponding taxonomy definition from `ctx.tagMetadataMap`. The metadata tells the component the attribute's data type (string, number, date, `SELECTION`), the available options for selection fields, the display label, and any validation or formatting rules defined in the taxonomy. This removes the need to duplicate type information in the form definition itself. ## Cross-Field Linking with valueFrom The `valueFrom` prop on `v2:attributeEditor` displays a value sourced from a different attribute instead of its own: * Set `valueFrom` to the tag path of the source attribute. * The field becomes read-only automatically. * The renderer checks `ctx.formValues` first, then falls back to searching `ctx.dataObjects` for a matching attribute path. This is useful for showing a computed or linked value -- for example, displaying a total calculated elsewhere, or mirroring a value from a related data object. ```json theme={null} { "component": "v2:attributeEditor", "props": { "tagPath": "invoice/computedTotal", "label": "Computed Total", "valueFrom": "invoice/lineItemSum" } } ``` Here the editor at `invoice/computedTotal` displays the value from `invoice/lineItemSum` and prevents direct editing. ## Scoped Contexts with groupTaxon When a `v2:panel` sets the `groupTaxon` prop, it scopes the data context for its children: 1. The panel filters `ctx.dataObjects` for objects whose `path` matches the `groupTaxon` value. 2. If multiple matching objects exist, the panel renders a tab for each instance. 3. Children receive a scoped `DataContextV2` containing only the active data object and all of its descendants (collected recursively by `parentId`). 4. Nested `groupTaxon` panels work within the already-scoped parent context, enabling hierarchies such as line items within a shipment. ```json theme={null} { "component": "v2:panel", "props": { "title": "Shipments", "groupTaxon": "shipments" }, "children": [ { "component": "v2:attributeEditor", "props": { "tagPath": "shipments/trackingNumber", "label": "Tracking Number" } }, { "component": "v2:panel", "props": { "title": "Line Items", "groupTaxon": "shipments/lineItems" }, "children": [ { "component": "v2:attributeEditor", "props": { "tagPath": "shipments/lineItems/description", "label": "Description" } } ] } ] } ``` If a document contains three shipment data objects, the outer panel renders three tabs. Selecting a tab scopes all child components -- including the nested line items panel -- to that shipment's data. The inner panel then further scopes to the line items belonging to the selected shipment. ## Next Steps Panels, tabs, rows, columns, and alerts Attribute editors, tables, grids, and more Direct extract and AI-powered data extraction Event handlers, QuickJS runtime, and document API # Data Components Source: https://developer.kodexa.ai/guides/data-forms/data-components Reference for Kodexa data form components: attribute editors, labels, tables, grids, markdown, exceptions, and knowledge sections bound to taxonomies. Data components display and edit extracted document data within V2 data forms. Each component binds to the data context via taxonomy tag paths, rendering the appropriate editor or display based on the taxon type defined in your taxonomy. ## v2:attributeEditor The core editing component. It resolves a taxonomy tag path to a data object attribute and renders the correct editor (text input, dropdown, date picker, etc.) based on the taxon type. ### Props | Prop | Type | Default | Description | | ------------- | ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | tagPath | string | required | Taxonomy path to bind to | | label | string | -- | Display label (auto-derived from the last segment of the tag path if omitted) | | colSpan | number | -- | Column span in grid layout (1-12) | | readonly | boolean | false | Disable editing | | valueFrom | string | -- | Tag path to mirror value from (makes field read-only, see [Data Binding](/guides/data-forms/data-binding)) | | editorOptions | AttributeEditorOptions | -- | Advanced editor configuration (see below) | When `valueFrom` is set, the editor becomes a read-only input that displays the value from the referenced tag path. It checks shared `formValues` first, then falls back to searching data object attributes. ### Editor Options The `editorOptions` object provides fine-grained control over editor behavior and appearance. | Option | Type | Description | | -------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | placeholder | string | Placeholder text when empty | | cellMode | boolean | Excel-like flat appearance for grid cells -- no border/shadow by default, border appears on hover/focus | | cellBackground | string | Cell background color (e.g., `'#f0fdf4'`) | | cellColor | string | Cell text color (e.g., `'#166534'`) | | cellTestId | string | Custom `data-testid` for testing | | displayAsRadio | boolean | Render SELECTION taxons as radio buttons instead of a dropdown | | displayAs | `"explanation"` | Render the attribute as a read-only AI explanation callout instead of the type-derived editor. The callout wraps its text, preserves line breaks, and grows to fit. Intended for STRING taxons holding AI rationale or narrative text that clipped in the fixed-height value box. The taxon stays STRING, so value conversion is unaffected — only the presentation changes, and the field is display-only in both editable and read-only forms. | | radioOrientation | `"horizontal"` \| `"vertical"` | Layout for radio options when `displayAsRadio` is `true`. `"horizontal"` (default) wraps options across the row, each option sharing the available width; `"vertical"` stacks one option per line at full width. Ignored unless `displayAsRadio` is `true`. | | showSelectedHint | boolean | Show the selected option's hint (plain text or markdown, per the option's `hintMarkdown` flag) in muted text beneath a SELECTION dropdown. Defaults to `true` -- set `false` to hide it. Ignored in `cellMode` and in `displayAsRadio` mode (radio options already render their hints inline). | | isCheckbox | boolean | Render as a checkbox | | onCheckValue | string | Value to set when checkbox is checked | | showCalendarPopup | boolean | Show a calendar picker for date fields | | sourceDateFormat | string | Source date format for parsing | | maskDateFormat | string | Display date format | | isMaskedText | boolean | Use masked text display | | maskedText | string | Mask pattern | | hideExceptionPopup | boolean | Hide the validation exception popup | | showPreview | boolean | Show value preview | | hideAttributeMenu | boolean | Hide the attribute context menu (defaults to `false`) | | hideInsertActions | boolean | Hide insert actions | | allowDirectExtract | boolean | Enable direct text extraction from the document (see [Extraction](/guides/data-forms/extraction)) | | aiExtraction | AIExtractionConfig | AI-powered extraction configuration (see [Extraction](/guides/data-forms/extraction)) | | showAddFromSelection | boolean | Show a "+" button on text selection (implied when `aiExtraction` is configured) | `allowDirectExtract` and `aiExtraction` are mutually exclusive. Use `allowDirectExtract` for fields where the value can be copied verbatim from the document. Use `aiExtraction` when an LLM should interpret and extract the value. When `aiExtraction` is configured without an explicit `placeholder`, the editor auto-sets the placeholder to *"Select text in document, then click to extract"*. When `allowDirectExtract` is configured without a placeholder, it auto-sets to *"Select text in document to copy value"*. ### Examples Basic attribute editor: ```json theme={null} { "component": "v2:attributeEditor", "props": { "tagPath": "invoice/invoiceNumber", "label": "Invoice Number" } } ``` Read-only editor with value mirrored from another field: ```json theme={null} { "component": "v2:attributeEditor", "props": { "tagPath": "invoice/displayTotal", "label": "Total", "readonly": true, "valueFrom": "invoice/calculatedTotal" } } ``` Checkbox editor: ```json theme={null} { "component": "v2:attributeEditor", "props": { "tagPath": "invoice/approved", "label": "Approved", "editorOptions": { "isCheckbox": true, "onCheckValue": "true" } } } ``` SELECTION field rendered as stacked radio buttons: ```json theme={null} { "component": "v2:attributeEditor", "props": { "tagPath": "invoice/approvalStatus", "label": "Approval Status", "editorOptions": { "displayAsRadio": true, "radioOrientation": "vertical" } } } ``` ## v2:label A read-only text display component. Use it for static headings, section titles, or descriptive text within a form. ### Props | Prop | Type | Default | Description | | ----- | ------ | ------------------------------------- | ---------------------------- | | text | string | required | The text content to display | | class | string | `text-sm font-medium text-foreground` | CSS class string for styling | ### Example ```json theme={null} { "component": "v2:label", "props": { "text": "Shipping Details", "class": "text-base font-bold text-foreground mb-2" } } ``` Labels support dynamic text via bindings: ```json theme={null} { "component": "v2:label", "bindings": { "text": "`Total items: ${ctx.dataObjects?.length ?? 0}`" } } ``` ## v2:table A row-based data display and editing component. It scopes rows by the parent taxonomy path of the first column's tag metadata, then renders each matching data object as a table row with inline attribute editors. ### Props | Prop | Type | Default | Description | | ------------- | ------ | -------- | ------------------------------------- | | tagPathPrefix | string | -- | Taxonomy path prefix for scoping rows | | columns | Array | required | Column definitions | Each entry in `columns` has the following shape: | Field | Type | Required | Description | | ------- | ------ | -------- | ---------------------------------------- | | tagPath | string | yes | Taxonomy path for the column's attribute | | label | string | yes | Column header text | | width | string | no | CSS width (e.g., `'200px'`, `'30%'`) | ### Example A three-column table for invoice line items: ```json theme={null} { "component": "v2:table", "props": { "tagPathPrefix": "invoice/lineItems", "columns": [ { "tagPath": "invoice/lineItems/description", "label": "Description", "width": "50%" }, { "tagPath": "invoice/lineItems/quantity", "label": "Qty", "width": "25%" }, { "tagPath": "invoice/lineItems/amount", "label": "Amount", "width": "25%" } ] } } ``` When no matching data objects are found, the table displays a "No line items found" placeholder. ## v2:grid An enhanced data grid backed by the `KodexaDataObjectGrid` component (ag-Grid). It provides sorting, filtering, grouping, and inline editing for repeating data objects. ### Props | Prop | Type | Default | Description | | -------------- | ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | groupTaxon | string | required | Taxonomy path of the group taxon whose children define columns | | groupable | boolean | false | Enable row grouping | | showColumnMenu | boolean | false | Show column menu for visibility and ordering | | aiExtraction | AIGridExtractionConfig | -- | AI-powered row extraction (see [Extraction](/guides/data-forms/extraction)) | | columns | V2GridColumnConfig\[] | -- | Custom per-row columns that render arbitrary V2 components instead of taxon-driven attribute editors (see below) | | height | number | -- | Pixel height of the grid. When omitted, the grid sizes itself with `calc(100vh - 19rem)`. An explicit value always wins, including when the grid runs inside a parent data-object scope. | | autoHeight | boolean | false | Size the grid to end exactly where its last row ends instead of reserving a fixed block of space. Supersedes `height` — set one or the other, not both. Every row renders (no virtualization), so prefer `autoHeight` for tables that are usually short and keep a bounded `height` for tables that can grow to hundreds of rows; with `autoHeight` the page scrolls rather than an inner grid. | | editorOptions | AttributeEditorOptions | -- | Default editor options applied to every taxon-driven cell. Useful for toggling `allowDirectExtract` off (defaults to `true` for grid cells). | | limitColumns | string\[] | -- | Restrict the taxon-driven columns to this tag-path subset, in order | | pagination | boolean | false | Enable ag-Grid pagination. Defaults to `false` on `v2:grid` so reviewer grids show all rows in a bounded, scrollable grid. Set `true` to page the grid. | | sort | V2GridSortEntry\[] | -- | Initial-only sort applied the first time the grid loads a task, then locked (see below). An array of entries; the first entry is the primary sort, additional entries produce a multi-column sort in order. Each entry is `{ tagPath?, header?, direction }`. | | movement | DataMovementConfig | -- | Per-row move buttons that re-parent a row under a different container (see [Row movement](#row-movement) below) | The grid automatically builds its column definitions from the children of the specified `groupTaxon` in the taxonomy. All data objects in the current data context are passed to the grid, which filters them internally by path and parent ID. Each entry in `sort` is a `V2GridSortEntry`: | Field | Type | Required | Description | | --------- | ------------------- | -------- | ------------------------------------------------------------- | | tagPath | string | no | Matches a taxon-driven column by its tag path | | header | string | no | Matches a custom column by its header text (case-insensitive) | | direction | `"asc"` \| `"desc"` | yes | Sort direction | Provide exactly one of `tagPath` or `header` per entry to select the column to sort on. For example, a default ascending sort on a line-item amount column: `{ "tagPath": "invoice/lineItems/amount", "direction": "asc" }`. `sort` sets the row order for the **first** load of a fresh task; it does not re-sort the grid on every change. After that first load the order is locked: as operators type into cells the grid no longer reorders, and a newly added row is inserted in place (by its source ordering) rather than jumping to a sorted position mid-entry. Operators can still re-sort manually by clicking a column header, which overrides the declared default. The resulting row order is persisted per task, so it is restored when the task is reopened or refreshed and is seen by other operators viewing the same task. Persistence is written only when an operator adds or removes a row — not on load and not on cell edits. ### Row movement The `movement` prop puts per-row **move** buttons on the grid, letting a reviewer re-parent a row under a different container — for example move an entry from the record level into one of its sections, or transfer it between sections — instead of deleting and re-keying it. Each rule names the taxon the row comes from (`sourceTaxon`) and the destinations it may move to (`allowedDestinations`); the destination picker resolves the candidate parent objects from those paths at click time. ```json theme={null} { "component": "v2:grid", "props": { "groupTaxon": "invoice/charges", "movement": { "enabled": true, "rules": [ { "buttonLabel": "Move to section", "sourceTaxon": "invoice/charges", "allowedDestinations": [ { "destination": "invoice/sections/charges" } ] } ] } } } ``` A rule's `sourceTaxon` must equal the grid's `groupTaxon` exactly, and each direction belongs on the grid that **displays** the rows being moved. The move button renders only on rows whose taxon matches a rule — a mismatch fails silently, with no button and no warning. If you are porting a V1 form, note the key: the configuration value is identical to V1's `taxonMovement` card property, but V2 spells the grid prop `movement` — a V1 block pasted under the old key is silently ignored. ### Custom columns Each entry in `columns` mounts a registered V2 component as the cell renderer for that column, alongside the taxon-driven attribute columns. Use this for columns that don't map to a single taxon attribute — source-document badges, copy buttons, per-row delete affordances. | Field | Type | Required | Description | | --------- | ------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | header | string | yes | Column header label | | component | string | yes | V2 component type name without the `v2:` prefix (e.g. `"attributeSourceBadge"`, `"attributeCopyButton"`, `"attributeRowPromote"`, `"attributeRowDeleteButton"`) | | props | Record\ | no | Props forwarded to the cell component on every render | | width | number | no | Pixel width (defaults to 220) | | position | `"start"` \| `"end"` | no | Place left of (`"start"`) or right of (`"end"`, default) the taxon columns | | pinned | `"left"` \| `"right"` | no | Pin the column to the grid edge | | sortable | boolean | no | Opt this custom column into sort interaction. The sort key is the row's resolved source ordinal — the first page of the (document type, group) source instance the attribute at `props.tagPath` was extracted from, batch-resolved from the page classification. Rows from the same source share a key, so a secondary `sort` entry can break ties within a source; pages the preprocessor left ungrouped fall back to the anchor's own page. Rows with no resolvable source sort last under the default ascending direction. Reference the column in the form's `sort` array by its `header`. | A sortable custom column paired with a matching `sort` entry — the grid loads with the "Source" column sorted ascending: ```json theme={null} { "component": "v2:grid", "props": { "groupTaxon": "invoice/lineItems", "sort": [ { "header": "Source", "direction": "asc" } ], "columns": [ { "header": "Source", "component": "attributeSourceBadge", "sortable": true, "props": { "tagPath": "invoice/lineItems/amount" } } ] } } ``` ### Example A grid for financial line items: ```json theme={null} { "component": "v2:grid", "props": { "groupTaxon": "financialStatement/lineItems", "groupable": true, "showColumnMenu": true } } ``` Grid with AI extraction: ```json theme={null} { "component": "v2:grid", "props": { "groupTaxon": "invoice/lineItems", "aiExtraction": { "promptRef": "my-org/extract-line-items", "modelType": "LARGE" } } } ``` When `aiExtraction` is configured, an "AI Extract" button appears in the grid toolbar. Target fields are automatically derived from the group taxon's children unless explicitly overridden via `targetPaths`. ## v2:dataTable A read-only data grid for rendering arbitrary JSON data, powered by AG Grid. Unlike `v2:table` (which binds to taxonomy tag paths and renders inline attribute editors), `v2:dataTable` takes a plain array of objects and renders them in a fully-featured grid with sorting, filtering, and column resizing. It is typically used as a child of `v2:serviceBridgeView` to display bridge responses, but can be used anywhere with binding expressions. The grid automatically uses the platform's AG Grid theme, matching light/dark mode. ### Props | Prop | Type | Default | Description | | ------------ | ------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | rows | array | -- | Array of objects to render as rows | | columns | Array | -- | Column definitions (inferred from first row if omitted) | | emptyMessage | string | `"No data"` | Message shown when there are no rows | | filterable | boolean | `true` | Show a quick-filter search box above the grid that filters across all columns. Set to `false` to hide it. | | pageSize | number | `10` | Number of rows per page. The grid always shows a pagination footer with row counts and page navigation. Adjust this to control how many rows are visible per page. | Each entry in `columns`: | Field | Type | Required | Description | | ----- | ------ | -------- | ------------------------------------------------------------------------------- | | field | string | yes | Property name to read from each row object | | title | string | no | Column header text (defaults to the field name) | | width | number | no | Column width in pixels. Columns without a width flex to fill available space. | | type | string | no | Column type: `"string"` (default) or `"boolean"` (renders checkmark/cross icon) | ### Built-in Features Because `v2:dataTable` uses AG Grid under the hood, you get these features automatically: * **Quick filter** -- a search box above the grid filters across all columns as you type (enabled by default, disable with `filterable: false`) * **Pagination** -- a footer showing row counts (e.g. "1 to 10 of 25") with page navigation controls. Defaults to 10 rows per page, configurable via `pageSize`. * **Sorting** -- click any column header to sort ascending/descending * **Column filtering** -- each column header shows a filter icon with type-appropriate filtering (text search for strings, set filter for booleans) * **Column resizing** -- drag column borders to resize * **Theme integration** -- automatically follows the platform's light/dark mode ### Examples With explicit columns (quick filter and pagination are enabled by default): ```json theme={null} { "component": "v2:dataTable", "bindings": { "rows": "ctx.$bridgeResult" }, "props": { "columns": [ { "field": "carrier", "title": "Carrier" }, { "field": "rate", "title": "Rate", "width": 120 }, { "field": "transit", "title": "Transit Days", "width": 120 } ] } } ``` Custom page size with quick filter disabled: ```json theme={null} { "component": "v2:dataTable", "bindings": { "rows": "ctx.$bridgeResult" }, "props": { "filterable": false, "pageSize": 25, "columns": [ { "field": "carrier", "title": "Carrier" }, { "field": "rate", "title": "Rate", "width": 120 }, { "field": "transit", "title": "Transit Days", "width": 120 } ] } } ``` Boolean column with checkmark rendering: ```json theme={null} { "component": "v2:dataTable", "bindings": { "rows": "ctx.$bridgeResult" }, "props": { "filterable": true, "columns": [ { "field": "name", "title": "Bill To Name" }, { "field": "isValid", "title": "Valid", "type": "boolean", "width": 100 } ] } } ``` With auto-inferred columns (column headers derived from property names): ```json theme={null} { "component": "v2:dataTable", "bindings": { "rows": "ctx.$bridgeResult?.items" } } ``` Static data via props (no bridge required): ```json theme={null} { "component": "v2:dataTable", "props": { "rows": [ { "code": "USD", "name": "US Dollar" }, { "code": "EUR", "name": "Euro" }, { "code": "GBP", "name": "British Pound" } ], "columns": [ { "field": "code", "title": "Currency Code" }, { "field": "name", "title": "Currency Name" } ] } } ``` ## v2:markdown Renders markdown content as formatted HTML using the platform's `KodexaMarkdown` renderer. Supports headings, lists, tables, code blocks, and all standard markdown syntax. Typically used as a child of `v2:serviceBridgeView` to display formatted text from bridge responses. ### Props | Prop | Type | Default | Description | | ------- | ------ | ---------- | ----------------------------------------------- | | content | string | -- | Markdown content to render | | size | string | `"medium"` | Size variant (`"small"`, `"medium"`, `"large"`) | ### Examples Static markdown: ```json theme={null} { "component": "v2:markdown", "props": { "content": "### Instructions\n\nReview the extracted values and correct any errors before approving." } } ``` Dynamic content from a bridge response: ```json theme={null} { "component": "v2:markdown", "bindings": { "content": "ctx.$bridgeResult?.analysisReport" } } ``` Markdown with size variant: ```json theme={null} { "component": "v2:markdown", "bindings": { "content": "ctx.$bridgeResult?.summary" }, "props": { "size": "small" } } ``` ## v2:knowledgeSection Displays knowledge base content within a form. It looks up a knowledge item by type from the workspace's document families, loads the item's markdown content, and renders it in a collapsible section. ### Props | Prop | Type | Default | Description | | ----------------- | ------ | -------- | --------------------------------------------- | | knowledgeItemType | string | required | The knowledge item type identifier to look up | The component handles three states: loading (with a spinner), not found (with an italicized message), and loaded (with an expandable section showing the title and rendered markdown). ### Example ```json theme={null} { "component": "v2:knowledgeSection", "props": { "knowledgeItemType": "processing-instructions" } } ``` The component searches across all document families in the current workspace for a knowledge item matching the specified type. The item's `instructionMarkdown` property is rendered as formatted HTML with support for headings, lists, tables, and code blocks. ## v2:exceptions Displays validation exceptions for data objects in the current scope. It aggregates exceptions from all data objects, filters by status and tag paths, and renders each exception as a compact card. ### Props | Prop | Type | Default | Description | | ------------ | --------- | ------- | ----------------------------------------------------------------------- | | title | string | -- | Optional heading above the exceptions list | | showResolved | boolean | false | Include resolved exceptions (by default only open exceptions are shown) | | filterPaths | string\[] | -- | Only show exceptions for these tag paths (shows all if omitted) | The component listens for `workspace:dataExceptionsUpdated` events and refreshes automatically when exceptions change. ### Example ```json theme={null} { "component": "v2:exceptions", "props": { "title": "Validation Issues", "filterPaths": ["Invoice/InvoiceNumber", "Invoice/InvoiceDate"] } } ``` ## v2:taxonNav Renders one colored chip per instance of a child taxon; clicking a chip scrolls the form to that instance. Place it at the top of a parent panel to give reviewers an at-a-glance list of what a record contains plus a one-click jump to each entry. Chip colors come from the taxonomy's conditional formats — the same source a stacked `v2:panel`'s `groupTintBy` reads — so a chip and the instance it jumps to always agree on color. ### Props | Prop | Type | Default | Description | | ---------- | ------ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | taxon | string | required | Taxon path of the instances to enumerate — one chip per instance | | labelFrom | string | required | Attribute path supplying each chip's text and the element the chip scrolls to | | tintBy | string | `labelFrom` | Attribute path whose taxonomy conditional-formatting colors the chip. Defaults to `labelFrom` — the thing you label by is usually the thing you color by. | | emptyLabel | string | `"Unnamed"` | Chip text when the label attribute has no value | | colSpan | number | -- | Column span in grid layout (1-12) | The chips deliberately sit outside the keyboard tab chain, so they add no stops during data entry. If you need a keyboard path, pair the component with form-level `shortcuts` instead. ### Example ```json theme={null} { "component": "v2:taxonNav", "props": { "taxon": "invoice/lineItems", "labelFrom": "invoice/lineItems/category", "emptyLabel": "Uncategorized" } } ``` A chip still renders for an instance whose label attribute doesn't exist yet (for example a freshly added row before its type is set), showing `emptyLabel`, so the reviewer can see the instance is there. ## v2:attributeSourceBadge A grid cell renderer that surfaces the source document(s) an attribute was extracted or promoted from, as one or more colored, clickable pills. For each attribute on the row at `tagPath`, it walks the attribute's spatial anchor (`tagId`) to the containing page, reads the preprocessor-set `document_type` + group sequence, and renders one badge per distinct `(document_type, group)` tuple. Clicking a badge dispatches `workspace.focusTag(tagId, viewId)`, highlighting the source content in the document viewer. ### Props | Prop | Type | Default | Description | | ------- | ------ | -------- | ----------------------------------------------------------------------------- | | tagPath | string | required | Path of the row attribute whose spatial anchor identifies the source document | Use the badge as a custom column on a `v2:grid` — its instance numbering (`"Bill of Lading #1, #2, …"`) is scoped per document type, so a form rendering multiple BoLs alongside multiple Invoices shows the right number for each pill regardless of how the underlying preprocessor sequences them. ### Example ```json theme={null} { "component": "v2:grid", "props": { "groupTaxon": "shipments/additionaldocumentnumbers", "columns": [ { "header": "Source", "component": "attributeSourceBadge", "width": 180, "props": { "tagPath": "shipments/additionaldocumentnumbers/docvalue" } } ] } } ``` ## v2:attributeCopyButton A grid cell renderer that promotes the row's attribute at `sourceTagPath` up to an ancestor scalar at `targetTagPath`. The source attribute's `tagId` is carried onto the target so click-to-source navigation in the document viewer keeps working after the copy. The target is also stamped with `ownerUri = user://`, exactly as a manual edit, so the edited-value indicator appears on the promoted field (blue when the source carried a `tagId`/document anchor, yellow -- shown as "Not from Document" -- when it did not). Click semantics: overwrites the destination silently and idempotently. If the target already has an attribute for that tag, it is updated in place (preserving its id so downstream watchers see a value change, not a delete + add). Duplicate target attributes left over from prior sessions are pruned automatically. ### Props | Prop | Type | Default | Description | | ------------- | ---------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sourceTagPath | string | required | Path of the row attribute whose value will be copied | | targetTagPath | string | required | Path of the ancestor scalar (e.g. `shipments/sidnumber`) that receives the value. The button walks up the row's parent chain until it finds the ancestor whose path matches the prefix of `targetTagPath`. | | tooltip | string | `"Copy to "` | Button tooltip text | | relatedCopies | `{sourceTagPath, targetTagPath}[]` | -- | Additional copy pairs executed in the same click after the primary copy. Use when promoting a value only makes sense alongside sibling fields — e.g. promoting a weight value requires the matching UOM. | ### Examples Single-field promotion: ```json theme={null} { "component": "v2:grid", "props": { "groupTaxon": "shipments/additionaldocumentnumbers", "columns": [ { "header": "", "component": "attributeCopyButton", "width": 60, "props": { "sourceTagPath": "shipments/additionaldocumentnumbers/docvalue", "targetTagPath": "shipments/sidnumber", "tooltip": "Copy to SID/DOC Number" } } ] } } ``` Multi-field promotion with `relatedCopies` — promotes weight and its UOM together: ```json theme={null} { "component": "attributeCopyButton", "props": { "sourceTagPath": "shipments/candidateweights/weightvalue", "targetTagPath": "shipments/shipweight", "tooltip": "Promote weight + UOM", "relatedCopies": [ { "sourceTagPath": "shipments/candidateweights/weightuom", "targetTagPath": "shipments/shipweightuom" } ] } } ``` ## v2:attributeCopyAction A scalar sibling-copy inline icon button for form layouts (not grid cells). Clicking it reads the value at `sourceTagPath` and writes it to a sibling scalar at `targetTagPath` on the same parent data object. Where [`v2:attributeCopyButton`](#v2-attributecopybutton) is a **grid cell renderer** that promotes a row attribute up to an ancestor scalar by walking the ag-grid row's parent chain, `v2:attributeCopyAction` resolves **both** paths at the form / data-context level — there is no grid row involved. Source and target must therefore live on the **same** parent taxonomy path (they are siblings). It sits alongside [`v2:attributeRowPromote`](#v2-attributerowpromote) (a row-level promote variant); use `v2:attributeCopyAction` for the form-layout, sibling-to-sibling case. ### Props | Prop | Type | Default | Description | | ------------- | ---------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | sourceTagPath | string | required | Scalar path to read the value from | | targetTagPath | string | required | Sibling scalar path to overwrite | | tooltip | string | `"Copy to "` | Button tooltip and aria-label | | relatedCopies | `{sourceTagPath, targetTagPath}[]` | -- | Extra sibling copy pairs run in the same click — e.g. copy a charge amount and its paired currency or unit scalar together | ### Behavior * **Value resolution** prefers `dataContext.formValues`, then the stored attribute (`decimalValue`, then `stringValue`), then a display-only formula-engine fallback for `FORMULA` taxons that have not persisted a value yet (the formula source is evaluated through the formula engine so the button works on stale-at-init documents). The fallback is never written back. * **The write always sets `stringValue`, `value`, and `decimalValue`** so number-typed targets (`NUMBER`, `CURRENCY`, `DECIMAL`, `INTEGER`, `PERCENTAGE`) re-render immediately rather than showing the stale prior string. * The source attribute's `tagId` is carried onto the target so click-to-source navigation keeps working, and the target is stamped with `ownerUri = user://` -- exactly as a manual edit -- so the edited-value indicator appears (blue when the source had a `tagId`/document anchor, yellow when it did not). * The button is **disabled while the source resolves to empty, zero, or non-numeric**, and shows a brief green checkmark on success. * An existing target attribute is updated in place (its id is preserved) and duplicate targets are pruned. ### Example In a plain form layout (not inside `v2:grid` columns), copy a computed line-items total into an editable total field, carrying the currency scalar along with it: ```json theme={null} { "component": "v2:attributeCopyAction", "props": { "sourceTagPath": "invoice/lineItemsTotal", "targetTagPath": "invoice/total", "tooltip": "Copy to Total", "relatedCopies": [ { "sourceTagPath": "invoice/lineItemsCurrency", "targetTagPath": "invoice/currency" } ] } } ``` ## v2:attributeRowPromote A grid cell renderer (a custom column on [`v2:grid`](#v2-grid)) that surfaces N promote destinations as a single **"Promote to…"** dropdown per row. It replaces the older pattern of one [`v2:attributeCopyButton`](#v2-attributecopybutton) chevron column per destination. How it differs from its siblings: `v2:attributeCopyButton` is one chevron button per single destination column; `v2:attributeRowPromote` collapses multiple destinations into one dropdown driven by a `targets` array, and adds an optional per-target `sourceTagPath` override. ([`v2:attributeCopyAction`](#v2-attributecopyaction) is the scalar form-button variant for non-grid layouts.) ### Props | Prop | Type | Default | Description | | ------------- | ---------------- | --------------- | ------------------------------------------------------------------------------------------------------------- | | sourceTagPath | string | required | Path of the row attribute promoted. Used as the default source for every target unless a target overrides it. | | targets | PromoteTarget\[] | required | The dropdown destinations (at least one entry) | | label | string | `"Promote to…"` | Trigger button text | The grid injects the cell context automatically, so authors do not set a `params` prop. Each entry in `targets` is a `PromoteTarget`: | Field | Type | Required | Description | | ------------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | label | string | yes | Dropdown item text, also shown in the green `"✓