---
tags: [ai-agent, agentic-workflow, agent-studio, sn_aia_agent, sn_aia_usecase, ai-automation, tools, triggers, authentication]
---

# Building AI Agents Guide

Build and configure ServiceNow AI Agents and AI Agentic Workflows using the Fluent SDK. AI Agents perform tasks with tools (CRUD, script, OOB, reference-based), while AI Agentic Workflows orchestrate multiple agents as a team. This guide covers end-to-end creation: authentication, tool configuration, instructions authoring, triggers, ACL deployment, and editing existing agents. Requires SDK 4.4.0 or higher.

---

## When to Use

- Creating a new AI Agent in ServiceNow
- Creating AI Agentic Workflows that orchestrate multiple agents
- Modifying existing agents or agentic workflows (adding tools, changing auth, updating instructions)
- Configuring agent authentication and security (Dynamic User vs AI User)
- Selecting and configuring agent tools (CRUD, script, OOB tools)
- Setting up triggers for agents or agentic workflows
- Defining team members for agentic workflows

---

## Workflow vs Single Agent Decision Tree

```
User Request
    |
Does it involve multiple tasks? (AND, THEN, followed by)
    | NO  -> USE SINGLE AI AGENT
    | YES
        |
    Do the tasks require DIFFERENT capability types?
    (e.g., search + summarize, fetch from table A + update table B)
        | NO  -> USE SINGLE AI AGENT (multiple tools, one agent)
        | YES -> USE AI AGENTIC WORKFLOW
```

**Pattern Recognition:**

| User Says | Type | Why |
|-----------|------|-----|
| "Fetch X AND do Y" (different capabilities) | Workflow | Different capability types working together |
| "Get data THEN process it" (different agents) | Workflow | Sequential operations needing different specializations |
| "Look up and update an incident" | Single Agent | Same table, same capability type (CRUD), multiple tools |
| "Search for incidents by priority" | Single Agent | Single task |

**Key distinction:** Multiple _tools_ on the same table or same capability type = single agent with multiple tools. Multiple _capability types_ requiring different specializations = workflow with multiple agents.

---

## AI Agent vs AI Agentic Workflow

| Feature | AI Agent | AI Agentic Workflow |
|---------|----------|---------------------|
| Purpose | Single agent performing tasks | Multiple agents working as a team |
| Import | `AiAgent` from `@servicenow/sdk/core` | `AiAgenticWorkflow` from `@servicenow/sdk/core` |
| Configuration | `tools` array | `team: { $id, name, members: [...] }` |
| Version array | `versionDetails` | `versions` |
| Record identity | `$id` (explicit ID) | `$id` (explicit ID) |
| Security | `securityAcl` (mandatory, auto-generates ACL) | `securityAcl` (mandatory, auto-generates ACL) |
| Run-as user | `runAsUser` | `runAs` |
| Execution mode | `executionMode` on tools | `executionMode` at workflow level (default: `'copilot'`) |
| Trigger channel | `'nap'` / `'nap_and_va'` (agent-level `channel`) | `"Now Assist Panel"` (trigger-level, mandatory) |
| Processing messages | `processingMessage`, `postProcessingMessage` | Not available |

---

## Security ACL (`securityAcl`)

`securityAcl` is **mandatory** on both `AiAgent` and `AiAgenticWorkflow`. It controls **who can invoke** the agent/workflow and auto-generates the `sys_security_acl` and `sys_security_acl_role` records. It is a discriminated union on the `type` field — each variant also requires a `$id` to identify the generated ACL record.

### Access Types

| `type` | Who can invoke | Extra fields |
|--------|----------------|--------------|
| `'Any authenticated user'` | Any logged-in user | None |
| `'Specific role'` | Only users with listed roles | `roles: [...]` (required) |
| `'Public'` | Anyone, no auth required | None |

