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)
{
"metadata": {
"aiNaming": {
"enabled": true,
"prompt": "Generate a title for {templateName} processing {documentFamilyPaths}"
}
}
}
YAML (kdx sync)
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 |
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:
Title Resolution Fallback Chain
The platform uses the first available title from this ordered chain:
- Explicit title — If the caller provides a title directly, it is used as-is.
- AI naming — If
aiNaming.enabled is true and a prompt is configured, the LLM generates a title and description.
- Template rendering — If
defaultTitleTemplate or defaultDescriptionTemplate is set on the plan or template, it is rendered using Go template syntax with {{ .inputs.field }} placeholders.
- Plan or template name — The name of the Activity Plan or Task Template is used as the title.
- 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”
The LLM must respond with a JSON object containing title and description fields:
{
"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.
# 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
kind: EXECUTION
config:
moduleRef: kodexa/invoice-extractor
options:
documentFamilyId: "{{ .inputs.documentFamilyId }}"