```typescript fluent
// Any authenticated user
securityAcl: {
    $id: Now.ID['my_agent_acl'],
    type: 'Any authenticated user',
}

// Specific roles only
securityAcl: {
    $id: Now.ID['my_agent_acl'],
    type: 'Specific role',
    roles: [
        '282bf1fac6112285017366cb5f867469',  // itil role sys_id
        'b05926fa0a0a0aa7000130023e0bde98',  // user role sys_id
    ]
}
```

> **Important distinction:** `securityAcl` controls *who can invoke* the agent. `runAsUser` (agent) / `runAs` (workflow) and `dataAccess` are separate — they control *which user identity the agent runs under* when executing.

### Execution Identity (`runAsUser` / `dataAccess`)

Set **either** `runAsUser` (agent) or `dataAccess` — not both:

- **`runAsUser`** — agent always runs as the specified sys_user sys_id regardless of invoker
- **`dataAccess.roleMap`** (role names) or **`dataAccess.roleList`** (role sys_ids) — agent runs as the invoking user, restricted to the listed roles. **Required when `runAsUser` is not set.** For workflows, use `dataAccess` when `runAs` is not set.

### Role Discovery

1. Identify target tables from the agent's CRUD tools
2. Query `sys_security_acl_role` for each table (encodedQuery: `sys_security_acl.nameLIKE<table_name>`)
3. Add discovered role **names** to `dataAccess.roleMap`, or role **sys_ids** to `dataAccess.roleList` and `securityAcl.roles`

### Common Role sys_ids

| Role | sys_id |
|------|--------|
| admin | `2831a114c611228501d4ea6c309d626d` |
| itil | `282bf1fac6112285017366cb5f867469` |
| user | `b05926fa0a0a0aa7000130023e0bde98` |

---

## Tool Types and Selection

### Selection Priority

1. **OOB tools** when available (e.g., Web Search, RAG, Knowledge Graph)
2. **Reference-based tools** (action, subflow, capability, catalog, topic)
3. **CRUD tools** for database operations
4. **Script tools** only when no other tool type fits

Never use CRUD tools for journal fields (`work_notes`, `comments`). Always use Script tools with `GlideRecordSecure`.

### Tool Selection Guide

| Need | Tool Type | Why |
|------|-----------|-----|
| Read/search records | CRUD (`lookup`) | Direct table query |
| Create new records | CRUD (`create`) | Maps inputs to columns |
| Modify records | CRUD (`update`) | Query + field update |
| Custom logic | Script | Full JavaScript control |
| Web information | OOB (`web_automation`) | Auto-linked OOB tool |
| Semantic/keyword search | RAG (`rag`) | Structured search with ValueLabelType inputs |
| Graph-based search | OOB (`knowledge_graph`) | Knowledge Graph tool |
| File ingestion | OOB (`file_upload`) | File Uploader tool |
| In-depth research | OOB (`deep_research`) | Deep Research tool |
| Desktop tasks | OOB (`desktop_automation`) | Desktop Automation tool |
| MCP integration | OOB (`mcp`) | MCP tool |
| Flow Designer action | Action (`action`) | Triggers existing flows |
| Now Assist skill | Capability (`capability`) | Links to skills |

### `inputs` Format by Tool Type

| Tool type | `inputs` format | Has `script` field? |
|-----------|-----------------|---------------------|
| `crud` | **Object** (`ToolInputType`) with `operationName`, `table`, `inputFields`, etc. | No (auto-generated) |
| `rag` | **Object** (`RagInputType`) with `searchType`, `searchProfile`, `sources`, etc. (all using `ValueLabelType`) | No (auto-generated) |
| `script` | **Array** of `[{ name, description, mandatory, value? }]` | Yes |
| `web_automation` | Omit (plugin provides defaults) | No |
| Reference types | Omit (platform resolves at runtime) | No |
| Other OOB types | Omit (plugin provides defaults) | No |

### Required Tool Properties

Every tool **must** have `name` and `type`. `preMessage` and `postMessage` are strongly recommended. Every agent **must** have `processingMessage` and `postProcessingMessage` (not available on workflows).

---

## CRUD Tools

### Operations

| Operation | Required Fields |
|-----------|----------------|
| `create` | `table`, `inputFields` with `mappedToColumn` |
| `lookup` | `table`, `queryCondition`, `returnFields` (mandatory) |
| `update` | `table`, `queryCondition`, `inputFields` with `mappedToColumn` |
| `delete` | `table`, `queryCondition` |

### queryCondition Syntax

Format: `"column_name=={{input_field_name}}"`. Always verify column names by querying `sys_dictionary` before writing tools.

| Operator | Syntax | Example |
|----------|--------|---------|
| Equals | `field=value` | `state=1` |
| Not equals | `field!=value` | `state!=7` |
| Contains | `fieldLIKEvalue` | `short_descriptionLIKEnetwork` |
| Is empty | `fieldISEMPTY` | `assigned_toISEMPTY` |
| OR | `^OR` | `priority=1^ORpriority=2` |

### Lookup Best Practices

- Use `LIKE` operator for text-based searches
- Use multiple keyword inputs with `OR` for natural language
- Prefer `number` over `sys_id` as primary filter: `number={{id}}^ORsys_id={{id}}`
- For reference fields in `returnFields`, include `referenceConfig`: `{ table: "sys_user", field: "name" }`

---

## Script Tools

All script inputs are **strings** at runtime. Parse with `parseInt()`, `JSON.parse()`, etc. The `inputs` field for script tools is a **simple array** of input field definitions (unlike CRUD tools which use an object).

```javascript
(function(inputs) {
    var impact = parseInt(inputs.impact, 10);
    var urgency = parseInt(inputs.urgency, 10);
    // ... logic
    return { priority: result, status: 'success' };
})(inputs);
```

- Always use `GlideRecordSecure` (not `GlideRecord`)
- Do NOT add CDATA tags (plugin handles automatically)
- `inputSchema` is auto-generated from `inputs` — do not specify it manually
- Use module imports for server-side script files (or `Now.include()` for legacy scripts)

---

## Reference-Based Tools

Each requires a type-specific reference field containing the target record's sys_id. Do NOT add `inputs`.

| Tool Type | Required Field | Target Table |
|-----------|----------------|--------------|
| `action` | `flowActionId` | `sys_hub_action_type_definition` |
| `capability` | `capabilityId` | `sn_nowassist_skill_config` |
| `subflow` | `subflowId` | `sys_hub_flow` |
| `catalog` | `catalogItemId` | `sc_cat_item` |
| `topic` | `virtualAgentId` | `sys_cs_topic` |
| `topic_block` | `virtualAgentId` | `sys_cs_topic` |

---

## OOB Tools

OOB tools only require `type` and `name`. The plugin auto-links to the existing OOB tool record.

```typescript fluent
{
    type: 'web_automation',
    name: 'AIA Web Search',
    preMessage: 'Searching the web...',
    postMessage: 'Web search results retrieved.'
}
```

Other supported OOB types: `'rag'`, `'knowledge_graph'`, `'file_upload'`, `'deep_research'`, `'desktop_automation'`, `'mcp'`.

---

## RAG (Search Retrieval) Tools

RAG tools enable semantic search and document retrieval from ServiceNow tables. The `type` for RAG tools is `'search_retrieval'` and requires structured `inputs` configuration.

### Search Types

| Type | Description | Required Fields |
|------|-------------|----------------|
| `'keyword'` | Simple text matching | None |
| `'semantic'` | Semantic search using embeddings | `semanticIndexes` (array of ValueLabelType) |
| `'hybrid'` | Combines keyword and semantic | `semanticIndexes` (array of ValueLabelType) |

**Note:** The `query` description is generated using the `searchProfile.label`. `semanticIndexes` and `documentMatchThreshold` are only included in the generated schema for `semantic` and `hybrid` search types. `fields` and `searchResultsLimit` apply to all search types.

### RAG Tool Example (Semantic Search)

```typescript fluent
{
    name: 'Semantic Knowledge Search',
    description: 'Searches knowledge articles using semantic search',
    type: 'search_retrieval',
    recordType: 'custom',
    executionMode: 'autopilot',
    preMessage: 'Performing semantic search...',
    postMessage: 'Semantic search completed.',
    inputs: {
        searchType: {
            type: 'semantic',
            semanticIndexes: [
                { value: 'kb_knowledge_text_index', label: 'Knowledge Base Text Index' }
            ],
            documentMatchThreshold: 0
        },
        searchProfile: {
            value: 'quick_action_kb_search_profile',
            label: 'Quick Action - KB Search Profile'
        },
        sources: [
            { value: 'kb_knowledge', label: 'Knowledge Articles' }
        ],
        fields: [
            { value: 'kb_knowledge.short_description', label: 'Short description [kb_knowledge]' },
            { value: 'kb_knowledge.text', label: 'Article body [kb_knowledge]' },
            { value: 'kb_knowledge.number', label: 'Number [kb_knowledge]' }
        ],
        searchResultsLimit: 5
    }
}
```

### Search Type Comparison

| Property | `keyword` | `semantic` | `hybrid` |
|----------|-----------|------------|----------|
| `searchType.type` | `'keyword'` | `'semantic'` | `'hybrid'` |
| `semanticIndexes` | Not applicable | Required | Required |
| `documentMatchThreshold` | Not applicable | Optional (default: 0) | Optional (default: 0) |
| `searchProfile` | Required | Required | Required |
| `sources` | Optional | Optional | Optional |
| `fields` | Optional | Optional | Optional |
| `searchResultsLimit` | Optional | Optional | Optional |

---

## Instructions Authoring

### Three Key Fields

| Field | Purpose | Answers |
|-------|---------|---------|
| `description` | Scope | "What problem does this agent solve?" |
| `agentRole` | Identity (agents only) | "Who am I?" |
| `instructions` | Behavior | "How should I act and use my tools?" |

### Writing Principles

- **Actionable steps**: Every step must bind to a tool action or concrete output
- **Explicit tool references**: Name tools explicitly in instructions
- **Contingencies**: Handle failures with gates: `"DO NOT PROCEED if details are missing"`
- **Trigger context**: Use "from the task" or "from the context" -- NOT "from the triggering record"

### Scaling by Complexity

| Complexity | Tools/Agents | Instructions Length |
|------------|--------------|---------------------|
| Simple | 1-2 tools | 5-10 lines |
| Moderate | 3-4 tools | 10-20 lines |
| Complex | 5+ tools | 20-30 lines |
| Workflow | 2-10 agents | 15-30 lines |

If instructions exceed ~30 lines, split into multiple agents orchestrated by a workflow.

---

## Trigger Configuration

Triggers are optional. Agents/workflows can operate without them via Now Assist Panel.

### Trigger Types

| Type | Description |
|------|-------------|
| `record_create` | On new record creation |
| `record_update` | On record update |
| `record_create_or_update` | On both |
| `email` | On email receipt |
| `scheduled` | On a repeating interval |
| `daily` / `weekly` / `monthly` | Scheduled at specific times |
| `ui_action` (workflows only) | From a UI action button |

### Key Differences: Agent vs Workflow Triggers

| Property | Agent trigger | Workflow trigger |
|----------|--------------|------------------|
| `channel` | `"nap"` or `"nap_and_va"` | `"Now Assist Panel"` (mandatory) |
| `triggerCondition` | Optional | Mandatory for record-based |
| `objectiveTemplate` | Required (defaults to `""`) | Optional |

### Scheduled Trigger Fields

The `schedule` object is used when `triggerFlowDefinitionType` is `'scheduled'`, `'daily'`, `'weekly'`, or `'monthly'`.

| Type | Required Fields (inside `schedule` object) |
|------|---------------------------------------------|
| `daily` | `schedule.time` |
| `weekly` | `schedule.runDayOfWeek` (1=Sun to 7=Sat), `schedule.time` |
| `monthly` | `schedule.runDayOfMonth` (1-31), `schedule.time` |
| `scheduled` | `schedule.repeatInterval` (e.g., `'1970-01-05 12:00:00'` = every 5 days) |

Time format: `"1970-01-01 HH:MM:SS"`.

The `schedule.triggerStrategy` field controls repeat behavior. Values differ by entity type:

| Entity | Valid `triggerStrategy` values |
|--------|-------------------------------|
| AI Agent | `'every'`, `'once'`, `'unique_changes'`, `'always'` |
| AI Agentic Workflow | `'every'`, `'immediate'`, `'manual'`, `'once'`, `'repeat_every'`, `'unique_changes'` |

### Run-As Configuration

For record-based triggers, use one of:
- `runAs: "<column_name>"` — column-based (the column on the target table that holds the user reference)
- `runAsUser: "<sys_id>"` — run as a specific user
- `runAsScript` — a script returning a user sys_id for dynamic resolution

---

## ACL Deployment

Both AI Agents and AI Agentic Workflows use `securityAcl`. The plugin **automatically generates** `sys_security_acl` and `sys_security_acl_role` records — no manual two-step deployment is needed.

The generated ACL name follows the format: `{domain}.{scope}.{name}`

```typescript fluent
// Works the same for both AiAgent and AiAgenticWorkflow
securityAcl: {
    $id: Now.ID['my_agent_acl'],
    type: 'Specific role',
    roles: [
        '282bf1fac6112285017366cb5f867469',  // itil
        'b05926fa0a0a0aa7000130023e0bde98'   // user
    ]
}
```

---

## Workflow Configuration

### Team Structure

Workflows use `$id` for record identity (just like agents). The `team` object also requires its own `$id`.

```typescript fluent
team: {
    $id: Now.ID["workflow_team"],  // MANDATORY on team
    name: "Workflow Team",
    // description is auto-populated from workflow.description — do not set it
    members: [
        "62826bf03710200044e0bfc8bcbe5df1"  // Agent sys_id from sn_aia_agent table
    ]
}
```

Team members can also be specified using the `Record` API (recommended for portability across instances):

```typescript fluent
import { AiAgenticWorkflow, Record } from '@servicenow/sdk/core'

const lookupAgent = Record({ table: 'sn_aia_agent', $id: Now.ID['lookup_agent'], data: { name: 'Lookup Agent' } })
const analysisAgent = Record({ table: 'sn_aia_agent', $id: Now.ID['analysis_agent'], data: { name: 'Analysis Agent' } })

AiAgenticWorkflow({
    // ...
    team: {
        $id: Now.ID['my_team'],
        name: 'My Team',
        members: [lookupAgent, analysisAgent],
    },
})
```

Agents **must** be deployed before creating workflows.

### Workflow-Level Fields

| Field | Type | Default | Notes |
|-------|------|---------|-------|
| `executionMode` | `'copilot' \| 'autopilot'` | `'copilot'` | Execution mode for the workflow |
| `memoryScope` | `string` | `'global'` | Memory scope for team members |
| `active` | `boolean` | `true` | Omit if active (default) |
| `sysDomain` | `string` | `'global'` | Omit if global (default) |

Only specify fields that differ from their defaults — the plugin suppresses default values in the transform output.

### Deployment Order

1. Create and deploy AI Agents (with `securityAcl`)
2. Get agent sys_ids from `sn_aia_agent`
3. Create and deploy workflow with `securityAcl` and agent sys_ids
4. Verify with `run_query` on `sn_aia_usecase`

### contextProcessingScript

Supports inline scripts or `Now.include()` for external files:

```javascript
(function(task, user_utterance, workflow_id, context) {
    return {
        pageContext: context?.pageContext,
        triggerContext: context?.triggerContext
    };
})(task, user_utterance, workflow_id, context);
```

```typescript fluent
// Preferred: external file
contextProcessingScript: Now.include('./context-processing-script.js')
```

---

## Complete AI Agent Example

```typescript fluent
import { AiAgent } from "@servicenow/sdk/core";

export const incidentHelperAgent = AiAgent({
    $id: Now.ID["incident_helper_agent"],
    name: "Incident Helper Agent",
    description: "Retrieves incident details and searches for resolution guidance.",
    agentRole: "You are an ITSM incident specialist.",

    // Security — auto-generates ACL records
    securityAcl: {
        $id: Now.ID['incident_helper_agent_acl'],
        type: 'Specific role',
        roles: [
            '282bf1fac6112285017366cb5f867469',  // itil
            'b05926fa0a0a0aa7000130023e0bde98'   // user
        ]
    },

    agentDescriptor: 'created_by_build_agent',
    channel: 'nap_and_va',
    recordType: 'custom',
    processingMessage: "Analyzing your incident request...",
    postProcessingMessage: "Incident analysis complete.",

    versionDetails: [{
        name: "V1",
        number: 1,
        state: "published",
        instructions: `Step 1: Extract the incident number from the user's request.
Step 2: Use the Lookup Incident tool to fetch details.
Step 3: Present details in bullet-point format.
Step 4: Use AIA Web Search for resolution guidance.
Step 5: Recommend next steps. NEVER modify without user approval.`
    }],

    tools: [
        {
            active: true,
            name: "Lookup Incident",
            description: "Searches for incidents by number",
            executionMode: "autopilot",
            type: "crud",
            recordType: "custom",
            preMessage: "Searching for the incident...",
            postMessage: "Incident details retrieved.",
            inputs: {
                operationName: "lookup",
                table: "incident",
                inputFields: [
                    { name: "incident_number", description: "Incident number", mandatory: false }
                ],
                queryCondition: "number={{incident_number}}",
                returnFields: [
                    { name: "number" },
                    { name: "short_description" },
                    { name: "state" },
                    { name: "priority" },
                    { name: "assigned_to", referenceConfig: { table: "sys_user", field: "name" } }
                ]
            }
        },
        {
            type: "web_automation",
            name: "AIA Web Search",
            active: true,
            preMessage: "Searching the web...",
            postMessage: "Web search results retrieved."
        }
    ],

    triggerConfig: []
});
```

## Complete Workflow Example

```typescript fluent
import { AiAgenticWorkflow } from "@servicenow/sdk/core";

export const incidentAnalysisWorkflow = AiAgenticWorkflow({
    $id: Now.ID["incident_analysis_workflow"],
    name: "Incident Analysis Workflow",
    description: "Orchestrates two agents to retrieve and analyze incidents.",
    recordType: "custom",

    // Security — auto-generates ACL records (mandatory)
    securityAcl: {
        $id: Now.ID['incident_analysis_workflow_acl'],
        type: 'Specific role',
        roles: [
            '282bf1fac6112285017366cb5f867469',  // itil
            'b05926fa0a0a0aa7000130023e0bde98'   // user
        ]
    },

    // dataAccess required when runAs is omitted (dynamic user identity)
    // Use roleMap (role names) or roleList (role sys_ids)
    dataAccess: {
        roleMap: ['itil', 'user']
    },

    team: {
        $id: Now.ID["incident_analysis_team"],
        name: "Incident Analysis Team",
        // description is auto-populated from workflow description
        members: [
            "62826bf03710200044e0bfc8bcbe5df1",
            "274b465a7d5f42e581664209557b2b18"
        ]
    },

    versions: [{
        name: "V1",
        number: 1,
        state: "published",
        instructions: `Step 1: Use the Incident Lookup Agent to fetch details.
Step 2: Use the Analysis Agent to examine patterns.
Step 3: Present findings. NEVER modify without user approval.`
    }],

    triggerConfig: [{
        name: "high_priority_incident",
        channel: "Now Assist Panel",
        targetTable: "incident",
        triggerFlowDefinitionType: "record_create",
        triggerCondition: "priority<=2",
        objectiveTemplate: "Analyze high priority incident ${number}",
        showNotifications: true,
        runAsScript: `(function(current) {
            return current.assigned_to || "6816f79cc0a8016401c5a33be04be441";
        })(current);`
    }]
});
```

---

## Editing Existing Agents/Workflows

### Safe Edit Workflow

1. Locate the `.now.ts` file
2. Read the entire current configuration
3. Identify scope of changes (see Change Impact Matrix)
4. Apply only the requested changes
5. Redeploy and verify with `run_query`

### Change Impact Matrix

| Change | Also Update |
|--------|-------------|
| Add CRUD tool | Verify columns, update instructions, check executionMode |
| Remove tool | Remove instruction references, check dependencies |
| Change auth mode | Update `securityAcl.type`, add/remove `roles` as needed |
| Add trigger | Add to `triggerConfig`, verify target table |
| Add team member (workflow) | Deploy agent first, get sys_id, update instructions |
| Update instructions | Ensure all referenced tool/agent names still exist |

### Rollback

Set current published version to `state: "withdrawn"`, set previous version to `state: "published"`, redeploy.

---

## Validation and Enums

### Required Fields

| Entity | Mandatory Fields |
|--------|-----------------|
| AI Agent | `$id`, `name`, `description`, `agentRole`, `securityAcl` |
| AI Agentic Workflow | `$id`, `name`, `description`, `securityAcl`, `team.$id` |
| CRUD tool | `name`, `type`, `inputs.operationName`, `inputs.table`, `inputs.inputFields` |
| Script tool | `name`, `type`, `script` |

### Valid Enums

| Property | Valid Values |
|----------|-------------|
| `recordType` (agent) | `"custom"`, `"template"`, `"aia_internal"`, `"promoted"` (default: `"template"`) |
| `recordType` (workflow) | `"custom"`, `"template"`, `"aia_internal"` (default: `"template"`) |
| `executionMode` (tool) | `"autopilot"`, `"copilot"` |
| `executionMode` (workflow) | `"autopilot"`, `"copilot"` (default: `"copilot"`) |
| `state` (version) | `"draft"`, `"committed"`, `"published"`, `"withdrawn"` (default: `"draft"`) |
| `tool.type` | `"script"`, `"crud"`, `"capability"`, `"subflow"`, `"action"`, `"catalog"`, `"topic"`, `"topic_block"`, `"web_automation"`, `"rag"`, `"knowledge_graph"`, `"file_upload"`, `"deep_research"`, `"desktop_automation"`, `"mcp"` |
| `securityAcl.type` | `'Any authenticated user'`, `'Specific role'`, `'Public'` |
| `agentDescriptor` | `"require_caller_id"`, `"created_by_ai_agent_advisor"`, `"created_by_build_agent"`, `""` (default: `""`) |
| `agentType` | `"internal"`, `"external"`, `"voice"`, `"aia_internal"` |
| `channel` (agent) | `"nap"`, `"nap_and_va"` (default: `"nap_and_va"`) |
| `schedule.triggerStrategy` (agent) | `"every"`, `"once"`, `"unique_changes"`, `"always"` |
| `schedule.triggerStrategy` (workflow) | `"every"`, `"immediate"`, `"manual"`, `"once"`, `"repeat_every"`, `"unique_changes"` |
| `outputTransformationStrategy` | `"abstract_summary"`, `"custom"`, `"none"`, `"paraphrase"`, `"summary"`, `"summary_for_search_results"` |

### Common Hallucinations to Avoid

| Wrong | Correct |
|-------|---------|
| `"standard"` | `"custom"` (recordType) |
| `"automatic"` | `"autopilot"` (executionMode) |
| `"active"` | `"published"` (state) |
| `"database"` | `"crud"` (tool.type) |
| `versions` (for agents) | `versionDetails` |
| `versionDetails` (for workflows) | `versions` |
| `runAs` (for agents) | `runAsUser` |
| `"nap"` (for workflow triggers) | `"Now Assist Panel"` |
| `acl: "..."` (for agents or workflows) | `securityAcl: { $id, type, roles? }` (mandatory for both) |
| Missing `securityAcl` on workflows | `securityAcl` is mandatory for workflows too, not just agents |
| `securityAcl: { userAccess: 'dynamic_user' }` | `securityAcl: { $id, type: 'Any authenticated user' \| 'Specific role' \| 'Public' }` |
| Missing `$id` at workflow top level | Workflows use `$id` just like agents — always include it |
| `processingMessage` on workflows | Agent-only field — not valid on `AiAgenticWorkflow` |
| `postProcessingMessage` on workflows | Agent-only field — not valid on `AiAgenticWorkflow` |
| `team.description` set manually | Auto-populated from workflow `description` — do not set |
| Manual `inputSchema` | Auto-generated from `inputs` — never set manually |
| `inputs: {...}` for script tools | `inputs: [...]` (array, not object) for script tools |
| `dataAccess` omitted when `runAs` absent (workflow) | `dataAccess` is mandatory for workflows when `runAs` is not set |
| `searchProfile: "profile_name"` (RAG) | `searchProfile: { value: "profile_name", label: "Display Name" }` (ValueLabelType required) |
| `sources: ["table1", "table2"]` (RAG) | `sources: [{ value: "table1", label: "Label1" }, ...]` (ValueLabelType array required) |
| `semanticIndexes: ["index1"]` (RAG) | `semanticIndexes: [{ value: "index1", label: "Label1" }]` (ValueLabelType array required) |
| `fields: ["field1", "field2"]` (RAG) | `fields: [{ value: "field1", label: "Label1" }, ...]` (ValueLabelType array required) |

---

## Error Recovery

| Error Pattern | Category | Resolution |
|---------------|----------|------------|
| `dataAccess must have at least one of roleList or roleMap` | Missing roles | Add `dataAccess.roleMap` (role names) or `dataAccess.roleList` (role sys_ids) — required when `runAsUser`/`runAs` is not set |
| `Table not found` | Bad table name | Query `sys_db_object` to verify |
| `Record not found` / `Invalid reference` | Bad sys_id | Query appropriate table for correct sys_id |
| `Duplicate name` | Name collision | Query both `sn_aia_agent` and `sn_aia_usecase` |
| ACL / permission error | ACL misconfiguration | Verify `securityAcl` is set correctly |

---

## Database Tables

| Table | Purpose |
|-------|---------|
| `sn_aia_agent` | AI Agent records |
| `sn_aia_agent_config` | Agent configuration and settings |
| `sn_aia_usecase` | AI Agentic Workflow records |
| `sn_aia_usecase_config_override` | Configuration overrides for workflows |
| `sn_aia_team` | Team configuration |
| `sn_aia_team_member` | Team member records |
| `sn_aia_version` | Version information |
| `sn_aia_tool` | Tool definitions |
| `sn_aia_agent_tool_m2m` | Agent-to-tool relationships |
| `sn_aia_trigger_configuration` | Trigger configuration |
| `sn_aia_trigger_agent_usecase_m2m` | Trigger-agent-usecase mappings |
| `sys_agent_access_role_configuration` | Role-based data access controls |
| `sys_security_acl` | ACL records (auto-generated by `securityAcl`) |
| `sys_user_role` | Role records |
