# Agent Blueprint Language (ABL) Source: https://koreai.mintlify.app/agent-platform/abl Agent Blueprint Language (ABL™) is the typed, schema-driven language used to define agents on the platform. Every agent — its identity, tools, memory, routing, constraints, and guardrails — is expressed in a single ABL definition. The compiler validates it. The runtime executes it across both cognitive engines. ABL definitions are YAML artifacts. The same blueprint compiles to the same abstract syntax tree (AST) and intermediate representation (IR), whether authored directly in ABL syntax or generated by Arch™ AI. ## What ABL defines A single ABL definition covers the full specification of an agent. Each construct maps to a runtime capability enforced by the compiler. | Construct | What It Specifies | | - | - | | **Identity** | Agent name, type, persona, and goals. | | **Execution** | LLM model, temperature, iteration limits, and timeout. | | **Tools** | Typed function signatures the agent can call, with input/output contracts. | | **Memory** | Short-term session state and long-term persistent state. | | **Handoffs** | Typed routing to specialist agents, including context grants and return expectations. | | **Delegates** | Inline sub-agent calls that answer a sub-question without transferring thread ownership. | | **Constraints** | `REQUIRE` / `ON_FAIL` business rules enforced at runtime. | | **Guardrails** | Input and output policy checks — PII redaction, topic scope, hallucination guards — compiled into the agent, not added externally. | | **Flow** | Optional step-by-step orchestration for interactions where the execution path is completely deterministic. | ## Compilation pipeline ABL definitions move through three stages before they execute. The ABL source is the diffable artifact stored in version control. The compiler produces a portable IR that is versioned and pinned to a deployment, and can be downloaded. The runtime executes the IR — not the raw ABL source. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A["ABL Definition\n(.agent.abl / YAML)\ndiffable"] --> B["ABL Compiler\nvalidates schema\nchecks references\nenforces contracts"] B --> C["IR\nportable artifact\nversioned · pinned to deployment · downloadable"] C --> D["ABL Runtime\nexecutes both\ncognitive engines"] ``` | Stage | What Happens | | - | - | | **ABL Compiler** | Validates the full definition. Catches missing tool references, broken handoffs, contract mismatches, and guardrail gaps before deployment. | | **IR** | The compiled artifact. Environment-agnostic — the same IR runs in dev, staging, pilot, and production without modification. | | **ABL Runtime** | Executes the IR. Runs the Agentic Brain for reasoning steps and the Deterministic Brain for constraint-enforced paths. Both share the same runtime. | Compile-time validation runs on every commit. Broken definitions never reach a downstream environment. ## ABL compared to framework code On framework-based stacks, building a production agent means stitching together prompts, code, and connectors across multiple languages and SDKs. The orchestration logic seems like glued code that's difficult to review, difficult to test, and brittle at scale. ABL replaces glued code with a single declarative definition. Constructs like `HANDOFF`, `DELEGATE`, `FAN_OUT`, and `GUARDRAILS` are first-class primitives that the compiler enforces and the runtime executes, rather than patterns re-implemented per project. | Capability | Framework Code | ABL | | - | - | - | | **Compile-time validation** | Not available — errors surface at runtime. | Built in — Detect missing tools, broken references, and contract violations before deployment. | | **Version control and diff** | Mixed across code, prompt files, and configuration — diffs are noisy. | Clean YAML diffs — agent changes review like application code. | | **Cross-runtime portability** | Locked into the framework's runtime. | IR-based — the same definition runs anywhere the runtime is deployed. | | **AI authoring** | Fragile — AI-generated framework code breaks at scale. | Reliable — ABL has a strict schema that AI can generate, validate, and modify consistently. | | **Governance** | Per-project — each team reinvents guardrails. | Uniform — one policy layer across every agent, regardless of who authored it. | ## Author ABL You can author ABL in three ways and all produce the same compiled output. | Method | How It Works | | - | - | | **Arch AI** | The default experience. Arch AI interviews stakeholders, proposes agent topology, generates ABL, and validates the output against the compiler before presenting it for human review. No manual ABL writing required. | | **Studio IDE** | The Monaco-based code editor in Agent Studio. Provides syntax highlighting, inline diagnostics, and an outline view for navigating large definitions. | | **External tools** | Claude Code, Cursor, and Codex connect to the platform through MCP. Agents can be authored from a CLI or IDE using the same MCP interface as the Studio. | ## ABL example The example below shows a funds-transfer agent. It illustrates the key constructs — identity, execution parameters, typed tools, memory scope, handoffs, constraints, guardrails, and flow steps. The `REASONING: true` flag on a flow step activates the Agentic Brain for that step; `REASONING: false` runs the Deterministic Brain. ```yaml expandable=true theme={null} AGENT: Funds_Transfer_Agent VERSION: "3.1.0" DESCRIPTION: "Orchestrates domestic and international funds transfers with fraud screening, compliance checks, FX conversion, and full audit trail." GOAL: | Guide the customer through a secure funds transfer end-to-end. Verify identity, check limits, screen for fraud, apply FX if needed, get explicit confirmation, execute, and notify all parties. Escalate to a human advisor for high-value or flagged transfers. EXECUTION: model: claude-opus-4-7 temperature: 0.1 // low temperature — financial precision required max_turns: 12 timeout_seconds: 180 TOOLS: verify_customer(customer_id: string, mfa_token?: string) → { verified: bool, tier: string, daily_limit: number } screen_fraud(customer_id: string, transfer: object) → { risk_score: number, flags: array, decision: string } confirm: never // always runs silently execute_transfer(customer_id: string, from_account: string, to_account: string, amount: number, currency: string) → { transfer_id: string, status: string, eta: string } confirm: always // always prompts customer before executing MEMORY: session: - customer_id - from_account - to_account - amount - currency - fraud_result - transfer_result HANDOFF: - TO: Senior_Advisor_Agent WHEN: amount > 50000 OR fraud_result.decision == "REVIEW" CONTEXT: { pass: [customer_id, transfer_details, fraud_result] } RETURN: true CONSTRAINTS: always: - REQUIRE customer.verified == true ON_FAIL: HANDOFF Authentication_Agent - REQUIRE fraud_result.decision != "BLOCK" ON_FAIL: RESPOND "This transfer has been flagged and cannot proceed." THEN: COMPLETE GUARDRAILS: input: topic_scope: allowed: [funds_transfer, account_balance, fx_query, dispute] on_out_of_scope: RESPOND "I can only assist with funds transfers and related queries." output: mask_account_numbers: pattern: account_number_pattern action: show_last_4_only FLOW: entry_point: verify_identity steps: [verify_identity, collect_transfer_details, screen, confirm, execute] verify_identity: REASONING: false CALL: verify_customer(customer_id) ON_SUCCESS: THEN collect_transfer_details ON_FAIL: HANDOFF Authentication_Agent collect_transfer_details: REASONING: true GOAL: "Collect from_account, to_account, amount, currency, optional reference." EXIT_WHEN: from_account IS SET AND to_account IS SET AND amount IS SET MAX_TURNS: 4 THEN: screen ``` ## Key properties ABL is designed so that agents are software artifacts, not configuration. Four properties follow from that design: * **AI-writable** — The strict schema allows Arch AI to generate, validate, and modify ABL reliably. Authoring through Arch AI is the default experience. * **Versioned** — Every change to an agent definition produces a reviewable diff in version control, like a change to application code. * **Diffable** — Agent behavior changes are visible in pull requests. The prompt is not the artifact; the ABL is. * **Auditable** — The compiled IR is the immutable record of what was deployed. Compiler validation runs on every commit, so the IR in production always matches the reviewed definition. *** **Related articles:** * [ABL Language Overview](/agent-platform/abl-reference/language-overview) # How to choose between a reasoning agent and a FLOW-based agent Source: https://koreai.mintlify.app/agent-platform/abl/how-to/choose-reasoning-agent-or-flow [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this guide to decide whether an agent should be goal-driven and flexible, step-driven and predictable, or a mix of both. ## Concept An Agent Business Language (ABL) agent can run without a `FLOW:` block or with one. A reasoning agent has an `AGENT`, a `GOAL`, optional persona or limitations, optional tools, optional gathered fields, and completion rules. Without a `FLOW:` block, the reasoning layer chooses the next best action within the defined boundaries. A flow-based agent adds a `FLOW:` block and named steps. The flow controls the conversation path. Each step declares whether it is deterministic with `REASONING: false` or allows a bounded reasoning area with `REASONING: true`. Use a reasoning agent when the work can be expressed as a business goal and the conversation can adapt. Use a flow when the order of operations, checkpoints, or troubleshooting path must be explicit. Use a workflow tool, not a flow, for long waits, polling, scheduled work, or multi-hour orchestration. ## Decision guide | Scenario | Use | Why | | - | - | - | | Flexible Q\&A, policy explanation, broad triage | Reasoning agent | The agent can choose how to answer from the goal, tools, limitations, and context. | | Required intake or compliance sequence | `FLOW` with `REASONING: false` steps | Each field, prompt, response, and next step is explicit. | | Scripted process with one conversational decision | `FLOW` with a bounded `REASONING: true` step | The flow owns the process while the reasoning step handles interpretation. | | Durable approval, polling, callback, scheduled follow-up | Workflow tool | Long-running state belongs outside the stateless agent flow. | ## Minimal working example: reasoning agent This example is a single agent file. It leaves the conversation flexible but gives the agent a required topic and a safe completion condition. ```yaml expandable=true theme={null} AGENT: Policy_QA_Agent GOAL: "Explain employee policies and identify when HR must handle the request" PERSONA: | Clear, concise HR policy assistant. Explains policy rules and next steps without approving exceptions. LIMITATIONS: - "Cannot approve policy exceptions" - "Cannot change employee records" GATHER: policy_topic: prompt: "Which policy do you need help with?" type: string required: true COMPLETE: - WHEN: policy_topic IS SET RESPOND: "I can explain the {{policy_topic}} policy and tell you when HR must review the request." ``` Use this pattern when there is no required path beyond collecting the information needed to answer safely. Avoid using `WHEN: true` as a normal completion rule because it can make the example appear complete even when the customer outcome is not reached. ## Working example: deterministic FLOW for required intake This example is a single agent file. It collects warranty information in a fixed order, then completes only after the required fields are present. ```yaml expandable=true theme={null} AGENT: Warranty_Intake_Agent GOAL: "Collect warranty intake details in a fixed order" FLOW: entry_point: collect_serial steps: - collect_serial - collect_issue - complete_request collect_serial: REASONING: false GATHER: - serial_number: prompt: "What is the device serial number?" type: string required: true THEN: collect_issue collect_issue: REASONING: false GATHER: - issue_description: prompt: "What problem are you seeing?" type: string required: true THEN: complete_request complete_request: REASONING: false RESPOND: "Thanks. I have the serial number and issue description." THEN: complete COMPLETE: - WHEN: serial_number IS SET AND issue_description IS SET RESPOND: "Warranty intake is ready for {{serial_number}}: {{issue_description}}." ``` Use this pattern when the customer must not skip required fields or when operations teams need to inspect the exact conversation path. ## Working example: bounded reasoning inside a FLOW This example is a single agent file. The reasoning step is limited to understanding the support path; the rest of the process remains explicit. ```yaml expandable=true theme={null} AGENT: Support_Intake_Agent GOAL: "Understand a support request, collect account context, and prepare the next action" FLOW: entry_point: understand_request steps: - understand_request - collect_account - finish_intake understand_request: REASONING: true GOAL: "Clarify whether the customer needs billing, technical support, or human escalation" EXIT_WHEN: "the support path and issue summary are clear enough to continue" MAX_TURNS: 3 THEN: collect_account collect_account: REASONING: false GATHER: - account_id: prompt: "What account should I use for this request?" type: string required: true THEN: finish_intake finish_intake: REASONING: false RESPOND: "I have the account context and the support path. I can continue with the next step." THEN: complete COMPLETE: - WHEN: account_id IS SET RESPOND: "Support intake is ready for account {{account_id}}." ``` Use this pattern when only one part of the process needs interpretation. Keep the reasoning zone narrow, give it an exit condition, and add a max-turn limit. ## Verification 1. Validate the agent file. 2. Run a sample utterance for the reasoning-agent example: "Can you explain the parental leave policy?" 3. Run the warranty flow with a serial number and issue description. Confirm the prompts happen in order and completion waits until both fields are present. 4. Run the bounded-reasoning flow with a broad request such as "I need help with a billing issue and my account." Confirm the reasoning step clarifies the path before collecting `account_id`. 5. Inspect trace/debug output for the active step name, gathered fields, and final completion response. ## Production readiness checklist * Use a reasoning agent only when `GOAL`, `LIMITATIONS`, tools, and completion rules are enough to bound behavior. * Use flow steps for required collection, compliance prompts, or processes support teams must inspect. * Keep `REASONING: true` steps bounded with a clear `GOAL`, exit condition, and max-turn limit. * Ensure every required gathered field feeds into completion, memory, a tool call, handoff, delegate, or another known consumer. * Move long waits, polling, scheduled callbacks, and multi-hour orchestration into a workflow tool. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Using `FLOW` for every conversation | Developers want control even when a goal is enough. | Start with a reasoning agent when the outcome is flexible and low-risk. | | Using `WHEN: true` as the main completion rule | It makes examples succeed before the customer outcome is complete. | Complete on meaningful fields, tool results, or final state. | | Adding a reasoning step that never affects the outcome | The flow has a reasoning step but no clear purpose. | Give the step a narrow goal and explain what it prepares for the next step. | | Using agent flow for durable work | Flow is visible and convenient, but not meant for long waits. | Use workflow-as-tool for durable orchestration. | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | A required gathered field triggers warnings | The field is never consumed. | Reference it in completion, memory, a tool input, handoff, delegate, or next-step logic. | | A reasoning step keeps asking questions | The exit condition is too broad or the max-turn limit is missing. | Tighten `GOAL`, `EXIT_WHEN`, and `MAX_TURNS`. | | The flow is hard to maintain | Too many business paths are in one agent. | Split into specialists or move durable work to a workflow. | # How to decide whether to build one agent or multiple specialist agents Source: https://koreai.mintlify.app/agent-platform/abl/how-to/choose-single-agent-or-specialists [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this guide when one customer-facing experience could be implemented either as one broad agent or as a supervisor with multiple specialist agents. ## Concept A single agent owns the conversation and the outcome. It can gather information, call tools, answer, and complete the request without transferring ownership. A specialist design separates the front door from the business owners. A supervisor receives the user request, chooses the right owner, and uses `HANDOFF` when another agent should take over. A specialist owns one capability, such as account access, order status, billing, claims, device care, or employee benefits. The design question is ownership. If one team, policy set, permission model, and success criteria can own the outcome, keep one agent. If different teams, tools, policies, or escalation paths own different parts of the experience, use specialists. ## Decision guide | Question | Prefer one agent when | Prefer specialists when | | - | - | - | | Scope | Requests are tightly related. | The entry point receives unrelated business requests. | | Tools and data | The same tools and permissions apply. | Different topics need different tools or data access. | | Risk | One policy set is enough. | Each topic has different compliance, escalation, or approval rules. | | Ownership | One team updates and tests the whole experience. | Different teams own different business outcomes. | | Routing | No transfer is needed. | The user needs to be routed to the right owner. | Do not split agents just because a domain is large. Splitting adds routing, context-passing, target-agent readiness, testing, monitoring, and fallback design. ## Minimal working example: one agent owns the outcome This example is one agent file. It is enough when order-status work has one owner and one required field. ```yaml expandable=true theme={null} AGENT: Order_Status_Agent GOAL: "Answer order status and delivery questions" LIMITATIONS: - "Cannot issue refunds" - "Cannot change fulfilled orders" GATHER: order_id: prompt: "What is your order ID?" type: string required: true COMPLETE: - WHEN: order_id IS SET RESPOND: "I can help check the status of order {{order_id}}." ``` Use this shape when the same agent can safely handle the full request. If refund, billing, or human escalation becomes part of the same entry point, add explicit boundaries or split ownership. ## Working example: supervisor with specialist agents This example is a project-level set. The supervisor and all referenced specialists must exist in the same project. `agents/support-supervisor.agent.abl` ```yaml expandable=true theme={null} SUPERVISOR: Support_Supervisor GOAL: "Route support requests to the right specialist" AGENTS: account: Account_Support_Agent orders: Order_Status_Agent human: Live_Agent INTENTS: account: "Account access or password help" order_status: "Order tracking or delivery help" human_help: "User asks for a person or the request is unclear" GATHER: customer_id: prompt: "What customer ID should I use for this request?" type: string required: true HANDOFF: - TO: Account_Support_Agent WHEN: intent.category == "account" PASS: [customer_id] SUMMARY: "Customer needs account or password support" RETURN: true - TO: Order_Status_Agent WHEN: intent.category == "order_status" PASS: [customer_id] SUMMARY: "Customer needs order status or delivery support" RETURN: false - TO: Live_Agent WHEN: intent.category == "human_help" PASS: [customer_id] SUMMARY: "Customer asks for a person or the request is unclear" RETURN: false ``` `agents/account-support-agent.agent.abl` ```yaml expandable=true theme={null} AGENT: Account_Support_Agent GOAL: "Resolve account access and password support requests" GATHER: customer_id: prompt: "What customer ID should I use?" type: string required: true COMPLETE: - WHEN: customer_id IS SET RESPOND: "I can continue account support for {{customer_id}}." ``` `agents/order-status-agent.agent.abl` ```yaml expandable=true theme={null} AGENT: Order_Status_Agent GOAL: "Resolve order status and delivery support requests" GATHER: customer_id: prompt: "What customer ID should I use?" type: string required: true COMPLETE: - WHEN: customer_id IS SET RESPOND: "I can continue order support for {{customer_id}}." ``` `agents/live-agent.agent.abl` ```yaml expandable=true theme={null} AGENT: Live_Agent GOAL: "Transfer the customer to a human support queue" COMPLETE: - WHEN: true RESPOND: "I will connect you with a human support specialist." ``` `AGENTS:` is a readable roster. `HANDOFF` is the executable routing surface. `customer_id` is gathered before handoff and passed to the specialists with `PASS`. Since none of the `HANDOFF` entries above declare `HISTORY`, each specialist now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). Also, because each `WHEN` here is a single-field comparison (`intent.category == "account"`), an unset field simply makes the comparison evaluate `false` — no error, and the trace shows your literal source text. ## When to use handoff Use `HANDOFF` when the target agent should own the next part of the conversation. Use `RETURN: true` when the specialist should do bounded work and return control to the supervisor. Use `RETURN: false` when the target agent or human channel should keep ownership. ## When to use delegate Use `DELEGATE` when the parent agent keeps the conversation but asks another agent for a result. This example is a two-agent project set. The child produces `total_fee`, and the parent maps that result into `quoted_fee`. `agents/booking-manager.agent.abl` ```yaml expandable=true theme={null} AGENT: Booking_Manager GOAL: "Manage booking changes while keeping ownership of the customer conversation" GATHER: booking_id: prompt: "What booking should I use?" type: string required: true DELEGATE: - TO: Fee_Calculator WHEN: "the user asks for a booking change fee" PURPOSE: "Calculate the fee for the requested booking change" PASS: { booking_id: booking_id } RETURNS: total_fee: quoted_fee USE_RESULT: "Explain the fee before asking the user to confirm" TIMEOUT: "10s" ON_FAILURE: RESPOND "I could not calculate the fee right now." COMPLETE: - WHEN: booking_id IS SET RESPOND: "I can manage booking changes for {{booking_id}}." ``` `agents/fee-calculator.agent.abl` ```yaml expandable=true theme={null} AGENT: Fee_Calculator GOAL: "Calculate booking change fees for the booking manager" FLOW: entry_point: calculate_fee steps: - calculate_fee calculate_fee: REASONING: false SET: total_fee = "$25" RESPOND: "The estimated change fee is $25." THEN: complete COMPLETE: - WHEN: total_fee IS SET RESPOND: "The estimated change fee is {{total_fee}}." ``` ## Verification 1. Validate all files in the project together, not only the supervisor. 2. Send an account access utterance and confirm the selected tool or trace shows `handoff_to_Account_Support_Agent`. 3. Send an order-status utterance and confirm ownership moves to `Order_Status_Agent`. 4. Send "I need a person" or an unclear request and confirm the fallback route reaches `Live_Agent`. 5. For the delegate example, send "What would it cost to change this booking?" and confirm the parent stays active while `Fee_Calculator` returns `total_fee`. ## Production readiness checklist * Every `HANDOFF TO` and `DELEGATE TO` target exists in the project or is configured as an external/remote agent. * Every `PASS` field is gathered, read from memory, produced by a tool, or supplied by the channel before it is needed. * Each route has a distinct business owner and success criteria. * A fallback or human route exists for unclear, unsupported, or high-risk requests. * Test utterances cover every route, delegate failure, and human escalation. * Trace/debug review confirms selected route, target agent, return behavior, and context passed. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Splitting too early | A broad domain looks complex, but one owner can still handle it. | Split only when ownership, tools, policy, or risk differs. | | Listing agents without routing | `AGENTS:` looks like it connects agents. | Add `HANDOFF` rules for executable transfer. | | Passing fields with no source | The route needs context that was never gathered or produced. | Gather, remember, or tool-populate every passed field. | | Using handoff for a subtask result | The parent only needed a calculation or answer. | Use `DELEGATE` when the parent should keep the conversation. | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | The supervisor stays active | No handoff rule was selected. | Check `HANDOFF`, route conditions, intent categories, and fallback path. | | A target cannot use passed context | The field is missing or named differently. | Check `PASS` field names and target-agent gathered fields. | | A delegate return is empty or warned | The child does not obviously produce the returned field. | Declare, gather, set, or tool-produce the child return field. | # How to clarify before routing to a specialist agent Source: https://koreai.mintlify.app/agent-platform/abl/how-to/clarify-before-routing [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Use this when a request is short, ambiguous, overloaded, or missing the one field that determines ownership. ## Concept Clarification is the right pattern when the supervisor has too little information to select a specialist. Instead of guessing, gather one routing field with a global `GATHER`, then use deterministic `HANDOFF` conditions over the clarified answer. Keep a quoted semantic fallback for answers that don't cleanly match any expected value. One thing to get right when you write that fallback condition: keep it in ordinary lowercase sentence form. The runtime's classifier that decides whether a quoted `WHEN` string is natural language or a structured expression checks for `AND`/`OR`/`NOT` **case-sensitively** (uppercase only) in the step that ultimately matters, but an earlier quote-unwrapping check matches `and`/`or` case-**in**sensitively. In practice this means: * Lowercase connector words in an ordinary sentence (like "the clarified route topic is unsupported **or** still unclear") work correctly and are treated as natural language. * The same sentence with an uppercase `OR`/`AND`/`NOT` can be misclassified as a structured deterministic expression instead — and since ordinary English words aren't valid variable names, that misclassification will fail rather than degrade gracefully. Write your fallback conditions the way this example does — as a plain, lowercase sentence — and you won't hit this. ## Minimal working example ```yaml expandable=true theme={null} SUPERVISOR: Clarify_Before_Routing_Supervisor GOAL: "Collect one routing decision before selecting a specialist" GATHER: route_topic: TYPE: string PROMPT: "What do you need help with: billing, technical support, cancellation, or something else?" REQUIRED: true HANDOFF: - TO: Billing_Agent WHEN: route_topic == "billing" EXPECT_RETURN: false CONTEXT: pass: [customer_id, route_topic] summary: "Customer clarified that this is a billing request." - TO: Technical_Support_Agent WHEN: route_topic == "technical support" EXPECT_RETURN: false CONTEXT: pass: [customer_id, route_topic] summary: "Customer clarified that this is a technical support request." - TO: General_Service_Agent WHEN: "the clarified route topic is unsupported or still unclear" EXPECT_RETURN: false CONTEXT: pass: [customer_id, route_topic] summary: "Customer still needs triage." AGENT: Billing_Agent GOAL: "Resolve billing requests" AGENT: Technical_Support_Agent GOAL: "Resolve technical support requests" AGENT: General_Service_Agent GOAL: "Clarify and route unsupported requests" ``` `customer_id` is passed as context with no declared source in this example — treat it as a project-local assumption (typically populated from authentication) and declare it via `MEMORY` in your actual project. ## How it works * The top-level `GATHER` runs before any `HANDOFF` is evaluated — the supervisor asks its one clarification question and waits for `route_topic` before routing. * The two deterministic routes compare `route_topic` against expected values directly. If `route_topic` somehow weren't set yet, the comparison would simply evaluate `false` — no error, and the trace shows your literal authored condition text. * The fallback route's quoted condition is evaluated by the runtime's semantic-routing classifier, not a deterministic expression evaluator — which is exactly why the lowercase-wording rule above matters. * Since `HISTORY` is omitted from every `HANDOFF`, each specialist now receives the full conversation history by default (the current platform default). ## Common variations * Global gather before any specialist route (shown above). * A fallback agent gathers clarification itself and returns to the supervisor (see the fallback-routing HowTo) instead of resolving directly in the supervisor's own `GATHER`. * Multi-intent disambiguation when several valid routes are detected in one message, instead of a single missing-field clarification. ## Verification * Parse and compile the ABL and confirm there are no parser or compiler errors/warnings. * Test at least one matching utterance for each specialist route, plus an utterance that should hit the fallback (something outside billing/technical support). * Deliberately test with an answer containing the word "or"/"and" in lowercase to confirm the fallback still classifies correctly; avoid ever writing an uppercase `AND`/`OR`/`NOT` into a quoted fallback condition. * Inspect the trace for the selected target and the evaluated condition (matches your literal authored text for the deterministic routes). ## Production readiness checklist * The gathered field asks for exactly the one value needed to route — nothing more. * Deterministic conditions use declared, gathered, tool-result, runtime, or returned fields. * Semantic fallback conditions are quoted, written as ordinary lowercase sentences, and never contain a capitalized `AND`/`OR`/`NOT`. * Fallback behavior is explicit and does not hide missing intent coverage. * Any variable assumed to already exist (like `customer_id` above) has a real declared source in your project. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Asking for a long form when one routing question is enough | Over-collecting "just in case" | Gather only the field that actually determines ownership | | Clarifying again after handoff when the supervisor already owns routing | Duplicated logic between supervisor and child | Keep clarification at the level that owns the routing decision | | Forgetting a fallback for answers outside the expected values | Assuming users will match one of the listed options exactly | Always include a quoted semantic fallback route | | Writing an uppercase `AND`/`OR`/`NOT` inside a quoted fallback condition | Reads naturally to a human author | Keep quoted fallback text in ordinary lowercase sentence form | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | The fallback route behaves unpredictably or seems to error | Uppercase `AND`/`OR`/`NOT` inside the quoted fallback text got misclassified as a structured expression | Rewrite the fallback condition in ordinary lowercase sentence form | | The supervisor never routes even after the user answers the clarification question | `route_topic` value doesn't exactly match any deterministic condition's literal string | Check the actual gathered value against the exact strings compared in `WHEN` | | Child agent has more/less conversation context than expected | `HISTORY` omitted; current default (`full`) applied | Set an explicit `HISTORY` strategy if bounded/summary history is intended | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route unclear requests to a fallback agent](/agent-platform/abl/how-to/route-to-fallback-agent). * [How to handle messages that contain multiple user intents](/agent-platform/abl/how-to/handle-multiple-intents). # How to combine reasoning steps with deterministic FLOW steps Source: https://koreai.mintlify.app/agent-platform/abl/how-to/combine-reasoning-and-flow-steps [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this pattern when a conversation needs both flexible judgment and predictable execution. A good enterprise flow often has one or two places where the agent should reason, such as understanding urgency or explaining a result, surrounded by deterministic steps that collect data, set variables, call tools, branch, and finish reliably. ## Concept ABL treats reasoning and deterministic behavior as step-level choices inside `FLOW`. `REASONING: true` gives a step a goal-driven reasoning zone. `REASONING: false` makes the step scripted: it should use explicit actions such as `GATHER`, `SET`, `CALL`, `RESPOND`, `ON_INPUT`, `ON_RESULT`, and `THEN`. Use reasoning steps for judgment: interpreting messy user language, choosing a tone, deciding whether a situation is urgent, or explaining a result. Use deterministic steps for control: collecting required fields, setting session values, invoking tools, branching on known outputs, and ending the flow. Do not use the old `MODE:` pattern. Current ABL expects `REASONING: true` or `REASONING: false` on each `FLOW` step. A deterministic step with a `GOAL` or `AVAILABLE_TOOLS` is misleading because those only affect reasoning behavior. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Claim_Intake_Agent GOAL: "Collect claim details, reason about urgency, and route to the right next step" PERSONA: "Careful claims intake specialist" FLOW: entry_point: collect_claim collect_claim: REASONING: false GATHER: - policy_id: prompt: "What is your policy ID?" type: string required: true - claim_type: prompt: "What type of claim are you filing?" type: string required: true THEN: assess_urgency assess_urgency: REASONING: true GOAL: "Decide whether the claim sounds urgent, routine, or unclear. Ask one brief clarification question if needed." THEN: prepare_summary prepare_summary: REASONING: false SET: claim_summary = "Policy " + policy_id + " reported a " + claim_type + " claim" THEN: explain_next_steps explain_next_steps: REASONING: true GOAL: "Explain the next steps in plain language using policy_id, claim_type, and claim_summary." THEN: COMPLETE ``` ## How it works The first step is deterministic because the platform should collect `policy_id` and `claim_type` before doing anything else. The second step is reasoning because urgency is not always a clean enum: the user may describe an accident, water damage, loss, injury, or uncertainty in their own words. The third step is deterministic again because it constructs a known variable. The final reasoning step turns the known state into a user-facing explanation. This structure makes the flow easier to test. You can verify that required fields are collected before `assess_urgency`, that `claim_summary` is set before `explain_next_steps`, and that completion happens only after the explanation step. ## Common variations ### Reason first, then collect Start with a reasoning step when users may open with broad language like "I need help with something that happened yesterday." The reasoning step can decide what to ask next, then transition into deterministic collection. ### Collect first, then reason Start with deterministic collection when compliance, eligibility, identity, or required intake fields must exist before any judgment is useful. ### Reason after a tool result Use a deterministic `CALL` and `ON_RESULT` to fetch known data, then use a reasoning step to explain the result. This keeps tool execution predictable while allowing the final response to adapt to the user. ## Verification * Validate the project and confirm every `FLOW` step declares `REASONING: true` or `REASONING: false`. * Test a routine claim, an urgent claim, and an unclear claim. * Confirm `collect_claim` gathers both fields before `assess_urgency`. * Confirm `prepare_summary` sets `claim_summary` before `explain_next_steps`. * Inspect the trace for step enter and exit events so you can prove the flow moved through deterministic and reasoning steps in the expected order. ## Production readiness checklist * Keep deterministic steps free of unused goals and available-tool lists. * Keep reasoning goals narrow enough that the model knows what decision or explanation it owns. * Add fallback paths for unclear user input, failed tool calls, and exhausted attempts. * Use deterministic steps for required data, compliance checks, and side-effecting actions. * Use traces to monitor unexpected loops, skipped steps, and repeated clarification turns. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Using `MODE:` for the whole agent | Older examples may show agent-level execution mode | Declare `REASONING` on each flow step | | Putting a `GOAL` on a deterministic step | The step looks like it needs explanation | Move the goal to a reasoning step or remove it | | Making every step reasoning-enabled | Flexible behavior feels attractive during design | Use deterministic steps for control, data movement, tools, and branches | | Making every step deterministic | The flow becomes brittle for messy language | Add reasoning zones where interpretation or explanation is required | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | The validator says a step must declare reasoning | A `FLOW` step is missing `REASONING` | Add `REASONING: true` or `REASONING: false` to every step | | A deterministic step does not use its goal | `REASONING: false` ignores step goals | Move the goal to a reasoning step | | The flow skips useful judgment | A transition jumps around the reasoning step | Check `THEN`, `ON_INPUT`, and `ON_RESULT` branches | | The agent loops in explanation | The reasoning goal is too broad or has no exit | Narrow the goal and transition to `COMPLETE` | *** **Related articles:** * [How to choose between a reasoning agent and a FLOW-based agent](/agent-platform/abl/how-to/choose-reasoning-agent-or-flow). # How to configure EXECUTION.pipeline models, modes, thresholds, and fallbacks Source: https://koreai.mintlify.app/agent-platform/abl/how-to/configure-agent-execution-pipeline [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this guide when the agent execution pipeline exists but needs tuning for model choice, mode, confidence thresholds, tool filtering, keyword veto, intent bridging, and fallback behavior. ## Concept `EXECUTION.pipeline` configuration is a set of routing controls. It does not replace the supervisor's handoff rules. Instead, it helps the runtime decide how aggressively to use classification before the normal reasoning path. The effective pipeline configuration is resolved from agent-level settings, project-level settings, and defaults. Agent settings can override project settings for the same field. Project settings can fill fields the agent does not specify. Defaults fill the rest. The pipeline is disabled by default, and an agent-level opt-out remains a veto even when the project is enabled. ## Minimal working example Use this when you only need to enable the execution pipeline and keep the first deployment conservative. ```yaml theme={null} AGENT: Pipeline_Minimal_Agent GOAL: "Answer support questions with execution pipeline assistance" EXECUTION: pipeline: enabled: true mode: sequential shortCircuit: enabled: true confidenceThreshold: 0.9 COMPLETE: - WHEN: true RESPOND: "I can help with your support request." ``` This example enables the pipeline, keeps mode predictable with `sequential`, and allows short-circuiting only at high confidence. ## Full configuration example Treat this as a separate project-level routing example from the minimal agent above. ```yaml expandable=true theme={null} SUPERVISOR: Insurance_Router GOAL: "Route insurance requests with tuned classifier thresholds and fallbacks" PERSONA: "Careful insurance routing supervisor" EXECUTION: pipeline: enabled: true mode: parallel model: "gpt-4.1-mini" shortCircuit: enabled: true confidenceThreshold: 0.92 toolFilter: enabled: true maxTools: 3 keywordVeto: enabled: true keywords: ["appeal", "fraud", "legal"] intentBridge: enabled: true programmaticThreshold: 0.88 guidedThreshold: 0.6 outOfScopeDecline: false multiIntentSignal: true HANDOFF: - TO: Claims_Agent WHEN: "The user asks to file, check, or update a claim" SUMMARY: "User needs claims support" - TO: Benefits_Agent WHEN: "The user asks about coverage, eligibility, benefits, or plan rules" SUMMARY: "User needs benefits support" - TO: Escalation_Agent WHEN: "The user mentions appeals, fraud, legal risk, or a complaint" SUMMARY: "User may need escalation review" ``` ```yaml theme={null} AGENT: Claims_Agent GOAL: "Help members with claim filing and claim status" PERSONA: "Claims support specialist" ``` ```yaml theme={null} AGENT: Benefits_Agent GOAL: "Explain plan benefits, coverage, and eligibility" PERSONA: "Benefits specialist" ``` ```yaml theme={null} AGENT: Escalation_Agent GOAL: "Handle escalations, complaints, appeals, and risk-sensitive requests" PERSONA: "Escalation specialist" ``` ## How it works `mode` controls how the pipeline work is organized. Use `sequential` when you want simpler, more predictable behavior. Use `parallel` when your deployment is tuned for faster classification and filtering paths. `model` chooses the classifier model. Prefer a model that is strong enough for category decisions but inexpensive enough for every turn where the pipeline runs. `shortCircuit.confidenceThreshold` controls when a single classified intent can bypass broader reasoning. Higher thresholds reduce misroutes but may send more traffic to guided reasoning. `toolFilter.maxTools` limits how many tools remain visible after filtering. Use it when the agent has many tools or routing tools and the classifier can narrow the relevant set. `keywordVeto.keywords` prevents short-circuiting when sensitive words appear. These words should reflect business risk, not just common nouns. `intentBridge.programmaticThreshold` and `intentBridge.guidedThreshold` split routing into deterministic, guided, and autonomous behavior. Defaults exist, but production values should be tuned from real traffic. Since none of the `HANDOFF` entries in the full configuration example declare `HISTORY`, each specialist now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). ## Common variations | Variation | Use when | Configuration focus | | - | - | - | | Conservative launch | Misroutes are expensive or the category set is new. | `mode: sequential`, high `confidenceThreshold`, keyword veto enabled. | | Low-latency routing | You have measured latency budget and strong monitoring. | `mode: parallel`, small tool set, tuned thresholds. | | Classifier-assisted but not autonomous | The classifier should guide reasoning but not route alone. | Keep `guidedThreshold` lower than `programmaticThreshold`. | | Sensitive-topic routing | Legal, fraud, cancellation, refund, or complaint paths need extra review. | Add `keywordVeto.keywords` and escalation routes. | | Agent opt-out | Project enables the pipeline but one agent is not ready. | Set agent-level `pipeline.enabled: false` and verify effective config. | ## Configuration checklist | Setting | Start with | Tighten when | | - | - | - | | `mode` | `sequential` | You have latency evidence and monitoring for `parallel` | | `shortCircuit.confidenceThreshold` | High, such as `0.9` or above | Misroutes are more expensive than slower routing | | `programmaticThreshold` | Higher than guided threshold | Direct routing or decline must be conservative | | `guidedThreshold` | Middle confidence range | You want classifier help without full automation | | `keywordVeto.keywords` | Sensitive terms and side-effect terms | Compliance, refund, legal, fraud, or cancellation risk grows | | `toolFilter.maxTools` | Small enough to reduce noise | Tool lists become large or overlapping | ## Verification * Validate the ABL and confirm the pipeline block compiles into execution settings. * Compare effective config when only project settings exist, only agent settings exist, and both exist. * Test confidence just below and at `guidedThreshold`. * Test confidence just below and at `programmaticThreshold`. * Test `keywordVeto` with each sensitive keyword. * Test agent opt-out when the project pipeline is enabled. * Inspect traces for selected mode, classifier model, thresholds, veto, and tiered action. ## Production readiness checklist * Project-level and agent-level pipeline settings are documented together. * Thresholds are chosen from real utterance samples and reviewed after launch. * Escalation or fallback exists for sensitive and ambiguous categories. * Keyword veto terms are reviewed for false positives and false negatives. * Tool filtering does not hide tools needed for safe recovery. * Dashboards track tier distribution, short-circuit rate, guided rate, autonomous rate, and fallback rate. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Lowering programmatic threshold too far | It increases automation | Keep direct routing conservative until measured | | Using keyword veto as a routing rule | Veto only blocks short-circuiting | Keep routing logic in handoff rules and categories | | Assuming agent opt-in is enough | Runtime has project-level gate behavior | Verify effective config in the deployed project | | Filtering tools too aggressively | Smaller tool lists look cleaner | Test failure recovery and edge cases | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Agent config is ignored | Project pipeline is disabled or absent | Compare agent, project, and effective config | | Guided behavior happens too often | Programmatic threshold is too high or categories are weak | Review classifier confidence and category distinctness | | Direct routing happens too often | Thresholds are too low or veto terms are missing | Raise thresholds and add sensitive veto words | | Tool choice gets worse | `toolFilter.maxTools` is too small | Increase max tools or improve tool descriptions | *** **Related articles:** * [How to use EXECUTION.pipeline for agent routing, classification, and short-circuiting](/agent-platform/abl/how-to/use-agent-execution-pipeline). # How to configure model, timeout, and execution settings for an agent Source: https://koreai.mintlify.app/agent-platform/abl/how-to/configure-agent-execution-settings [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use `EXECUTION` when an agent needs explicit model choice, response limits, timeout boundaries, concurrency behavior, thinking settings, history windowing, voice settings, or execution-pipeline configuration. ## Concept `EXECUTION` is the agent-level configuration surface for runtime behavior. It does not decide whether a flow step is reasoning or deterministic; that is handled by step-level `REASONING`. Instead, `EXECUTION` controls the environment in which the agent runs: which model to use, how long LLM and tool calls can wait, how many iterations are allowed, how much history is retained, and how concurrent messages are handled. Use explicit settings when the default behavior is not enough for cost, latency, safety, or operational predictability. Keep settings minimal for simple agents. Add settings only when you can explain why the value exists and how it will be tested. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Enterprise_Support_Agent GOAL: "Answer customer support questions with bounded model and execution behavior" PERSONA: "Concise support assistant" EXECUTION: model: gpt-4.1-mini temperature: 0.2 max_tokens: 1200 tool_timeout: 8000 llm_timeout: 15000 session_idle_timeout: 1800000 max_reasoning_iterations: 4 max_flow_iterations: 12 concurrency: preemptive maxQueueDepth: 20 maxConcurrentMessages: 3 enable_thinking: true thinking_budget: 1024 conversation_history_window: 12 fallback_model: gpt-4.1-mini ``` ## How it works The parser accepts both snake\_case and some camelCase forms for several execution settings. The compiler lowers the values into the agent execution IR. Runtime components then use that execution contract to bound model calls, tool calls, history, concurrency, and related behavior. The most common settings are: | Setting | Use it for | | - | - | | `model` | Primary model for the agent | | `temperature` | Response variability | | `max_tokens` | Output budget | | `tool_timeout` | Maximum wait for tool calls | | `llm_timeout` | Maximum wait for LLM calls | | `session_idle_timeout` | Idle session boundary | | `max_reasoning_iterations` | Preventing reasoning loops | | `max_flow_iterations` | Preventing flow loops | | `concurrency` | Serial, preemptive, or parallel handling strategy | | `conversation_history_window` | Number of recent turns to retain | | `fallback_model` | Backup model when fallback is configured | ## Common variations ### Low-latency support agent Use a smaller model, low token budget, short LLM timeout, and small history window when the agent handles high-volume support questions. ### Careful enterprise agent Use lower temperature, bounded reasoning iterations, and explicit timeout values when the agent answers regulated, contractual, or account-related questions. ### Voice or realtime agent Use voice-specific execution settings and stricter latency budgets. Test the experience in the voice channel because a setting that works in chat may feel slow on a call. ### Agent execution pipeline Use nested `EXECUTION.pipeline` only when you need agent routing, classification, short-circuiting, tool filtering, keyword veto, or intent bridging. That is a separate decision from choosing a base model or timeout. ## Verification * Validate the agent and confirm `EXECUTION` compiles into the agent execution settings. * Test a normal model response and a slow model response against `llm_timeout`. * Test a normal tool call and a slow tool call against `tool_timeout`. * Inspect traces for model, timeout, and execution-path metadata. * Load test concurrency settings before using `parallel` or high queue depths in production. ## Production readiness checklist * Every non-default setting has an owner and reason. * Timeout values match the channel experience and downstream service SLOs. * Token limits are high enough for expected answers but low enough for cost control. * Iteration limits prevent loops without cutting off valid flows. * Fallback behavior is tested, not just configured. * Voice settings are tested with real turn-taking and latency expectations. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Using top-level `MODEL:` | It looks natural as a section | Put `model` under `EXECUTION` | | Using `MODE:` for reasoning/scripted behavior | Older patterns used agent-level mode | Use step-level `REASONING` in `FLOW` | | Setting timeouts too high | Avoids visible failures in testing | Match timeouts to user experience and service SLOs | | Copying settings across agents | It is faster during setup | Tune by agent role, channel, and risk | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Parser rejects `MODEL:` | Model is not a top-level section | Move it to `EXECUTION.model` | | Agent is slow under load | Timeout or concurrency settings are too loose | Check traces, queue depth, and downstream service latency | | Agent stops mid-flow | Iteration limit is too low | Inspect step count and loop behavior | | Costs are higher than expected | Model, token, or history settings are too large | Review `model`, `max_tokens`, and history window | *** **Related articles:** * [How to use EXECUTION.pipeline for agent routing, classification, and short-circuiting](/agent-platform/abl/how-to/use-agent-execution-pipeline). * [How to combine reasoning steps with deterministic FLOW steps](/agent-platform/abl/how-to/combine-reasoning-and-flow-steps). # How to create a welcome message for a new conversation Source: https://koreai.mintlify.app/agent-platform/abl/how-to/create-welcome-message [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when a conversation should open with a clear first message before the user asks for help. ## Concept A welcome message is the first assistant-owned turn in a session. In ABL, the most direct way to author it is `ON_START`, because `ON_START` runs when the runtime initializes the session, before the first user message is processed. Use a welcome message to orient the user, name the service, and make the first action obvious. Keep it short. If the same agent also has a `FLOW`, the top-level `ON_START` response behaves like a startup prelude: it can be emitted and the entry flow can continue. If an `ON_START` branch is selected, that branch owns the startup turn and the runtime can pause before the flow continues. ## Minimal working example ```yaml theme={null} AGENT: Welcome_Message_Agent GOAL: "Welcome the user and then continue to the first support step" ON_START: RESPOND: "Welcome to Acme Support. I can help with orders, returns, billing, or account questions." FLOW: entry_point: listen steps: - listen listen: REASONING: false RESPOND: "What would you like help with today?" THEN: COMPLETE ``` ## How it works The runtime initializes the session, executes `ON_START`, emits the authored response when the channel supports proactive startup delivery, and then enters the flow entry step. The welcome response is also written into assistant history when it is delivered, so later reasoning can see what the user has already been told. The first flow response should not repeat the welcome. Use it to ask the first useful question or show the first menu. ## Common variations ### Add buttons or rich content Use actions when the channel can render buttons and you want the user to choose a common path immediately. ```yaml expandable=true theme={null} AGENT: Rich_Welcome_Agent GOAL: "Welcome users with a card and a clear first action" ON_START: RESPOND: "Welcome to Acme Support." FORMATS: markdown: | ## Welcome to Acme Support Choose a topic to begin. ACTIONS: - BUTTON: "Track an order" -> track_order VALUE: "track_order" - BUTTON: "Start a return" -> start_return VALUE: "start_return" FLOW: entry_point: wait_for_choice steps: - wait_for_choice wait_for_choice: REASONING: false RESPOND: "Select an option or describe your question." THEN: COMPLETE ``` ## Verification Start a new session and confirm that the welcome appears before the first user message. In traces, look for `dsl_on_start` followed by `dsl_respond` with `source: on_start`. If the flow also runs, the flow response should appear after the welcome. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Repeating the same greeting in `ON_START` and the first flow step | Both are visible during startup on proactive channels. | Make `ON_START` orient the user and make the first flow step ask the next useful question. | | Putting long policy text in the welcome | The welcome becomes a wall of text before the user asks anything. | Keep startup copy short and move details into the next guided step. | | Assuming every channel shows the proactive welcome | Some channel contracts suppress startup payloads. | Test each channel and keep critical initialization separate from visible copy. | ## Troubleshooting If the welcome does not appear, check the channel contract. Some channels suppress proactive startup payloads and allow `ON_START` only for side effects. If the flow response repeats the welcome, move the greeting into `ON_START` and make the flow step ask the next question. ## Production readiness checklist * Keep the welcome short enough for every channel. * Include a useful next step, not only a greeting. * Use rich content only when the target channel supports it. * Verify traces for `dsl_on_start` and `dsl_respond`. * Confirm the first flow step does not duplicate the startup message. # How to debug routing decisions in traces Source: https://koreai.mintlify.app/agent-platform/abl/how-to/debug-routing-traces [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Use this when support, QA, or partners need to understand why a user went to one specialist instead of another, or why a route silently fell through to fallback. ## Concept Every `HANDOFF` decision, whether deterministic (`WHEN: intent.category == "billing"`) or semantic (`WHEN: "the request is about billing"`), produces a trace event describing what was evaluated and why a target was or wasn't selected. The runtime does not just log "routed to Billing\_Agent" — it records the condition it evaluated, the context available at evaluation time, the outcome, and (for temporary child agents) what came back through `ON_RETURN`. One thing surprises people the first time they read a routing trace: **not every routing event is visible at every trace verbosity level.** The runtime supports `minimal`, `standard`, `verbose`, and `debug` verbosity. If you're not seeing an event you expect (like `handoff_condition_check` for a route that *didn't* match), raise verbosity before assuming the event doesn't exist. ## Minimal working example ```yaml expandable=true theme={null} SUPERVISOR: Traceable_Routing_Supervisor GOAL: "Make routing decisions easy to inspect in traces" ON_START: SET: _meta.entry_channel = session.channel _meta.routing_version = "support-router-v3" INTENTS: billing: "Billing and payment requests." technical_support: "Product troubleshooting requests." HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" EXPECT_RETURN: false CONTEXT: pass: [customer_id, account_id, conversation_summary] summary: "Traceable billing route." - TO: Technical_Support_Agent WHEN: intent.category == "technical_support" EXPECT_RETURN: false CONTEXT: pass: [customer_id, issue_summary, conversation_summary] summary: "Traceable technical support route." - TO: Fallback_Triage_Agent WHEN: "the request does not clearly match billing or technical support" EXPECT_RETURN: true CONTEXT: pass: [customer_id, conversation_summary] summary: "Traceable fallback route." ON_RETURN: action: resume_intent AGENT: Billing_Agent GOAL: "Resolve billing requests" AGENT: Technical_Support_Agent GOAL: "Resolve technical support requests" AGENT: Fallback_Triage_Agent GOAL: "Clarify unclear requests" ``` `customer_id`, `account_id`, `issue_summary`, and `conversation_summary` are shown as passed context fields; declare them via `MEMORY` or populate them via `GATHER`/tool results in your actual project — this example assumes they already exist as project-local session values. ## How it works * The supervisor keeps ownership until a `HANDOFF` condition matches, evaluated in authored order. * Each evaluated condition emits `handoff_condition_check` with a shape similar to: ```json theme={null} { "event": "handoff_condition_check", "condition": "intent.category == \"billing\"", "result": false, "evaluatedContext": { "intent.category": "technical_support" }, "target": "Billing_Agent", "whenOutcome": "deterministic_no_match" } ``` For a matched deterministic route, the runtime also emits `deterministic_routing` and `deterministic_handoff`; for a matched semantic (quoted natural-language) `WHEN`, `whenOutcome` reflects the semantic evaluation path instead. * If a condition references a variable that is not yet set anywhere in scope, the runtime emits `route_condition_unresolved` instead of silently treating it as a non-match — check for this event before concluding "the condition just didn't match." * If a flow-level return suppresses a condition that would otherwise have matched (for example, the conversation is mid-flow and routing is intentionally deferred), the runtime emits `handoff_condition_suppressed`. * Once a target is selected, `handoff_context_pass_resolved` records what the resolved `CONTEXT.pass` values actually were, and `handoff_context_set_applied` records any `CONTEXT.set` writes applied to the child. * When `EXPECT_RETURN: true` is used (as in the fallback route above), the child's return path emits `handoff_return_handler`, and `ON_RETURN.action: resume_intent` produces a `resume_intent` trace entry showing what intent processing resumed with. * Remember: since `HISTORY` is omitted from every `HANDOFF` above, each child receives the full conversation history by default (the platform default when `HISTORY` is unset). Set `HISTORY: auto` or another explicit strategy if you want bounded/summary history instead, and expect the trace's history-related fields to reflect whichever strategy is in effect. * `_meta.*` values (like `_meta.entry_channel` above) are not routing logic — they're custom trace dimensions for filtering/grouping in dashboards. You can `SET _meta.*` in `ON_START`, in flow steps, in lifecycle hooks, and in `ON_ERROR`/`ESCALATE` triggers, not only at conversation start. ## Common variations ### Debugging a semantic (natural-language) condition Quoted `WHEN` text like `WHEN: "the request does not clearly match billing or technical support"` produces the same `handoff_condition_check` event, but `whenOutcome` distinguishes the semantic evaluation path from a deterministic one. There's no separate event name to search for — filter on `whenOutcome` instead. ### Debugging multiple intents in one message Multi-intent routing emits its own event family instead of a single glob: `multi_intent_plan_built` (the queue was constructed), `multi_intent_target_resolved` (a queue entry resolved to a target agent), `multi_intent_queued` (an entry queued for later processing), `multi_intent_sequential` plus `multi_intent_sequential_task_start`/`_task_complete`/`_executed` (sequential processing), and `multi_intent_parallel` (parallel processing). Search for the specific event you expect rather than a wildcard. ### Debugging trace visibility If an event you expect isn't showing up, check the trace verbosity setting first (`minimal`/`standard`/`verbose`/`debug`) before assuming the routing logic is wrong — some events only appear at `verbose` or `debug`. ## Verification * Parse and compile the ABL and confirm there are no parser or compiler errors/warnings. * Test at least one matching utterance for each deterministic route, one for the semantic fallback, and one that should trigger `route_condition_unresolved` (reference a variable you know is unset). * Inspect the trace for: the selected `target`, the `condition` actually evaluated (matches your literal authored text), `evaluatedContext`, and — for `EXPECT_RETURN: true` routes — the `handoff_return_handler` and `resume_intent` entries. * Raise trace verbosity to `verbose` or `debug` if an expected event isn't appearing. ## Production readiness checklist * Every specialist route has a clear owner and a concise context summary. * Deterministic conditions use declared, gathered, tool-result, runtime, or returned fields. * Semantic conditions are quoted as natural-language `WHEN` text. * Temporary child agents (`EXPECT_RETURN: true`) produce every field mapped in `ON_RETURN`, verified via `handoff_return_handler`/`resume_intent` traces. * Fallback behavior is explicit and does not hide missing intent coverage — confirm via `route_condition_unresolved` and `handoff_condition_suppressed` that nothing is silently falling through. * `_meta.*` dimensions are stable, low-cardinality values (not raw user text), set consistently across the surfaces where routing decisions can occur (not just `ON_START`). * Trace verbosity is set appropriately for the environment (higher in staging/debugging, tuned for volume/cost in production). ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Only checking `handoff_condition_check` for the matched route | Skipped candidates are just as diagnostic when a route unexpectedly loses to another | Inspect all `handoff_condition_check` entries in the turn, not just the one that matched | | Setting `_meta.*` only in `ON_START` | The article/example historically only showed `ON_START` | Also set `_meta.*` in flow steps, hooks, or `ON_ERROR`/`ESCALATE` when the dimension should reflect later-turn state | | Setting high-cardinality `_meta.*` values (for example, raw user utterance) | Seems convenient for debugging one session | Use stable, low-cardinality values; high-cardinality dimensions break dashboard aggregation | | Not finding an expected trace event | Trace verbosity is below the level that surfaces it | Raise verbosity to `verbose`/`debug` before concluding the event doesn't fire | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | A route you expect to match never fires, and there's no `handoff_condition_check` for it at all | Trace verbosity is too low, or the flow suppressed the condition before it was ever evaluated | Raise verbosity; check for a `handoff_condition_suppressed` entry | | A condition evaluates to a non-match even though the variable looks set in your test | The variable is a different path than the condition references, or it's unset at evaluation time | Look for `route_condition_unresolved`; confirm the variable's actual path via `evaluatedContext` in `handoff_condition_check` | | A temporary child agent's fields never make it back to the parent | Missing or mismatched `ON_RETURN` field mapping | Inspect `handoff_return_handler` for the actual returned payload shape | | Child agent has less/more conversation context than expected | `HISTORY` was omitted and the platform default (`full`) applied, or an explicit strategy elsewhere in the project differs from what you assumed | Check `HISTORY`/`CONTEXT.history` on the `HANDOFF`; confirm against the current platform default | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route conversations based on user intent](/agent-platform/abl/how-to/route-by-user-intent). * [How to handle messages that contain multiple user intents](/agent-platform/abl/how-to/handle-multiple-intents). # How to define clear responsibilities for each enterprise agent Source: https://koreai.mintlify.app/agent-platform/abl/how-to/define-agent-responsibilities [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this guide when an agent is becoming too broad, too vague, or difficult for customers, builders, and support teams to reason about. ## Concept An agent responsibility is a contract. It says what the agent owns, what it can do, what it must not do, what information it needs, and when it should stop, hand off, or escalate. Clear responsibilities make routing safer. A supervisor can only route well when every specialist has a distinct job. | Surface | What it defines | | - | - | | `GOAL` | The business outcome the agent owns. | | `PERSONA` | How the agent communicates. | | `LIMITATIONS` | What the agent should not claim or attempt. | | `GATHER` | Information the agent must collect. | | `CONSTRAINTS` | Conditions that must be true before proceeding. | | `TOOLS` | External capabilities the agent can use. | | `HANDOFF` | When another agent should take ownership. | | `DELEGATE` | When another agent should provide a result while this agent stays active. | | `COMPLETE` | When the agent can finish the interaction. | Limitations guide the model. For hard enforcement, pair them with tools, constraints, guardrails, permissions, and handoff boundaries. ## Decision guide | Use this design choice | When it fits | | - | - | | Keep one agent | One business owner, one data boundary, one tool set, and one success definition cover the work. | | Split into specialist agents | Different owners, permissions, policies, tools, or completion criteria are involved. | | Add handoff boundaries | Another agent should own the rest of the customer journey. | | Add delegate boundaries | The current agent needs a subtask result but should remain the active conversation owner. | | Add constraints, guardrails, tools | A limitation must be enforced rather than only described. | | Add human escalation | Verification, safety, policy, or customer frustration requires a person. | ## Responsibility worksheet Before writing the agent, answer these questions: | Question | Example answer | | - | - | | What business outcome does this agent own? | Recover account access after identity verification. | | What must it never do? | Bypass verification or answer billing questions. | | What information must it collect? | `account_id`, `verification_method`. | | Which tools or data can it use? | Identity verification tools, account recovery tools. | | When should it hand off? | Billing request, human request, verification cannot continue. | | How does it complete? | Required identity context is present and the next recovery step is clear. | ## Minimal working example: account-security specialist project This example is a project-level set. The responsibility boundary is visible in the account-security agent, and the handoff targets are defined in the same project. ```yaml expandable=true theme={null} AGENT: Account_Security_Agent GOAL: "Verify identity and help users recover account access" PERSONA: | Security-conscious account support specialist. Explains verification steps clearly. LIMITATIONS: - "Cannot bypass identity verification" - "Cannot answer billing questions" INTENTS: billing: "Billing, invoices, payments, or subscription charges" human_help: "User asks for a person or verification cannot continue" GATHER: account_id: prompt: "What is your account ID?" type: string required: true verification_method: prompt: "How would you like to verify your identity?" type: string required: true CONSTRAINTS: - REQUIRE account_id IS SET ON_FAIL: "I need your account ID before I can continue." HANDOFF: - TO: Billing_Support_Agent WHEN: intent.category == "billing" PASS: [account_id] SUMMARY: "Customer needs billing support" RETURN: false - TO: Live_Agent WHEN: intent.category == "human_help" PASS: [account_id] SUMMARY: "Customer needs a human agent or cannot continue verification" RETURN: false COMPLETE: - WHEN: account_id IS SET AND verification_method IS SET RESPOND: "I can continue account recovery for {{account_id}} using {{verification_method}} verification." ``` ```yaml theme={null} AGENT: Billing_Support_Agent GOAL: "Answer billing questions after account-security handoff" GATHER: account_id: prompt: "What account should I review?" type: string required: true COMPLETE: - WHEN: account_id IS SET RESPOND: "I can continue billing support for {{account_id}}." ``` ```yaml theme={null} AGENT: Live_Agent GOAL: "Transfer customers to a human support specialist" COMPLETE: - WHEN: true RESPOND: "I will connect you with a human support specialist." ``` The account-security agent owns identity verification and recovery. Billing and human assistance are outside that responsibility and are represented as handoff boundaries. Since neither `HANDOFF` entry above declares `HISTORY`, each target now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). Also, because each `WHEN` here is a single-field comparison (`intent.category == "billing"`), an unset field simply makes the comparison evaluate `false` — no error, and the trace shows your literal source text. ## How to write strong goals Weak goal: "Help customers with account stuff." Strong goal: "Verify identity and help users recover account access." A strong goal names the business outcome, the user, and the scope. It should be narrow enough that an owner can say whether the agent succeeded. ## How to write useful limitations | Limitation | Why it helps | | - | - | | `Cannot bypass identity verification` | Prevents unsafe account recovery. | | `Cannot approve refunds` | Separates policy explanation from financial action. | | `Cannot provide medical diagnosis` | Draws a regulated-domain boundary. | For regulated or high-risk actions, do not rely on limitations alone. Add hard boundaries through tools, constraints, guardrails, permissions, or human handoff. ## When to split an agent Split an agent when it has more than one business owner, more than one permission model, conflicting limitations, different tools by topic, or different success criteria by request type. Keep one agent when the same owner, policy, tools, data, and success criteria apply across the conversation. Use `DELEGATE` instead of splitting when the active agent only needs a subtask result and should keep the user conversation. ## Verification 1. Validate the account-security agent and its handoff targets together. 2. Send: "I need to recover my account." Confirm `account_id` and `verification_method` are gathered. 3. Send: "I have a billing question." Confirm the route goes to `Billing_Support_Agent` and passes `account_id`. 4. Send: "I want to talk to someone." Confirm the route goes to `Live_Agent`. 5. Inspect trace/debug output for selected handoff target, passed context, and final active agent. ## Production readiness checklist * Each specialist has one primary business owner and one primary outcome. * Required fields are gathered or otherwise populated before tools, handoffs, or completion need them. * Sensitive or regulated actions are enforced with tools, constraints, guardrails, permissions, or human handoff. * Out-of-scope intents have handoff routes. * Success, out-of-scope, human escalation, and failure paths have test utterances. * Trace/debug output shows clear ownership transitions. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Writing vague goals | The builder starts from a department name instead of an outcome. | Name the exact business outcome. | | Treating limitations as hard enforcement | Limitations are easy to write and read. | Add constraints, tools, guardrails, permissions, or handoff for hard boundaries. | | Creating overlapping specialists | Teams define agents by org chart instead of user outcomes. | Use distinct goals and test ambiguous requests. | | Handing off without context | The boundary is defined, but the target lacks state. | Pass only the fields the target needs and verify they are populated. | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | A specialist answers outside its area | Goal or limitations are too broad, or no handoff boundary exists. | Tighten `GOAL`, add specific `LIMITATIONS`, and add `HANDOFF` routes. | | Required fields warn or appear unused | The agent collects fields but never consumes them. | Use the fields in completion, tools, memory, handoff, delegate, or constraints. | | Supervisor routing is inconsistent | Specialist responsibilities overlap. | Revisit each specialist's owner, outcome, and out-of-scope routes. | # How to design channel-specific welcome experiences Source: https://koreai.mintlify.app/agent-platform/abl/how-to/design-channel-specific-welcome [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when web chat, voice, messaging, or async channels need different startup behavior. ## Concept Channels do not all deliver startup content the same way. Some support proactive `ON_START` messages. Some suppress startup payloads and allow `ON_START` only for side effects such as setting values or delegating. A good channel-specific welcome separates the authored conversation welcome from launcher text, placeholder text, and channel-native connection messages. In ABL, use startup context and `ON_START BRANCHES` to vary the welcome content. Use channel configuration for launcher copy that appears outside the conversation transcript. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Channel_Welcome_Agent GOAL: "Adapt welcome copy by channel context" ON_START: SET: channel_name = session.channel BRANCHES: - IF: channel_name == "voice" RESPOND: "Thanks for calling. Say billing, orders, or agent." VOICE: PLAIN_TEXT: "Thanks for calling. Say billing, orders, or agent." - ELSE: RESPOND: "Welcome. You can type billing, orders, or agent." FORMATS: markdown: | **Welcome.** Type a topic to begin. FLOW: entry_point: ready steps: - ready ready: REASONING: false RESPOND: "Ready." THEN: COMPLETE ``` ## How it works The runtime copies the channel name into `channel_name`, evaluates branches in order, and selects the first matching response. The voice branch includes a `VOICE` payload. The non-voice branch includes markdown. ## Common variations ### Keep launcher copy separate ```yaml expandable=true theme={null} AGENT: Launcher_Copy_Agent GOAL: "Keep channel launcher copy separate from the ABL startup message" ON_START: RESPOND: "Welcome inside the conversation." FLOW: entry_point: ready steps: - ready ready: REASONING: false RESPOND: "Ask a question to continue." THEN: COMPLETE ``` Launcher welcome text, placeholder text, and connecting status text are channel configuration. The ABL `ON_START` response is conversation content. ## Verification Start sessions for each target channel. Confirm the conversation transcript receives the ABL welcome only on channels that support proactive startup delivery. For suppressed channels, confirm `ON_START` side effects still run and traces include `dsl_on_start_skipped`. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Treating launcher text as the ABL welcome | Both appear near session start in web chat. | Keep launcher copy in channel configuration and conversation copy in `ON_START`. | | Writing one welcome for every channel | Text chat, voice, and async channels have different delivery constraints. | Branch from channel context and test the deployed channel contract. | | Putting channel side effects inside branches | `ON_START` branches select responses, not startup side effects. | Run `SET` or `CALL` at top-level `ON_START`, then branch on the resulting values. | ## Troubleshooting If a welcome appears twice in web chat, check whether the SDK launcher message and ABL `ON_START` response say the same thing. If a voice greeting is too long, move details into the next turn and keep startup speech short. ## Production readiness checklist * Test each deployed channel separately. * Keep launcher copy and conversation copy distinct. * Use `VOICE` payloads for voice-specific phrasing. * Provide text fallback for rich content. * Verify suppression behavior for async channels. # How to design reusable agents and modules for large projects Source: https://koreai.mintlify.app/agent-platform/abl/how-to/design-reusable-agent-modules [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this pattern when a project has many assistants, specialists, tools, and workflows that need to be composed without duplicating responsibilities or creating routing ambiguity. ## Concept Reusable design in ABL starts with stable agent boundaries. A reusable agent owns a business capability, has a clear goal, accepts a predictable context package, and can be called from more than one supervisor or entry point. Do not make reuse mean "one giant shared agent." Reuse works best when specialists are small enough to be reliable but broad enough to represent a durable business capability, such as product advice, order status, billing disputes, identity verification, or appointment scheduling. For tools, current validation rejects removed `FROM ... USE` tool import syntax in agent DSL. Keep agent files focused on agent behavior and manage tool implementations through project tools and deployment configuration. ## Minimal working example ```yaml theme={null} SUPERVISOR: Commerce_Supervisor GOAL: "Route shopping requests to reusable commerce specialists" PERSONA: "Concise commerce routing supervisor" HANDOFF: - TO: Product_Advisor WHEN: "The user asks for product recommendations, comparisons, or availability" PASS: [customer_id, locale] SUMMARY: "User needs shopping advice" - TO: Order_Status_Agent WHEN: "The user asks about an existing order, delivery, or return status" PASS: [customer_id, order_id] SUMMARY: "User needs order status help" ``` ```yaml theme={null} AGENT: Product_Advisor GOAL: "Help customers compare products and choose the best option" PERSONA: "Practical shopping advisor" ``` ```yaml theme={null} AGENT: Order_Status_Agent GOAL: "Help customers understand order, delivery, and return status" PERSONA: "Precise order support specialist" ``` ## How it works The supervisor is reusable because it routes to stable capability agents. The specialist agents are reusable because they describe their responsibility without depending on one specific entry point. The `PASS` fields are the integration contract between the supervisor and specialists. In a large project, keep the reusable boundary visible in three places: the agent goal, the routing condition, and the context package. If those three disagree, reuse becomes fragile. Since neither `HANDOFF` above declares `HISTORY`, each specialist now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). ## Common variations ### Shared specialist agent Use one specialist from several supervisors when the business capability is truly the same. For example, `Order_Status_Agent` can serve commerce chat, WhatsApp, and voice entry points if the required context is consistent. ### Channel-specific wrapper Use a thin channel-specific supervisor when routing or welcome behavior differs by channel, while the underlying specialists remain shared. ### Project manifest composition Use the project manifest to list the agents, entry agent, and project assets. Keep agent names stable because handoff targets depend on them. ### Shared tool implementations Manage reusable tool implementations in Project Tools. Agent DSL should declare behavior and use tools through validated current syntax; do not rely on removed `FROM ... USE` imports. ## Verification * Validate the full project group, not only one agent file. * Confirm every handoff target exists in the same project. * Test each reusable specialist from every entry point that can route to it. * Confirm every `PASS` field has a source before handoff. * Inspect traces to prove the active agent changes to the intended specialist. ## Production readiness checklist * Agent names are stable and unique. * Each reusable agent has one durable business capability. * Handoff context is minimal, explicit, and privacy-reviewed. * Shared specialists have tests from each supervisor or channel entry point. * Tool bindings and permissions are configured outside removed import syntax. * Ownership is clear for shared modules so one team does not break another team's entry point. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Reusing an agent with a vague goal | It feels flexible | Define a durable business capability | | Passing too much context | Teams want reuse to be easy | Pass only fields the specialist needs | | Depending on removed tool import syntax | Older examples may still show it | Use current Project Tools setup | | Sharing specialists without shared tests | The first entry point works | Test every supervisor and channel that can call the specialist | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Handoff target is not found | Agent name mismatch or missing project asset | Compare `TO:` targets with project agent names | | Specialist gives entry-point-specific answers | Its goal or persona includes one channel or journey | Rewrite it around the reusable capability | | A shared change breaks another flow | No owner or compatibility test for the module | Add cross-entry tests before changing shared specialists | | Tool setup fails after reuse | Tool implementation was assumed in agent DSL | Verify Project Tools bindings and deployment configuration | *** **Related articles:** * [How to decide whether to build one agent or multiple specialist agents](/agent-platform/abl/how-to/choose-single-agent-or-specialists). * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). # How to design a supervisor that routes users to specialist agents Source: https://koreai.mintlify.app/agent-platform/abl/how-to/design-supervisor-routing-agent [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this guide when one conversational entry point must identify what the user needs, choose the right specialist, and pass enough context for that specialist to continue. ## Concept A supervisor is the front door for a multi-agent experience. Its primary job is not to solve every problem. Its job is to understand the request, choose the right owner, and transfer context. A specialist agent owns one business capability. A good specialist has a narrow goal, the right tools or data access, clear limitations, and a completion path. Routing is the decision that connects the user request to the correct owner. In ABL, the executable route is `HANDOFF`. `AGENTS:` is an optional roster that makes the supervisor easier to read, but it does not route by itself. ## Decision guide: routing condition styles | Style | Example | Use when | | - | - | - | | Declared intent category | `WHEN: intent.category == "order_status"` | You want stable route labels for testing and reporting. | | Structured condition | `WHEN: customer_id IS SET` | The route depends on known fields or variables. | | Quoted semantic condition | `WHEN: "the user is asking for a refund"` | The route is best described as plain-language guidance. | Quote natural-language conditions. Unquoted text can be parsed as variable names and produce undefined-variable warnings. ## Minimal working example: support supervisor project This example is a project-level set. Put the supervisor and target agents in the same project. `agents/support-supervisor.agent.abl` ```yaml expandable=true theme={null} SUPERVISOR: Support_Supervisor GOAL: "Route support requests to the right specialist" AGENTS: account: Account_Support_Agent orders: Order_Status_Agent human: Live_Agent INTENTS: account: "Account access or password help" order_status: "Order tracking or delivery help" human_help: "User asks for a person or the request is unclear" GATHER: customer_id: prompt: "What customer ID should I use for this request?" type: string required: true HANDOFF: - TO: Account_Support_Agent WHEN: intent.category == "account" PASS: [customer_id] SUMMARY: "Customer needs account or password support" RETURN: true - TO: Order_Status_Agent WHEN: intent.category == "order_status" PASS: [customer_id] SUMMARY: "Customer needs order status or delivery support" RETURN: false - TO: Live_Agent WHEN: intent.category == "human_help" PASS: [customer_id] SUMMARY: "Customer asks for a person or the request is unclear" RETURN: false ``` `agents/account-support-agent.agent.abl` ```yaml theme={null} AGENT: Account_Support_Agent GOAL: "Resolve account access and password support requests" GATHER: customer_id: prompt: "What customer ID should I use?" type: string required: true COMPLETE: - WHEN: customer_id IS SET RESPOND: "I can continue account support for {{customer_id}}." ``` `agents/order-status-agent.agent.abl` ```yaml theme={null} AGENT: Order_Status_Agent GOAL: "Resolve order status and delivery support requests" GATHER: customer_id: prompt: "What customer ID should I use?" type: string required: true COMPLETE: - WHEN: customer_id IS SET RESPOND: "I can continue order support for {{customer_id}}." ``` `agents/live-agent.agent.abl` ```yaml theme={null} AGENT: Live_Agent GOAL: "Transfer the customer to a human support queue" COMPLETE: - WHEN: true RESPOND: "I will connect you with a human support specialist." ``` The supervisor gathers `customer_id`, then passes it with `PASS`. `RETURN: true` on account support means bounded specialist work can return to the supervisor. `RETURN: false` on order status and live agent means the target keeps ownership. Since none of the `HANDOFF` entries above declare `HISTORY`, each specialist now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). Also, because each `WHEN` here is a single-field comparison (`intent.category == "account"`), an unset field simply makes the comparison evaluate `false` — no error, and the trace shows your literal source text. ## Common variations ### Plain-language routing variation Use quoted semantic conditions when business users can describe a route more clearly in natural language than as a strict expression. Treat this as an alternative supervisor project to the previous example; do not combine both supervisors in the same deployment unless the project explicitly declares the intended entry supervisor. `agents/billing-supervisor.agent.abl` ```yaml theme={null} SUPERVISOR: Billing_Supervisor GOAL: "Route billing questions to the right specialist" HANDOFF: - TO: Pending_Payments WHEN: "user is asking about payment status, unpaid balance, invoice due date, or how to pay" SUMMARY: "Customer needs help with pending payments or payment instructions" - TO: Refund_Guidance WHEN: "user is asking about a refund, reimbursement, returned payment, claim payout, or refund timeline" SUMMARY: "Customer needs refund guidance or refund status help" ``` `agents/pending-payments.agent.abl` ```yaml theme={null} AGENT: Pending_Payments GOAL: "Help customers understand pending payments and payment instructions" COMPLETE: - WHEN: true RESPOND: "I can help with pending payments and payment instructions." ``` `agents/refund-guidance.agent.abl` ```yaml theme={null} AGENT: Refund_Guidance GOAL: "Help customers understand refund status and refund next steps" COMPLETE: - WHEN: true RESPOND: "I can help with refund status and refund next steps." ``` Keep semantic routes distinct. If two routes could match the same request, customers may experience inconsistent routing. ## Designing the context package Every handoff should answer three questions. | Question | Design guidance | | - | - | | What does the target need to know? | Pass only useful fields, such as `customer_id`, `order_id`, `issue_summary`, or `transfer_reason`. | | Why is the transfer happening? | Use `SUMMARY` so the target starts with a concise reason. | | Should control come back? | Use `RETURN: true` for bounded specialist work and `RETURN: false` for permanent transfer. | Do not pass every field by default. Pass the context the target needs to continue safely. ## Fallback and ambiguity design Do not rely on the roster as a fallback. If no handoff is selected, the supervisor remains the active agent and should clarify, retry, or route to a fallback based on your design. For production supervisors: * Add a route for human help, unsupported requests, or unclear requests. * Make route descriptions mutually exclusive. * Add test utterances that intentionally look ambiguous. * Inspect traces to confirm the selected handoff target. * If your runtime configuration supports multi-intent disambiguation, verify that the user is asked to choose when a message contains multiple requests. ## Verification 1. Validate the supervisor and all target agents together. 2. Send: "I forgot my password." Expect `Account_Support_Agent`. 3. Send: "Where is my order?" Expect `Order_Status_Agent`. 4. Send: "I need to talk to someone." Expect `Live_Agent`. 5. Confirm `customer_id` is gathered before routes that pass it. 6. Inspect trace/debug output for handoff target, return behavior, and context passed. ## Production readiness checklist * Every `TO` target exists and has a clear owner. * Every `PASS` field is populated before handoff. * Every route has a distinct condition and summary. * A human or fallback route exists. * Route tests cover success, no-match, ambiguous, and escalation paths. * Trace/debug output is reviewed for each route before go-live. * Support owners agree on what `RETURN: true` and `RETURN: false` mean for their customer journey. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Treating `AGENTS:` as routing | The roster looks like configuration. | Add `HANDOFF` entries for executable routes. | | Passing unavailable context | The route references a field that was never gathered or produced. | Gather it, read it from memory, or remove it from `PASS`. | | Omitting fallback | Happy-path routing looks complete. | Add human, unsupported, or clarification handling. | | Overlapping route conditions | Business categories are not mutually exclusive. | Test ambiguous utterances and tighten route descriptions. | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | No transfer happens | No handoff route was selected. | Check `HANDOFF`, `WHEN`, intent category, and fallback coverage. | | Wrong target receives the user | Route conditions overlap. | Rewrite conditions and add route-specific test utterances. | | Target asks for information already collected | Context was not passed or field names differ. | Check `PASS`, target `GATHER`, and trace/debug context. | # How to handle empty-state and fallback greetings Source: https://koreai.mintlify.app/agent-platform/abl/how-to/handle-fallback-greetings [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when startup context may be missing, a branch may not match, or a channel may not deliver proactive welcome content. ## Concept A fallback greeting is the safe default that prevents the first turn from feeling broken. Fallbacks are needed because startup conditions can fail, channel metadata can be absent, and some delivery contracts suppress proactive startup messages. In ABL, a top-level `ON_START RESPOND` can act as the fallback when branches do not match. If there is no `ON_START`, the first flow step can be the greeting. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Fallback_Greeting_Agent GOAL: "Recover when startup conditions do not match" ON_START: RESPOND: "Welcome. I can help with service, billing, or returns." BRANCHES: - IF: session.interaction.current.language == "fr" RESPOND: "Bonjour. Je peux vous aider avec le service, la facturation ou les retours." FLOW: entry_point: ready steps: - ready ready: REASONING: false RESPOND: "How can I help?" THEN: COMPLETE ``` ## How it works The runtime evaluates the branch. If the language is French, the branch response is selected. If it is not French, the top-level response is used. Because the top-level response is a fallback prelude, the flow can continue into `ready`. ## Common variations ### Start without ON\_START Use the first flow step as the greeting when the channel or product experience should not send proactive content. ```yaml theme={null} AGENT: No_Startup_Handler_Agent GOAL: "Start safely even without a proactive startup response" FLOW: entry_point: first_prompt steps: - first_prompt first_prompt: REASONING: false RESPOND: "Hello. What can I help you with?" THEN: COMPLETE ``` ## Verification Test a matching branch, a non-matching branch, and a session without `ON_START`. Confirm every path produces a clear first customer-facing response. In traces, branch misses should not cause an error when a top-level fallback exists. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Omitting the generic fallback | Branch examples focus on the happy path. | Add top-level `ON_START RESPOND` or make the first flow step a safe greeting. | | Personalizing fallback copy with unverified data | Startup metadata may be missing or untrusted. | Keep fallback copy generic until identity and context are known. | | Treating a branch miss as an error | Branches are conditional by design. | Test branch misses and verify the fallback response. | ## Troubleshooting If no greeting appears, confirm whether the channel suppresses startup payloads. If a malformed branch condition is present, the runtime falls back instead of selecting the broken branch; inspect traces for branch diagnostics. ## Production readiness checklist * Always define a generic startup or first-flow fallback. * Keep fallback copy broad enough for unknown users. * Avoid sensitive personalization in fallback messages. * Test branch miss and missing-context cases. * Confirm the no-`ON_START` path still has a useful first flow step. # How to handle messages that contain multiple user intents Source: https://koreai.mintlify.app/agent-platform/abl/how-to/handle-multiple-intents [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Use this pattern when customers naturally bundle several tasks into a single message, such as "check my balance, pay my bill, and update my address." The `MULTI_INTENT:` section lets the agent detect, triage, and dispatch those intents instead of silently dropping secondary requests. ## Concept When a user message contains more than one intent, ABL can detect the individual intents, assess their relationships, and handle them according to a configurable strategy. The `MULTI_INTENT:` section is a top-level ABL block available on both `AGENT:` and `SUPERVISOR:` declarations. The system uses three layers to decide multi-intent behavior: 1. **Agent-level** — the `MULTI_INTENT:` section in the agent's ABL. 2. **Project-level** — the project runtime configuration (set in Studio or via API). 3. **Platform defaults** — built-in fallback values. Agent-level settings take precedence over project-level, which takes precedence over platform defaults. When multi-intent detection is enabled, the NLU pipeline assesses the relationship between detected intents as one of: * **independent** — intents can be handled in any order with no dependency between them. * **dependent** — later intents require results from earlier ones. * **ambiguous** — the system cannot determine the relationship. This assessment drives strategy resolution, especially for the `auto` strategy. ## Minimal working example ```yaml theme={null} SUPERVISOR: Multi_Intent_Service_Supervisor GOAL: "Handle messages that contain more than one customer intent" MULTI_INTENT: enabled: true strategy: primary_queue max_intents: 3 confidence_threshold: 0.6 queue_max_age_ms: 600000 INTENTS: check_balance: "Customer wants account balance." pay_bill: "Customer wants to pay a bill." update_address: "Customer wants to change address." HANDOFF: - TO: Balance_Agent WHEN: intent.category == "check_balance" RETURN: true CONTEXT: pass: [customer_id, account_id] summary: "Customer wants to check balance." - TO: Bill_Payment_Agent WHEN: intent.category == "pay_bill" RETURN: true CONTEXT: pass: [customer_id, account_id] summary: "Customer wants to pay a bill." - TO: Address_Update_Agent WHEN: intent.category == "update_address" RETURN: true CONTEXT: pass: [customer_id] summary: "Customer wants to update address." AGENT: Balance_Agent GOAL: "Handle balance requests" AGENT: Bill_Payment_Agent GOAL: "Handle bill payment" AGENT: Address_Update_Agent GOAL: "Handle address changes" ``` The `customer_id` and `account_id` variables passed via `CONTEXT: pass:` must be gathered, set from runtime context, or populated by a tool result before the handoff executes. These are project-local assumptions — replace them with the actual variable names your agents produce. When `history:` is omitted on a `HANDOFF`, the child agent receives the full parent conversation history (the current platform default is `full`). ## How it works 1. The agent receives the user message and multi-intent detection runs as part of the NLU pipeline. 2. Detected intents are filtered by `confidence_threshold` — only intents above the threshold are considered. 3. The number of considered intents is capped at `max_intents`. 4. The NLU layer assesses the relationship between the intents (independent, dependent, or ambiguous). 5. The `strategy` (or `auto` resolution based on the relationship assessment) determines how intents are dispatched. 6. For `primary_queue` and `sequential`, queued intents are surfaced after the current intent completes. Queued intents expire after `queue_max_age_ms` milliseconds. ## MULTI\_INTENT properties | Property | Type | Default | Valid range | Description | | - | - | - | - | - | | `enabled` | boolean | `true` | true/false | Whether multi-intent detection is active. | | `strategy` | string | `primary_queue` | `primary_queue`, `sequential`, `parallel`, `disambiguate`, `auto` | How detected intents are dispatched. | | `unknown_relationship_strategy` | string | `parallel` | `sequential`, `parallel`, `disambiguate` | Fallback strategy when the intent relationship cannot be determined. Used by the `auto` strategy and supervisor routing when relationship data is missing. | | `max_intents` | integer | `3` | 1 - 10 | Maximum number of intents to consider per message. | | `confidence_threshold` | number | `0.6` | 0.0 - 1.0 | Minimum confidence for a secondary intent to be accepted. | | `queue_max_age_ms` | integer | `600000` | 0 - 3,600,000 | Time-to-live for queued intents in milliseconds. Expired intents are dropped. | ## Strategy reference ### primary\_queue (default) Handle the highest-confidence intent immediately and queue the rest. After the primary intent completes, the agent surfaces queued intents one by one for confirmation. The user can accept, decline, or let them expire. ### sequential Execute detected intents one at a time in authored or detected order. Context from earlier intents carries forward. Use when later intents depend on earlier results — for example, authenticate first, then process payment, then send confirmation. ```yaml theme={null} AGENT: Sequential_Support GOAL: "Handle dependent support requests in order" MULTI_INTENT: strategy: sequential max_intents: 4 confidence_threshold: 0.7 FLOW: entry_point: detect steps: - detect - authenticate - payment - confirmation detect: REASONING: false ON_INPUT: - IF: input contains "pay" THEN: authenticate - ELSE: RESPOND: "How can I help?" THEN: COMPLETE authenticate: REASONING: false RESPOND: "Authentication complete." THEN: payment payment: REASONING: false RESPOND: "Payment processed." THEN: confirmation confirmation: REASONING: false RESPOND: "Confirmation sent." THEN: COMPLETE ``` ### parallel Fan out to sub-agents simultaneously. Only available for supervisor agents that route to independent child agents. The runtime enforces safety: * If the agent is not a supervisor, `parallel` downgrades to `sequential`. * If the relationship between intents is `dependent`, `parallel` downgrades to `sequential`. * If the relationship is `ambiguous`, `parallel` downgrades to `disambiguate`. ```yaml theme={null} SUPERVISOR: Parallel_Service_Supervisor GOAL: "Handle independent customer requests in parallel" MULTI_INTENT: enabled: true strategy: parallel unknown_relationship_strategy: parallel HANDOFF: - TO: Billing_Specialist WHEN: intent.category == "billing" RETURN: true CONTEXT: pass: [customer_id] - TO: Shipping_Specialist WHEN: intent.category == "shipping" RETURN: true CONTEXT: pass: [customer_id, order_id] AGENT: Billing_Specialist GOAL: "Handle billing inquiries" AGENT: Shipping_Specialist GOAL: "Handle shipping inquiries" ``` ### disambiguate Present detected intents to the user and ask which to handle first. The user selects by number or by exact intent name. The disambiguation prompt text is customizable. ### auto Let the model decide the strategy at runtime based on the assessed relationship between intents: * **independent** intents on a supervisor resolve to `parallel`; on a non-supervisor, they resolve to `sequential`. * **dependent** intents resolve to `sequential` regardless of agent type. * **ambiguous** intents resolve to `disambiguate`. ```yaml theme={null} SUPERVISOR: Auto_Router GOAL: "Route customer requests using relationship-aware strategy" MULTI_INTENT: strategy: auto unknown_relationship_strategy: sequential max_intents: 5 INTENTS: billing: "Billing and payment questions." technical: "Technical support and troubleshooting." account: "Account management tasks." HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" RETURN: true - TO: Technical_Agent WHEN: intent.category == "technical" RETURN: true - TO: Account_Agent WHEN: intent.category == "account" RETURN: true AGENT: Billing_Agent GOAL: "Handle billing questions" AGENT: Technical_Agent GOAL: "Handle technical support" AGENT: Account_Agent GOAL: "Handle account management" ``` ## Customizing user-facing prompts The disambiguation and queue-surfacing prompts can be customized through the `MESSAGES:` block: ```yaml theme={null} MESSAGES: multi_intent_disambiguate_header: "I noticed your message may contain multiple requests. Which would you like me to help with first?" multi_intent_disambiguate_option: "{{index}}. {{intent}} (confidence: {{confidence}})" multi_intent_disambiguation_reprompt: "Please choose an option, pause, or cancel." multi_intent_queued_notice: "I will address your other requests after completing the current one." multi_intent_queued_follow_up: "Next: {{next_intent}}. Would you like me to help with that?" ``` ## Verification * Parse and compile the ABL to confirm there are no errors or warnings. * Send a test utterance that contains two or more intents covered by your `INTENTS:` or `HANDOFF:` conditions. * Inspect traces for `multi_intent_plan_built` to confirm the detected strategy and targets. * For `primary_queue`, check for `multi_intent_queue_surfaced` traces when the primary intent completes. * For `disambiguate`, check for `multi_intent_disambiguate_choice` traces. * For `parallel`, check for `multi_intent_parallel_executed` traces. * Confirm that a single-intent utterance bypasses multi-intent handling and routes normally. * Test a message where all intents fall below `confidence_threshold` to verify fallback behavior. ## Production readiness checklist * Every specialist route has a clear owner and a concise context summary. * Deterministic conditions use declared, gathered, tool-result, runtime, or returned fields. * Semantic conditions are quoted as natural-language `WHEN` text. * Temporary child agents (with `RETURN: true`) produce every field mapped in `ON_RETURN`. * Fallback behavior is explicit and does not hide missing intent coverage. * `max_intents` is set to a value that matches realistic customer behavior (typically 2-5); values above 5 may degrade conversation quality. * `confidence_threshold` is tuned to avoid false positives from low-confidence secondary intents. * `queue_max_age_ms` is set to a reasonable session window (default 600,000 ms = 10 minutes; maximum 3,600,000 ms = 1 hour). * For `parallel` strategy on a supervisor, confirm all target agents can operate independently. * Consider setting `unknown_relationship_strategy` to `sequential` or `disambiguate` if safety is more important than speed. * If using `auto` strategy, test with messages that produce each relationship type (independent, dependent, ambiguous). * Review and customize disambiguation and queue prompts via `MESSAGES:` for your brand voice. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Enabling `parallel` on a non-supervisor agent | Only supervisors can fan out to sub-agents | The runtime automatically downgrades to `sequential`, but configure it explicitly to avoid confusion | | Setting `max_intents` above 5 | Hoping to catch every possible intent | High values fragment the conversation; keep it at 3-5 for production | | Assuming queued intents never expire | Not realizing `queue_max_age_ms` has a finite default | Set `queue_max_age_ms` to match your expected session duration | | Using `EXPECT_RETURN` instead of `RETURN` | Copying from older examples | `EXPECT_RETURN` is a legacy alias; use `RETURN: true` or `RETURN: false` | | Omitting `unknown_relationship_strategy` | Not realizing the field exists | The default is `parallel`; set it to `sequential` or `disambiguate` if your intents might have dependencies | | Using `parallel` for dependent work | Assuming all fan-out is safe | With `auto` strategy the runtime detects this; with explicit `parallel` it downgrades dependent relationships to `sequential` | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Secondary intents are silently dropped | `confidence_threshold` is too high or `max_intents` is too low | Lower `confidence_threshold` or increase `max_intents`; inspect `multi_intent_plan_built` traces | | Parallel routes execute sequentially | Agent is not a supervisor, or intents have dependent/ambiguous relationships | Check traces for strategy downgrade; confirm the agent uses `SUPERVISOR:` declaration | | Disambiguation prompt does not appear | Strategy is not `disambiguate` (or `auto` with ambiguous relationships) | Check `multi_intent_disambiguate_choice` traces; verify strategy config | | Queued intents expire before the user is asked | `queue_max_age_ms` is too short for the conversation flow | Increase `queue_max_age_ms` (max 3,600,000 ms) | | User sees default English prompts | Custom `MESSAGES:` block not set | Add customized `multi_intent_disambiguate_header` and `multi_intent_queued_notice` to MESSAGES | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route conversations based on user intent](/agent-platform/abl/how-to/route-by-user-intent). * [How to ask a clarification question before routing](/agent-platform/abl/how-to/clarify-before-routing). # How to initialize session variables before the first user turn Source: https://koreai.mintlify.app/agent-platform/abl/how-to/initialize-session-before-first-turn [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when flow steps, routing, templates, or tools need known session values before the user says anything. ## Concept Startup initialization belongs in `ON_START`. Values set there are written into session data before the welcome response and before the entry flow runs. This is useful for counters, channel labels, selected locale, feature flags, campaign markers, and any value that should be stable for the rest of the conversation. Keep initialization deterministic. `ON_START SET` is for values already available at startup, not values that require asking the user. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Session_Init_Agent GOAL: "Initialize session values before the first flow step" ON_START: SET: retry_count = 0 SET: channel_name = session.channel SET: preferred_language = session.interaction.current.language RESPOND: "Session is ready." FLOW: entry_point: show_context steps: - show_context show_context: REASONING: false RESPOND: "Channel {{channel_name}}, language {{preferred_language}}, retries {{retry_count}}." THEN: COMPLETE ``` ## How it works `SET` assignments run before `RESPOND`. Assignment values are resolved against runtime session values. The flow can use the initialized variables immediately. ## Common variations ### Initialize project or campaign values Use constants or channel-supplied values for campaign and entry-state variables. This example uses constants; in a real channel integration, those values are usually copied from trusted session metadata. ```yaml expandable=true theme={null} AGENT: Metadata_Init_Agent GOAL: "Copy startup metadata into session variables" ON_START: SET: campaign_id = "summer-renewal" SET: landing_page = "member portal" RESPOND: "Welcome from {{landing_page}}." FLOW: entry_point: ready steps: - ready ready: REASONING: false RESPOND: "Campaign {{campaign_id}} is active for this session." THEN: COMPLETE ``` ## Verification Start a session and inspect session values after initialization. Confirm `retry_count`, `channel_name`, and `preferred_language` are present before the first flow response. In traces, check `dsl_set` events with `source: on_start`. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Setting values that are not available yet | Startup runs before the first user turn. | Use `ON_START SET` only for constants and pre-existing runtime context. | | Using different names later in the flow | Startup values are easy to rename while drafting. | Reuse the exact variable names in templates, `WHEN`, `PASS`, and tools. | | Collecting user input with startup `SET` | Initialization and data collection get mixed together. | Use `GATHER` for user-provided values. | ## Troubleshooting If a variable renders as empty, verify the runtime path exists. For channel-specific fields, confirm the channel actually supplies the value before the session starts. If the value should come from the user, collect it with `GATHER` instead of startup `SET`. ## Production readiness checklist * Initialize only values available before the first user turn. * Keep names consistent with later `WHEN`, `PASS`, and template references. * Avoid writing sensitive values unless they are needed. * Trace every startup assignment during validation. * Reset counters intentionally when a new session starts. # Artemis how-to guides Source: https://koreai.mintlify.app/agent-platform/abl/how-to/overview These task-based guides help you build ABL agents. Each guide starts with the decision or pattern you need, then gives you working ABL to copy into a project. Use the guides alongside the [ABL reference](/agent-platform/abl/reference/language-overview). The reference explains what each section and keyword does. The guides show which sections to combine for a scenario, and why. ## Prerequisites Before you use these guides: * Read the [ABL overview](/agent-platform/abl/reference/language-overview) to understand sections, indentation, and the goal-driven execution model. * Get access to a project where you can create or edit agents. * Identify the tools, workflows, and knowledge bases your agent uses. The examples reference these by name, so you must replace the placeholder names with your own. ## Guide categories Choose an agent pattern, split work across agents, and set execution behavior. Control greetings, startup logic, session setup, and channel differences. Route each conversation to the owning agent and debug the routing decision. *** ## Agent architecture and design Decide how many agents to build, how to split their work, and how to configure their execution. | Guide | Use it when | | - | - | | [How to choose between a reasoning agent and a FLOW-based agent](/agent-platform/abl/how-to/choose-reasoning-agent-or-flow) | You need to decide whether an agent should be goal-driven and flexible, step-driven and predictable, or a mix of both. | | [How to decide whether to build one agent or multiple specialist agents](/agent-platform/abl/how-to/choose-single-agent-or-specialists) | One customer-facing experience could be one broad agent or a supervisor with specialists. | | [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent) | One entry point must identify what the user needs, choose a specialist, and pass enough context. | | [How to define clear responsibilities for each enterprise agent](/agent-platform/abl/how-to/define-agent-responsibilities) | An agent is becoming too broad or too vague for builders and support teams to reason about. | | [How to combine reasoning steps with deterministic FLOW steps](/agent-platform/abl/how-to/combine-reasoning-and-flow-steps) | A conversation needs both flexible judgment and predictable execution. | | [How to write effective agent goals, personas, instructions, and limitations](/agent-platform/abl/how-to/write-agent-goals-personas-instructions) | An agent needs a clear job boundary, a consistent voice, and explicit limits. | | [How to use behavior profiles to change agent behavior by context](/agent-platform/abl/how-to/use-behavior-profiles-by-context) | The same agent keeps its core responsibility but must change style, constraints, or tools by context. | | [How to configure model, timeout, and execution settings for an agent](/agent-platform/abl/how-to/configure-agent-execution-settings) | An agent needs explicit model choice, response limits, timeout boundaries, or history windowing. | | [How to design reusable agents and modules for large projects](/agent-platform/abl/how-to/design-reusable-agent-modules) | Many agents, tools, and workflows must compose without duplicated responsibilities or routing ambiguity. | | [How to use EXECUTION.pipeline for agent routing, classification, and short-circuiting](/agent-platform/abl/how-to/use-agent-execution-pipeline) | A supervisor needs classifier-assisted routing before the normal reasoning path decides what to do. | | [How to configure EXECUTION.pipeline models, modes, thresholds, and fallbacks](/agent-platform/abl/how-to/configure-agent-execution-pipeline) | The execution pipeline exists but needs tuning for model, mode, confidence thresholds, or fallback behavior. | ## Conversation entry and welcome experience Control what happens before and during the first user turn: greetings, startup logic, session setup, and channel differences. | Guide | Use it when | | - | - | | [How to create a welcome message for a new conversation](/agent-platform/abl/how-to/create-welcome-message) | A conversation should open with a clear first message before the user asks for help. | | [How to use ON\_START to greet users before the first message](/agent-platform/abl/how-to/use-on-start-welcome) | The agent must run startup logic before the user sends the first turn. | | [How to personalize a welcome message for returning users](/agent-platform/abl/how-to/personalize-returning-user-welcome) | The agent should recognize a known user and make the first turn specific to that user. | | [How to run an authentication or profile lookup during welcome](/agent-platform/abl/how-to/run-auth-profile-lookup-on-start) | The first turn should use authenticated context, such as account status or display name. | | [How to route users from the welcome experience to the right agent](/agent-platform/abl/how-to/route-users-from-welcome) | The first assistant turn should guide the user into the right specialist path. | | [How to initialize session variables before the first user turn](/agent-platform/abl/how-to/initialize-session-before-first-turn) | Flow steps, routing, templates, or tools need known session values before the user says anything. | | [How to design channel-specific welcome experiences](/agent-platform/abl/how-to/design-channel-specific-welcome) | Web chat, voice, messaging, or async channels need different startup behavior. | | [How to handle empty-state and fallback greetings](/agent-platform/abl/how-to/handle-fallback-greetings) | Startup context may be missing, a branch may not match, or a channel may not deliver proactive content. | ## Routing and multi-agent orchestration Send each conversation to the agent that owns the work, handle unclear or bundled requests, and debug the routing decision. | Guide | Use it when | | - | - | | [How to route users by detected intent](/agent-platform/abl/how-to/route-by-user-intent) | A supervisor classifies what the user is trying to do, then hands the conversation to the specialist that owns it. | | [How to design supervisor routing with priority gates](/agent-platform/abl/how-to/route-with-supervisor-priority) | Mandatory checks such as authentication, fraud screening, or consent must run before a business route. | | [How to route by role, channel, language, and session state](/agent-platform/abl/how-to/route-by-role-channel-language-state) | The same user intent must branch by channel, language, role, or current session state. | | [How to route by tool result, profile lookup, or remembered state](/agent-platform/abl/how-to/route-by-tool-result-or-memory) | Routing depends on profile, case status, risk, entitlement, or a remembered preference, not just the utterance. | | [How to route unclear or unsupported requests to a fallback agent](/agent-platform/abl/how-to/route-to-fallback-agent) | The supervisor cannot confidently choose a specialist, or the request is outside the supported intent catalog. | | [How to handle messages that contain multiple user intents](/agent-platform/abl/how-to/handle-multiple-intents) | Customers bundle several tasks into a single message and secondary requests must not be dropped. | | [How to clarify before routing to a specialist agent](/agent-platform/abl/how-to/clarify-before-routing) | A request is short, ambiguous, overloaded, or missing the one field that determines ownership. | | [How to switch the active agent during a conversation](/agent-platform/abl/how-to/switch-active-agent) | A supervisor should stop answering directly and let a specialist own the next turn. | | [How to debug routing decisions in traces](/agent-platform/abl/how-to/debug-routing-traces) | You need to explain why a user reached one specialist, or why a route fell through to fallback. | # How to personalize a welcome message for returning users Source: https://koreai.mintlify.app/agent-platform/abl/how-to/personalize-returning-user-welcome [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when the agent should recognize a known user and make the first turn feel specific to that user. ## Concept Personalization at startup has two moving pieces: a source of user context and an `ON_START` response that uses it. The context can come from runtime session values, channel metadata copied into session values, memory recall, or a startup tool call. Use only low-risk personalization in the welcome. Do not expose sensitive account details until identity and authorization are established. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Returning_User_Welcome_Agent GOAL: "Personalize the welcome message for a known user" TOOLS: lookup_profile(memberId: string) -> {content: string, first_name: string, tier: string} description: "Lookup the returning user's profile" side_effects: false confirm: never params: memberId: description: "Member identifier supplied by the channel session" ON_START: CALL: lookup_profile WITH: memberId: session.member_id AS: profile RESPOND: "Welcome back {{profile.first_name}}. You are on the {{profile.tier}} plan." FLOW: entry_point: ask_need steps: - ask_need ask_need: REASONING: false RESPOND: "What can I help you with today?" THEN: COMPLETE ``` ## How it works The tool call runs during startup. Its result is stored under `profile` because the call uses `AS: profile`. The response interpolates `profile.first_name` and `profile.tier`. The tool is declared as read-only with `side_effects: false` and `confirm: never`, because a profile lookup should not require the user to approve a side effect. ## Common variations ### Personalize by segment Use startup `SET` when the value is already known at session start. ```yaml expandable=true theme={null} AGENT: Segment_Welcome_Agent GOAL: "Use returning-user segment metadata in the welcome" ON_START: SET: customer_segment = "premium" BRANCHES: - IF: customer_segment == "premium" RESPOND: "Welcome back. Your priority support lane is ready." - ELSE: RESPOND: "Welcome back. How can I help?" FLOW: entry_point: ready steps: - ready ready: REASONING: false RESPOND: "Ready." THEN: COMPLETE ``` ## Verification Start a session with a known `session.member_id`. Confirm that the startup tool receives the member id, the `profile` value is available in session data, and the welcome uses the returned profile fields. In traces, look for startup tool execution followed by `dsl_respond`. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Exposing sensitive account facts in the welcome | Personalization is mistaken for authorization. | Use only low-risk fields until identity and permissions are verified. | | Forgetting `AS` on the startup lookup | The response cannot reference a stable result object. | Bind the startup tool result with `AS` and interpolate from that object. | | Making a read-only lookup look side-effecting | Tool metadata is incomplete. | Declare startup lookups with `side_effects: false`, `confirm: never`, and parameter descriptions. | ## Troubleshooting If the welcome shows an empty value, verify the tool result shape and the `AS` name. If the tool fails, the runtime logs the startup call error and continues; use a fallback response that does not depend on the tool result when the lookup is not guaranteed. ## Production readiness checklist * Personalize with non-sensitive fields only. * Make startup lookup tools read-only. * Define parameter descriptions for tool inputs. * Provide a generic fallback when profile data is unavailable. * Do not assume channel metadata exists unless the channel integration supplies it. # How to route by role, channel, language, and session state Source: https://koreai.mintlify.app/agent-platform/abl/how-to/route-by-role-channel-language-state [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Enterprise routing often depends on more than intent. A voice caller may need a different path than a web user, a Spanish-speaking user may need a language-specific specialist, and an administrator may need a privileged-support queue. ABL lets a supervisor combine deterministic context paths and semantic conditions in the same `HANDOFF` list to route on channel, locale, role, entitlement, failure count, or session state. Use this when the same user intent must branch by channel, language, role, or current session state. ## Concept A supervisor evaluates `HANDOFF` entries in authored order. Each entry has a `WHEN` condition that can be either: * **Deterministic**: a structured expression referencing session data values, such as `session.channel == "voice"` or `session.interaction.current.language == "es"`. The runtime evaluates these against session state and produces a boolean result without involving the model. * **Semantic**: a natural-language description of when the route should activate, such as `"the user is an administrator and the account is locked"`. The runtime defers these to the model, which decides based on conversation context. You can mix both in the same HANDOFF list. Deterministic conditions are reliable for known, platform-provided values. Semantic conditions are flexible for signals that come from conversation context, identity systems, or policy rules that are not yet in structured session data. When no HANDOFF condition matches, the supervisor retains ownership and may respond directly or re-prompt the user. Always include a catch-all route or ensure the supervisor has a meaningful direct-response capability. ### Key context paths | Path | Source | Type | Example | | - | - | - | - | | `session.channel` | Set at session initialization from the channel type (for example, `web`, `voice`, `web_chat`, `api`) | deterministic | `session.channel == "voice"` | | `session.interaction.current.language` | Resolved from the interaction context (message, session preference, channel, project, or agent default) | deterministic | `session.interaction.current.language == "es"` | | `session.interaction.current.locale` | Resolved alongside language | deterministic | `session.interaction.current.locale == "es-MX"` | | `session.interaction.current.timezone` | Resolved alongside language | deterministic | `session.interaction.current.timezone == "America/Mexico_City"` | | User role, account state, entitlements | Not platform-provided; must be populated via ON\_START SET, GATHER, tool output, caller context, or memory | semantic or deterministic (if populated) | `"the user is an administrator"` or `user_role == "admin"` if gathered | ### History default When a HANDOFF entry omits the `history` property, the child agent receives the **full** parent conversation history. This is the platform default. To limit what the child sees, set `history` explicitly: * `full` (default when omitted): complete parent conversation history * `auto`: uses the handoff summary when available, otherwise the last 5 messages * `summary_only`: only the summary text, no raw messages * `none`: no history * `{ mode: last_n, count: }`: the last N messages ## Minimal working example This supervisor routes by channel, language, role, and a general fallback. Each variable in `pass` must be populated before routing — typically via `ON_START SET`, `GATHER`, a tool call, or caller context from the SDK/channel. ```yaml theme={null} SUPERVISOR: Context_Aware_Routing_Supervisor GOAL: "Route by user role, channel, language, and session state" HANDOFF: - TO: Voice_Escalation_Agent WHEN: session.channel == "voice" EXPECT_RETURN: false PASS: [customer_id, conversation_summary] SUMMARY: "Voice caller routed to voice-specific handling." HISTORY: auto - TO: Spanish_Service_Agent WHEN: session.interaction.current.language == "es" EXPECT_RETURN: false PASS: [customer_id, conversation_summary] SUMMARY: "Customer should be served in Spanish." - TO: Admin_Support_Agent WHEN: "the user is an administrator and the account is locked or requires privileged support" EXPECT_RETURN: false PASS: [customer_id, account_id] SUMMARY: "Admin user needs account lock support." - TO: Standard_Service_Agent WHEN: "the user needs ordinary support and no channel, language, role, or state override applies" EXPECT_RETURN: false PASS: [customer_id, conversation_summary] SUMMARY: "Customer needs standard service." AGENT: Voice_Escalation_Agent GOAL: "Handle voice escalation" AGENT: Spanish_Service_Agent GOAL: "Handle Spanish-language service" AGENT: Admin_Support_Agent GOAL: "Handle administrator support" AGENT: Standard_Service_Agent GOAL: "Handle standard support" ``` **Variable sourcing assumptions:** `customer_id` and `account_id` are populated from caller context or an ON\_START lookup. `conversation_summary` is a platform-provided session variable. These must exist in session data before the HANDOFF evaluates. This example uses the flat CONTEXT shorthand (`PASS:`, `SUMMARY:`, `HISTORY:` as direct siblings of the HANDOFF entry). The nested `CONTEXT:` wrapper form is also supported: ```yaml theme={null} - TO: Voice_Escalation_Agent WHEN: session.channel == "voice" EXPECT_RETURN: false CONTEXT: pass: [customer_id, conversation_summary] summary: "Voice caller routed to voice-specific handling." history: auto ``` Both forms produce identical results. ## How it works 1. The supervisor receives the user message and evaluates HANDOFF entries in authored order. 2. For each entry, the runtime classifies the WHEN condition: * **Structured conditions** (containing operators like `==`, `!=`, `AND`, `OR`, `IS SET`, comparisons) are evaluated deterministically against session data values. * **Natural-language conditions** (free-form text without operators) are deferred to the model, which decides based on conversation context and the condition text. 3. The first matching handoff transfers control to the target agent. The target receives the declared `pass` variables, the `summary`, and conversation history according to the `history` strategy (default: `full`). 4. When `EXPECT_RETURN: false`, the child becomes the new active agent permanently. When `EXPECT_RETURN: true`, the child returns control to the supervisor via `ON_RETURN`. ## Common variations ### Combining deterministic and semantic conditions You can combine a deterministic check with a semantic qualifier in a single WHEN using AND: ```yaml theme={null} - TO: VIP_Voice_Agent WHEN: session.channel == "voice" AND "the user is a VIP customer" EXPECT_RETURN: false PASS: [customer_id, vip_tier] SUMMARY: "VIP voice caller needs premium support." ``` The runtime evaluates the deterministic part first. If it passes, the semantic part is deferred to the model. Both must be true for the route to match. ### Routing on locale instead of language ```yaml theme={null} - TO: LatAm_Agent WHEN: session.interaction.current.locale == "es-MX" EXPECT_RETURN: false PASS: [customer_id] SUMMARY: "Mexican Spanish customer." ``` ### Routing with explicit priority Use `PRIORITY` to override authored-order evaluation. Lower values are evaluated first: ```yaml theme={null} HANDOFF: - TO: Emergency_Agent WHEN: "the user reports an emergency or safety concern" PRIORITY: 1 EXPECT_RETURN: false PASS: [customer_id] SUMMARY: "Emergency escalation." - TO: Standard_Agent WHEN: "the user needs general support" PRIORITY: 10 EXPECT_RETURN: false PASS: [customer_id] SUMMARY: "Standard support." ``` ### Return-expected handoff with ON\_RETURN When a specialist should return control after completing its task: ```yaml theme={null} - TO: Language_Detection_Agent WHEN: "the user's language is unclear from the initial message" EXPECT_RETURN: true PASS: [customer_id] SUMMARY: "Detect preferred language." ON_RETURN: MAP: detected_language: language_result ``` ### Adding ON\_FAILURE for robustness ```yaml theme={null} - TO: Voice_Escalation_Agent WHEN: session.channel == "voice" EXPECT_RETURN: false PASS: [customer_id] SUMMARY: "Voice caller." ON_FAILURE: "I'm unable to connect you to the voice team right now. Let me help you directly." ``` `ON_FAILURE` fires when the target agent cannot be found or the dispatch fails before the target accepts the handoff. ## Verification * Validate the ABL document parses and compiles without errors or warnings. * Send a test message from a voice channel and confirm the `Voice_Escalation_Agent` is selected. Inspect the trace for a `routing_primitive_when` event showing `kind: deterministic_pass` for the `session.channel == "voice"` condition. * Send a test message with the interaction context language set to `es` and confirm the `Spanish_Service_Agent` is selected. * Send a test message as an administrator (with role context populated) and confirm the `Admin_Support_Agent` is selected via model-deferred semantic evaluation. * Send a test message that matches no specific override and confirm the `Standard_Service_Agent` is selected. * Confirm that each target agent receives the expected `pass` variables and conversation history. ## Production readiness checklist * Every variable in `pass` lists has a verified source: gathered field, ON\_START SET, caller context, tool output, or memory. Variables that are not populated before routing will be passed as empty/null. * Deterministic conditions use platform-provided paths (`session.channel`, `session.interaction.current.language`) or variables with known, reliable sources. * Semantic conditions describe clear, unambiguous selection criteria. Vague conditions may cause inconsistent routing. * The `history` default is `full` when omitted. If children should not see full history, set `history` explicitly on each handoff. * A catch-all route or direct-response capability exists for messages that match no specific route. * Each specialist agent is defined and reachable (either inline or as an imported agent reference). * `ON_FAILURE` is set on critical routes where target unavailability must produce a user-facing message rather than silence. * Route distinctness: each deterministic WHEN condition is mutually exclusive or ordered so the first match wins correctly. Overlapping semantic conditions may produce non-deterministic routing. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Using `session.interaction.current.channel` for channel routing | Confusion between interaction context (language/locale/timezone) and session-level channel | Use `session.channel` for channel routing | | Assuming user role is in session data | Role is not a platform-provided field | Populate role via ON\_START lookup, GATHER, tool call, or caller context before routing | | Omitting `history:` without realizing children get full history | The default changed from `auto` to `full` | Set `history` explicitly when children should not see the full conversation | | Mixing unquoted NL text with expression operators | The condition classifier may treat it as a malformed structured expression | Quote NL routing text or ensure it has no `==`, `!=`, `AND`, `OR`, `IS SET` operators | | Creating separate specialists for every channel when behavior is identical | Over-specialization adds maintenance cost | Only create channel-specific agents when the agent behavior genuinely differs | | Not populating PASS variables before routing | Variables passed as null/empty if not sourced | Ensure each variable is set via ON\_START, GATHER, tool, caller context, or memory before the HANDOFF evaluates | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Channel condition never matches | Wrong context path or channel type string | Confirm `session.channel` value in traces; common values are `web`, `voice`, `web_chat`, `api`, `digital` | | Language condition never matches | Interaction context not populated | Check `session.interaction.current.language` in traces; verify the channel or SDK sends language context | | Semantic WHEN routes inconsistently | Model interprets NL condition differently across turns | Make the condition text more specific; consider adding a deterministic guard variable instead | | No route matches and the supervisor goes silent | No catch-all and `direct_response_allowed` is false | Add a fallback HANDOFF with a broad semantic WHEN, or set the supervisor role to `orchestrator` | | Child agent does not have expected context | Variables not populated before handoff | Verify each variable in `pass` is set before routing; check ON\_START or GATHER ordering | | Child agent receives too much history | Default `history` is `full` | Set `history: summary_only` or `history: none` on the handoff entry | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route conversations based on user intent](/agent-platform/abl/how-to/route-by-user-intent). * [How to debug routing decisions in traces](/agent-platform/abl/how-to/debug-routing-traces). # How to route by tool result, profile lookup, or remembered state Source: https://koreai.mintlify.app/agent-platform/abl/how-to/route-by-tool-result-or-memory [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Use this when routing depends on customer profile, case status, fraud risk, policy coverage, entitlement, remembered preference, or any state loaded before the handoff — not just the user's utterance. ## Concept Some routes can't be decided from what the user typed. The supervisor first calls a read-only lookup tool, binds the result with `AS`, then routes with deterministic conditions over that result's fields. This is the pattern for premium routing, existing-case routing, risk routing, entitlement routing, and remembered-preference routing. One behavior is worth knowing before you move past the happy path: **you don't have to copy tool-result fields into separate session variables before routing on them.** You can reference the `AS`-bound object's fields directly in `WHEN`; a flat copy (`SET profile_tier = profile.tier`) is only useful if you also want to pass a shorter, flatter set of fields through `CONTEXT.pass`. ## Minimal working example ```yaml expandable=true theme={null} SUPERVISOR: Tool_Result_Routing_Supervisor GOAL: "Look up customer state before selecting a specialist" TOOLS: lookup_customer_profile(customer_id: string) -> object description: "Return customer tier, risk, and open-case status for routing." side_effects: false confirm: never params: customer_id: description: "Customer identifier to look up." ON_START: CALL: lookup_customer_profile WITH: customer_id: customer_id AS: profile SET: profile_tier = profile.tier has_open_case = profile.has_open_case fraud_risk = profile.fraud_risk HANDOFF: - TO: Fraud_Review_Agent WHEN: profile.fraud_risk == "high" EXPECT_RETURN: false CONTEXT: pass: [customer_id, profile_tier, fraud_risk] summary: "Customer profile indicates high fraud risk." - TO: Existing_Case_Agent WHEN: profile.has_open_case == true EXPECT_RETURN: false CONTEXT: pass: [customer_id, profile_tier, has_open_case] summary: "Customer already has an open case." - TO: Premium_Service_Agent WHEN: profile.tier == "premium" EXPECT_RETURN: false CONTEXT: pass: [customer_id, profile_tier] summary: "Premium customer needs support." - TO: Standard_Service_Agent WHEN: "no fraud, open-case, or premium routing condition is matched" EXPECT_RETURN: false CONTEXT: pass: [customer_id] summary: "Standard customer support route." AGENT: Fraud_Review_Agent GOAL: "Review fraud-risk requests" AGENT: Existing_Case_Agent GOAL: "Continue existing cases" AGENT: Premium_Service_Agent GOAL: "Serve premium customers" AGENT: Standard_Service_Agent GOAL: "Serve standard customers" ``` `customer_id` is referenced as both a tool parameter and a `CONTEXT.pass` field with no declared source in this example — treat it as a project-local assumption (typically populated earlier from authentication or a prior turn) and declare it via `MEMORY` in your actual project. ## How it works * `ON_START` calls the read-only lookup tool once, before any `HANDOFF` is evaluated, and binds the result to `profile` with `AS`. * Each `HANDOFF`'s `WHEN` is evaluated in authored order. Every condition here is a plain equality comparison (`profile.fraud_risk == "high"`, `profile.has_open_case == true`, `profile.tier == "premium"`) — if the field hasn't resolved yet, the comparison simply evaluates `false` rather than erroring, so you don't need to write `profile.fraud_risk IS SET AND profile.fraud_risk == "high"` yourself. * The `SET` step that copies `profile.tier`/`profile.has_open_case`/`profile.fraud_risk` into flat variables is not required for the `WHEN` conditions to work — they reference `profile.*` directly. The copies exist here only so the flatter names can be listed in `CONTEXT.pass`; you can pass `profile` itself (or a subset via a shorthand) instead if you prefer not to duplicate fields. * The fallback route uses a quoted natural-language condition. Keep this kind of fallback text in lowercase, ordinary sentence form — a quoted condition containing an uppercase `AND`/`OR`/`NOT` can be misclassified by the runtime as a structured deterministic expression rather than natural language, which will not evaluate the way you intend. Lowercase words like "or"/"and" inside ordinary sentences (as in this example) are handled correctly. * Since none of the `HANDOFF` entries declare `HISTORY`, each child now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). Set `HISTORY: auto` (or another explicit strategy) if you specifically want bounded/summary history instead. * `lookup_customer_profile` is declared with `side_effects: false` and `confirm: never` because it's a read-only enrichment call used purely to decide a route — it should never require user confirmation and should never be confused with a state-changing action. ## Common variations ### Routing on remembered/persistent state instead of a live lookup Replace the `ON_START CALL ... AS: profile` step with a read from persistent memory (see the memory HowTos for declaring and recalling scoped memory), then route on the recalled fields the same way — the `WHEN` conditions don't change shape. ### Passing the bound object instead of flat copies If you don't need a smaller/renamed field set in `CONTEXT.pass`, skip the flat `SET` copies entirely and pass `profile` (or specific `profile.*` paths) directly. ## Verification * Parse and compile the ABL and confirm there are no parser or compiler errors/warnings. * Test at least one utterance that resolves to each of the four routes, including the fallback path (no fraud, no open case, non-premium tier). * Inspect the trace for the selected target and the evaluated condition — you'll see your literal authored condition text, since HANDOFF conditions are not rewritten by the compiler. * Confirm the fallback route's semantic condition resolves as intended by testing an utterance where all three deterministic conditions are false. ## Production readiness checklist * The lookup tool is declared read-only (`side_effects: false`) and never requires confirmation for a routing-only call. * Every specialist route has a clear owner and a concise context summary. * Deterministic conditions reference fields that are actually present on the tool's declared return shape. * Quoted natural-language fallback text avoids uppercase `AND`/`OR`/`NOT`. * `customer_id` (or any other assumed pre-existing variable) has a real declared source in your project, not just an assumption carried over from this example. * Fallback behavior is explicit and does not hide missing routing coverage. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Calling a side-effecting tool just to decide a route | Reusing an existing action tool instead of declaring a dedicated read-only lookup | Declare a separate tool with `side_effects: false` and `confirm: never` for routing-only enrichment | | Assuming a flat `SET` copy is required before routing on a tool result | Copying feels like the "safe" pattern | Reference the `AS`-bound object's fields directly in `WHEN`; only copy when you need a flatter `CONTEXT.pass` shape | | Writing uppercase `AND`/`OR`/`NOT` inside a quoted natural-language fallback `WHEN` | It reads naturally to a human author | Keep quoted fallback conditions in ordinary lowercase sentence form | | Passing the entire profile object when the child only needs tier, risk, or case state | Convenience | Pass only the specific fields each specialist needs | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | The fallback route fires even when a deterministic condition should have matched | The tool result field wasn't populated by the time `HANDOFF` conditions were evaluated | Confirm `ON_START CALL ... AS` completed before `HANDOFF` evaluation; check the tool's actual return shape | | A quoted fallback condition behaves unpredictably | Uppercase `AND`/`OR`/`NOT` inside the quoted text was misclassified as a structured expression | Rewrite the fallback text in ordinary lowercase sentence form | | A child agent has more/less conversation context than expected | `HISTORY` was omitted and the current platform default (`full`) applied | Set an explicit `HISTORY` strategy if you need bounded/summary history | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route conversations based on user intent](/agent-platform/abl/how-to/route-by-user-intent). # How to route users by detected intent Source: https://koreai.mintlify.app/agent-platform/abl/how-to/route-by-user-intent [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Use this pattern when a supervisor agent classifies what the user is trying to do, then hands the conversation to the specialist that owns that work. ## Concept Intent routing uses three ABL surfaces working together: * **INTENTS** declares the classifier categories. Each category has a description that tells the model what kinds of requests belong to it. * **HANDOFF** is the executable routing surface. Each entry names a target agent, the condition that triggers the handoff, and the context the child receives. * **WHEN** conditions on each handoff entry can be deterministic expressions (`intent.category == "billing"`) or semantic natural-language text (`"the user request is unclear"`). The platform detects the form automatically: multi-word quoted text without structured operators routes through model reasoning; expressions with operators like `==` route through deterministic evaluation. Two decisions shape every intent-routing supervisor: 1. **History strategy** — By default, the child agent receives the full parent conversation history (`history: full`). This is the platform default when `history` is omitted. Choose `summary_only`, `none`, `auto`, or `last_n` when the child should see less. See [Common variations](#common-variations) for the options. 2. **Return behavior** — `EXPECT_RETURN: false` (the default) transfers ownership permanently. `EXPECT_RETURN: true` pauses the parent and resumes it when the child returns. Use `ON_RETURN` to map child results back to parent variables. ## Minimal working example ```yaml expandable=true theme={null} SUPERVISOR: Intent_Routing_Supervisor GOAL: "Route customer requests to the specialist that owns the detected intent" MEMORY: customer_id: type: string source: session account_id: type: string source: session INTENTS: billing: "Questions about invoices, payment status, unpaid balances, or payment methods." technical_support: "Problems with product access, errors, setup, or troubleshooting." cancellation: "Requests to cancel, downgrade, or stop renewal." HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" CONTEXT: pass: [customer_id, account_id] summary: "Customer needs billing help." - TO: Technical_Support_Agent WHEN: intent.category == "technical_support" CONTEXT: pass: [customer_id] summary: "Customer needs technical support." - TO: Retention_Agent WHEN: intent.category == "cancellation" CONTEXT: pass: [customer_id, account_id] summary: "Customer is asking to cancel or downgrade." - TO: General_Service_Agent WHEN: "the user request is unclear or does not match billing, technical support, or cancellation" CONTEXT: pass: [customer_id] summary: "Customer needs general service triage." AGENT: Billing_Agent GOAL: "Resolve billing requests" AGENT: Technical_Support_Agent GOAL: "Resolve technical support requests" AGENT: Retention_Agent GOAL: "Handle cancellation and downgrade conversations" AGENT: General_Service_Agent GOAL: "Clarify and route unsupported requests" ``` The `MEMORY` section declares `customer_id` and `account_id` as session variables so the `pass` fields have a defined source. Any variable you pass must come from MEMORY, GATHER, a tool result, or runtime context. ## How it works 1. The supervisor receives the user message and retains ownership until a handoff condition matches. 2. The platform evaluates the INTENTS classifier against the user message, producing `intent.category`. 3. HANDOFF entries are evaluated in authored order (override with `PRIORITY` when needed). The first matching condition wins. 4. The matched child receives: the `pass` variables, the `summary` text, and conversation history controlled by the `history` strategy. When `history` is omitted, the child receives the **full parent conversation** — this is the platform default (`full`). 5. If `EXPECT_RETURN: false` (the default), the child owns the conversation permanently. If `EXPECT_RETURN: true`, the child runs and returns control to the parent, optionally mapping result variables through `ON_RETURN`. 6. If no HANDOFF condition matches, the supervisor continues with its own GOAL. A semantic fallback entry (like the `General_Service_Agent` above) prevents unmatched requests from going unhandled. ## Common variations ### Control conversation history with `history` When the child should not receive the full parent transcript, set `history` explicitly: ```yaml theme={null} HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" CONTEXT: pass: [customer_id, account_id] summary: "Customer needs billing help." history: summary_only ``` | Value | Behavior | | - | - | | `full` | Platform default. The complete parent conversation history is passed. | | `auto` | Uses the summary when available; falls back to the last 5 messages. | | `none` | No conversation history. The child starts fresh with only `pass` variables and `summary`. | | `summary_only` | Only the `summary` text is passed — no raw messages. | | `{ mode: last_n, count: N }` | The last N messages from the parent are passed. | Choose `summary_only` or `none` when the child is a focused specialist that should not be influenced by earlier conversation turns. Choose `full` (or omit) when the child needs the complete conversation record. ### Shorthand context syntax `pass`, `summary`, and `history` can appear as direct siblings of the handoff entry instead of nested under `CONTEXT:`: ```yaml theme={null} HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" pass: [customer_id, account_id] summary: "Customer needs billing help." history: summary_only ``` Both forms compile identically. The nested `CONTEXT:` form is preferred when you also need `set` or `memory_grants`. ### Return to the parent with ON\_RETURN When a specialist should report back to the supervisor after completing its task: ```yaml expandable=true theme={null} HANDOFF: - TO: Verification_Agent WHEN: intent.category == "verification_needed" EXPECT_RETURN: true CONTEXT: pass: [customer_id] summary: "Customer needs identity verification." history: summary_only ON_RETURN: action: continue map: verified: verification_result ``` `EXPECT_RETURN: true` pauses the parent. When the child returns, `ON_RETURN.map` copies child variables into the parent scope. `action: continue` resumes the parent's flow; `action: resume_intent` re-runs routing with the original user intent and the returned context. The legacy keyword `RETURN` is accepted as an alias for `EXPECT_RETURN`. ### Set evaluation order with PRIORITY When multiple handoff conditions could match the same utterance, use `PRIORITY` to control evaluation order. Lower values are evaluated first: ```yaml expandable=true theme={null} HANDOFF: - TO: Urgent_Support_Agent WHEN: intent.category == "technical_support" and priority_level == "urgent" PRIORITY: 0 CONTEXT: pass: [customer_id] summary: "Urgent technical support request." - TO: Technical_Support_Agent WHEN: intent.category == "technical_support" PRIORITY: 1 CONTEXT: pass: [customer_id] summary: "Technical support request." ``` Without `PRIORITY`, entries are evaluated in authored order. ### Handle dispatch failures with ON\_FAILURE `ON_FAILURE` defines what happens when the handoff dispatch itself fails — for example, the target agent is not found or pre-transfer validation fails. It does not handle child-agent runtime errors. ```yaml theme={null} HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" CONTEXT: pass: [customer_id, account_id] summary: "Customer needs billing help." ON_FAILURE: ESCALATE ``` | Value | Behavior | | - | - | | `CONTINUE` | Silently continue in the current agent. | | `ESCALATE` | Trigger human escalation. | | `RESPOND "message"` | Send a message and continue in the current agent. | ## Verification * Send a test utterance for each intent category and confirm the correct child agent is selected. Check the trace for the matched HANDOFF entry and the condition result. * Send an ambiguous utterance and confirm the fallback entry handles it. Verify the trace shows the semantic WHEN evaluation. * Inspect the trace for passed context: confirm `pass` variables are present in the child's session and the `summary` appears in the child's context. * If `EXPECT_RETURN: true` is used, verify the parent resumes and `ON_RETURN.map` variables appear in the parent's session after the child returns. * Verify the `history` strategy by checking what conversation messages the child receives. With `full` (or omitted), the child should see the complete parent transcript. With `summary_only`, the child should see only the summary text. ## Production readiness checklist * Every specialist route has a clear owner and a concise context summary. * Every `pass` variable has a declared source (MEMORY, GATHER, tool result, or runtime context). * Deterministic conditions use declared fields; semantic conditions are quoted as natural-language `WHEN` text. * Choose a `history` strategy appropriate for each child. The default `full` passes the entire parent transcript, which may include irrelevant turns and increase token usage. * Add `ON_FAILURE` to critical handoff entries so dispatch failures have a defined recovery path. * Fallback behavior is explicit and does not hide missing intent coverage. * Test at least one utterance per intent category, one ambiguous utterance, and one utterance outside all categories. * Temporary child agents (`EXPECT_RETURN: true`) produce every field mapped in `ON_RETURN`. * Verify handoff depth does not exceed the platform limit of 10 nested handoffs. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Authoring a `ROUTING:` section | Confusion between conceptual routing and the ABL surface | The parser rejects `ROUTING:` and points to `HANDOFF:`. Use `HANDOFF:` for all routing entries. | | Leaving semantic prose unquoted | The WHEN condition looks like an expression with undefined variables | Quote multi-word natural-language conditions so the platform routes them through model reasoning instead of expression evaluation. | | Assuming the child starts with a blank slate | The default history strategy is `full`, not `none` | Set `history: none` or `history: summary_only` explicitly when you want the child to start fresh. | | Passing variables without a declared source | The variables compile but have no value at runtime | Declare every `pass` variable in MEMORY, GATHER, or as a tool/runtime output before referencing it. | | Routing every unmatched request to the nearest specialist | No explicit fallback; the closest label absorbs unrelated requests | Add a dedicated fallback agent with a semantic WHEN condition for unclear or unmatched requests. | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Child agent sees the full conversation even though only `pass` variables were declared | `history` defaults to `full` when omitted | Set `history: summary_only` or `history: none` in the CONTEXT block. | | Handoff never fires for a valid utterance | WHEN condition uses an undefined variable or has a syntax error | Check that the WHEN expression references a valid field (for example, `intent.category`) and the INTENTS section defines the expected category. | | Compiler warning about ungrounded pass fields | `pass` variables are not declared in MEMORY, GATHER, or a tool output | Declare each variable source or mark it as a project-local runtime value. | | Fallback agent handles requests that should match a specific intent | Intent categories overlap or the classifier description is ambiguous | Refine INTENTS descriptions to be mutually exclusive; use `PRIORITY` to control evaluation order for overlapping conditions. | | Parent does not resume after child completes | `EXPECT_RETURN` is set to `false` or omitted (default) | Set `EXPECT_RETURN: true` on the handoff entry. Confirm the child agent completes and returns control. | | `ON_RETURN.map` variables are missing in parent scope | Child agent does not produce the expected output variables | Verify the child agent sets the mapped variables before returning. | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). # How to route unclear or unsupported requests to a fallback agent Source: https://koreai.mintlify.app/agent-platform/abl/how-to/route-to-fallback-agent [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) A fallback agent is not a dumping ground; it is a specialist for clarification, unsupported requests, conflicting intents, and safe recovery. The fallback can either complete the conversation or return a clarified signal so the supervisor can resume routing. Use this when the supervisor cannot confidently choose a specialist or when the user asks for something outside the supported intent catalog. ## Concept Keep clear deterministic or intent routes first, and put a quoted semantic fallback condition last for vague, unsupported, or conflicting requests. When the fallback should re-enter normal routing after clarifying, use `EXPECT_RETURN: true` with `ON_RETURN: { action: resume_intent, MAP: {...} }` — the fallback child gathers whatever's missing, and `MAP` copies its result back into a parent-owned variable your supervisor can route on next turn. One thing affects how this looks once you look past the syntax: **`HANDOFF` history defaults to `full` when omitted.** Every route below omits `HISTORY`, so each child (including the fallback) now receives the entire conversation by default. ## Minimal working example ```yaml expandable=true theme={null} SUPERVISOR: Fallback_Routing_Supervisor GOAL: "Send clear requests to specialists and unclear requests to a fallback agent" INTENTS: billing: "Billing and payment requests." technical_support: "Product troubleshooting requests." HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" EXPECT_RETURN: false CONTEXT: pass: [customer_id, account_id] summary: "Customer needs billing help." - TO: Technical_Support_Agent WHEN: intent.category == "technical_support" EXPECT_RETURN: false CONTEXT: pass: [customer_id, issue_summary] summary: "Customer needs technical support." - TO: Fallback_Triage_Agent WHEN: "the request is vague, unsupported, conflicting, or missing enough information to choose a specialist" EXPECT_RETURN: true CONTEXT: pass: [customer_id, conversation_summary] summary: "Clarify the customer's request and return if a specialist can be chosen." ON_RETURN: action: resume_intent MAP: clarified_intent: clarified_intent AGENT: Billing_Agent GOAL: "Resolve billing requests" AGENT: Technical_Support_Agent GOAL: "Resolve technical support requests" AGENT: Fallback_Triage_Agent GOAL: "Clarify unsupported or ambiguous requests" MEMORY: SESSION: - clarified_intent GATHER: clarified_intent: TYPE: string PROMPT: "Which area should handle this request?" REQUIRED: true COMPLETE: - WHEN: clarified_intent IS SET RESPOND: "Thanks, I captured the clarified route." ``` `customer_id`, `account_id`, `issue_summary`, and `conversation_summary` are shown as passed context with no declared source in this example — treat them as project-local assumptions (typically populated from authentication or an earlier turn) and declare them via `MEMORY` in your actual project. ## How it works * The fallback route's `WHEN` is a quoted natural-language condition, evaluated only when neither deterministic route above it matched. * `EXPECT_RETURN: true` makes `Fallback_Triage_Agent` a temporary child: it gathers `clarified_intent` from the user, then its `COMPLETE` fires once that field `IS SET`. * `ON_RETURN.action: resume_intent` tells the supervisor to resume routing after the child returns. `MAP: { clarified_intent: clarified_intent }` copies the child's gathered value (left side, child key) into the parent supervisor's own `clarified_intent` session variable (right side, parent key) — declared here in the supervisor's own `MEMORY.SESSION`. * If you don't need to map any fields back — the fallback child only needs to signal "done, try routing again" — you can use the simpler shorthand `ON_RETURN: "resume_intent"` instead of the structured `action`/`MAP` block. * Since `HISTORY` is omitted from every `HANDOFF`, each child (including the fallback) receives the full conversation history by default. ## Common variations * Clarify and return to the supervisor (shown above). * Complete unsupported requests directly in the fallback agent with a safe explanation, without ever returning to the supervisor (`EXPECT_RETURN: false`, or omit `ON_RETURN` and let the fallback's own `COMPLETE` end the conversation). * Escalate to a human after repeated clarification failures instead of looping the fallback indefinitely. * Use the plain string shorthand `ON_RETURN: "resume_intent"` when the fallback child doesn't need to map any fields back. ## Verification * Parse and compile the ABL and confirm there are no parser or compiler errors/warnings. * Test at least one matching utterance for each specialist route and one clearly ambiguous utterance that should hit the fallback. * Confirm the fallback's `COMPLETE` fires once `clarified_intent IS SET`, and that the parent's `clarified_intent` reflects the child's gathered value after return. * Inspect the trace for the selected target, the evaluated condition (matches your literal authored text for the deterministic routes), and the `ON_RETURN`/`MAP` application on return. ## Production readiness checklist * Every specialist route has a clear owner and a concise context summary. * The fallback's `MAP` targets a variable the parent actually declares and routes on next turn. * Semantic conditions are quoted as natural-language `WHEN` text. * Fallback behavior is explicit and does not hide missing intent coverage — use trace review to find requests that keep landing in fallback and add real routes for them. * Any variable assumed to already exist (like `customer_id` above) has a real declared source in your project. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Using literal `true` as the only route | Looks like a simple catch-all | Only do this intentionally, and document that it's a deliberate catch-all, not a missing condition | | Mapping a clarified field the fallback child never produces | `MAP` target doesn't match anything the child's `GATHER`/`COMPLETE` actually sets | Confirm the child key in `MAP` matches a field the child truly returns | | Letting fallback silently absorb missing intent coverage | Fallback "just works" so gaps go unnoticed | Review fallback-route traces periodically and add real specialist routes for recurring patterns | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | The parent's `clarified_intent` is never updated after the fallback returns | `MAP` key mismatch, or the child never actually sets the mapped field | Confirm child-side field name in `MAP` matches what `GATHER`/`COMPLETE` produces | | Routing doesn't resume after the fallback returns | `ON_RETURN.action` isn't `resume_intent`, or `EXPECT_RETURN` was `false` | Confirm `EXPECT_RETURN: true` and `ACTION: resume_intent` are both set | | A specialist route unexpectedly falls through to the fallback | The referenced field wasn't populated yet, or the condition evaluated differently than expected | Inspect the trace's evaluated condition and `evaluatedContext` | | Child agent has more/less conversation context than expected | `HISTORY` omitted; current default (`full`) applied | Set an explicit `HISTORY` strategy if bounded/summary history is intended | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route conversations based on user intent](/agent-platform/abl/how-to/route-by-user-intent). # How to route users from the welcome experience to the right agent Source: https://koreai.mintlify.app/agent-platform/abl/how-to/route-users-from-welcome [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when the first screen or first assistant turn should guide the user into the right specialist path. ## Concept Welcome routing turns the first interaction into a routing signal. The signal can come from a button, an entry-page value copied into session context, or the user's first typed response. Once the signal is known, normal handoff routing should move the conversation to a specialist agent. Do not reference a specialist agent unless it exists in the project. A routing example is only operational when the target agents are defined and the value used in `WHEN` is actually collected or set. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Welcome_Router_Agent GOAL: "Route users from the welcome experience to the right specialist" GATHER: startup_choice: PROMPT: "Which option did you choose?" TYPE: string REQUIRED: true HANDOFF: - TO: Billing_Agent WHEN: startup_choice == "billing" PASS: [account_id, startup_choice] SUMMARY: "User selected billing from the welcome experience" - TO: Returns_Agent WHEN: startup_choice == "returns" PASS: [account_id, startup_choice] SUMMARY: "User selected returns from the welcome experience" ON_START: RESPOND: "Welcome. Choose billing or returns." ACTIONS: - BUTTON: "Billing" -> billing VALUE: "billing" - BUTTON: "Returns" -> returns VALUE: "returns" FLOW: entry_point: capture_choice steps: - capture_choice capture_choice: REASONING: false GATHER: startup_choice: PROMPT: "Which option did you choose?" TYPE: string REQUIRED: true THEN: COMPLETE ``` ```yaml theme={null} AGENT: Billing_Agent GOAL: "Handle billing questions" FLOW: entry_point: start steps: - start start: REASONING: false RESPOND: "I can help with billing." THEN: COMPLETE ``` ```yaml theme={null} AGENT: Returns_Agent GOAL: "Handle returns questions" FLOW: entry_point: start steps: - start start: REASONING: false RESPOND: "I can help with returns." THEN: COMPLETE ``` ## How it works The welcome message presents choices. The flow gathers `startup_choice`. The handoff conditions use that same field. The specialist agents are included so the compiler and deployment can resolve the targets. `startup_choice` is declared both at the top level and inside `capture_choice`. Keep only the flow-step `GATHER` if the field is only ever collected inside that step; a top-level declaration is only needed if `HANDOFF` conditions must reference the field before the flow step that collects it has run in a given turn. When in doubt, declare the field once, in the flow step that actually collects it. Since neither `HANDOFF` example above declares `HISTORY`, each specialist now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). ## Common variations ### Route from a known entry topic If the channel integration already knows the entry topic, copy it into a session variable during startup and route with that value. ```yaml expandable=true theme={null} AGENT: Welcome_Context_Router GOAL: "Route from startup context when the channel passes an entry intent" HANDOFF: - TO: Claims_Agent WHEN: entry_topic == "claims" PASS: [member_id, entry_topic] SUMMARY: "User entered from a claims landing page" - TO: General_Service_Agent WHEN: entry_topic == "general" PASS: [member_id, entry_topic] SUMMARY: "User needs general service" ON_START: SET: entry_topic = "claims" RESPOND: "Welcome. I will get you to the right team." FLOW: entry_point: continue steps: - continue continue: REASONING: false RESPOND: "Tell me what you need help with." THEN: COMPLETE ``` ```yaml theme={null} AGENT: Claims_Agent GOAL: "Handle claims questions" FLOW: entry_point: start steps: - start start: REASONING: false RESPOND: "I can help with claims." THEN: COMPLETE ``` ```yaml theme={null} AGENT: General_Service_Agent GOAL: "Handle general service questions" FLOW: entry_point: start steps: - start start: REASONING: false RESPOND: "I can help with general service." THEN: COMPLETE ``` ## Verification Test each startup choice and confirm the expected handoff target. In traces, inspect the startup response, the collected or set routing value, and the handoff event. Also test an unknown choice and confirm the user is asked to clarify instead of being routed incorrectly. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Referencing a route variable that is never set | Button labels and route conditions drift apart. | Gather or set the exact value used in `WHEN`. | | Showing buttons without a typed fallback | Some users type instead of clicking. | Let the flow gather the same route value from text. | | Omitting target agents from the project | The supervisor example looks complete by itself. | Define every `TO` agent or state the project-local dependency. | ## Troubleshooting If no route matches, verify that the variable in `WHEN` has the same name as the gathered or set value. If the compiler reports missing targets, define or import the specialist agents before validating the project. ## Production readiness checklist * Define every target specialist agent. * Make every routing variable traceable to `GATHER`, `SET`, channel context, or tool output. * Add a fallback for unknown or ambiguous choices. * Pass only the fields the specialist needs. * Test each welcome option before publishing. # How to design supervisor routing with priority gates Source: https://koreai.mintlify.app/agent-platform/abl/how-to/route-with-supervisor-priority [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Use this pattern when your supervisor must enforce mandatory checks — authentication, fraud screening, regulatory consent, or VIP escalation — before the reasoning model selects a business route. ## Concept A supervisor with priority gates combines two routing modes in a single HANDOFF block: * **Deterministic gates** use structured expressions (field comparisons, IS SET checks) and are evaluated first. They enforce hard requirements such as "the user must be authenticated before any account operation." * **Semantic routes** use quoted natural-language conditions or intent-category comparisons and are evaluated after the gates pass. The reasoning model chooses the best specialist based on the user's intent. The compiler assigns each HANDOFF entry an evaluation priority from its position in the authored list (first entry = highest priority). The runtime sorts entries by priority and evaluates them top to bottom, selecting the first match. Placing mandatory gates at the top of the HANDOFF block guarantees they are checked before any semantic route. When a gate hands off with `EXPECT_RETURN: true`, the child agent runs and then returns control. The supervisor can then reprocess the original user intent (`action: resume_intent`) or continue where it left off (`action: continue`). ### History default When you omit `HISTORY:` on a handoff, the platform default is `full` — the child agent receives the entire parent conversation history. This is important to understand because: * For temporary gate agents (authentication, compliance), you may want `HISTORY: auto` (summary-first, falling back to the last 5 messages) or `HISTORY: none` to avoid passing unnecessary context. * For permanent ownership transfers (billing specialist), `full` is usually appropriate so the specialist has complete conversation context. The supported history strategies are: `full` (default), `auto`, `none`, `summary_only`, and `{ mode: last_n, count: }`. ## Minimal working example ```yaml theme={null} SUPERVISOR: Priority_Routing_Supervisor GOAL: "Apply mandatory gates before semantic routing" INTENTS: account_help: "Account access, profile, or settings requests." billing: "Billing, invoice, or payment requests." support: "General product support requests." MEMORY: SESSION: - is_authenticated - customer_id - customer_tier - account_id - conversation_summary HANDOFF: # Gate 1: Authentication (highest priority, deterministic) - TO: Authentication_Agent WHEN: is_authenticated != true EXPECT_RETURN: true CONTEXT: pass: [customer_id] summary: "Authenticate the customer before handling account or billing data." history: auto ON_RETURN: action: resume_intent MAP: is_authenticated: is_authenticated # Gate 2: VIP escalation (semantic, checked before ordinary routes) - TO: Priority_Care_Agent WHEN: "the customer is platinum or strategic and asks for escalation" EXPECT_RETURN: false CONTEXT: pass: [customer_id, customer_tier, conversation_summary] summary: "High-priority customer asked for escalation." # Business routes (intent-based) - TO: Billing_Agent WHEN: intent.category == "billing" EXPECT_RETURN: false CONTEXT: pass: [customer_id, account_id] summary: "Customer needs billing help." ON_FAILURE: RESPOND "I was unable to connect you with billing. Let me try again." - TO: Account_Agent WHEN: intent.category == "account_help" EXPECT_RETURN: false CONTEXT: pass: [customer_id] summary: "Customer needs account help." - TO: Support_Agent WHEN: intent.category == "support" EXPECT_RETURN: false CONTEXT: pass: [customer_id, conversation_summary] summary: "Customer needs product support." # Fallback (lowest priority, catches unmatched messages) - TO: Support_Agent WHEN: true EXPECT_RETURN: false CONTEXT: pass: [customer_id] summary: "No specific route matched. General support fallback." AGENT: Authentication_Agent GOAL: "Authenticate customers" GATHER: is_authenticated: TYPE: boolean PROMPT: "Is the customer authenticated?" REQUIRED: true COMPLETE: - WHEN: is_authenticated IS SET RESPOND: "Authentication result captured." AGENT: Priority_Care_Agent GOAL: "Handle priority escalations" AGENT: Billing_Agent GOAL: "Resolve billing requests" AGENT: Account_Agent GOAL: "Resolve account requests" AGENT: Support_Agent GOAL: "Resolve support requests" ``` ## How it works 1. The supervisor receives the user message and keeps ownership until a route matches. 2. Intent categories, runtime context, gathered values, or tool results provide the routing evidence. 3. HANDOFF entries are evaluated in authored order (first = highest priority). The compiler assigns `priority: 1, 2, 3, ...` based on position, and the runtime sorts by priority (lower = first). 4. The first matching WHEN condition wins. Deterministic gates at the top of the list are always checked before semantic or intent-based routes below them. 5. When a handoff fires, the child agent receives the PASS fields, the summary, and conversation history according to the HISTORY strategy. The default strategy is `full` (entire parent history). Use `HISTORY: auto` or `HISTORY: none` when you want the gate agent to receive less context. 6. If the child is temporary (`EXPECT_RETURN: true`), it returns through `ON_RETURN`. The supervisor then either resumes intent routing (`action: resume_intent`) or continues its current flow (`action: continue`). 7. If the child owns the conversation (`EXPECT_RETURN: false`), it remains the active agent permanently. ## Common variations ### Authentication gate before account or billing specialists Use `EXPECT_RETURN: true` with `ON_RETURN: action: resume_intent` so the supervisor reprocesses the original user intent after authentication completes. Set `HISTORY: auto` to give the auth agent only a summary instead of full conversation history. ```yaml theme={null} HANDOFF: - TO: Auth_Gate WHEN: is_authenticated != true EXPECT_RETURN: true CONTEXT: pass: [session_token] summary: "Verify user identity before proceeding." history: auto ON_RETURN: action: resume_intent MAP: is_authenticated: auth_result ``` ### VIP escalation route before ordinary queue routing Place the VIP check after mandatory gates but before standard business routes. Use a quoted natural-language WHEN condition when the routing evidence comes from business context rather than a single ABL field. ```yaml theme={null} HANDOFF: - TO: VIP_Concierge WHEN: "the customer has a platinum or enterprise tier and is requesting priority service" EXPECT_RETURN: false CONTEXT: pass: [customer_id, customer_tier, account_value] summary: "VIP customer requesting priority service." ``` ### Compliance gate with explicit PRIORITY field When you need to override the default authored-order priority, use the `PRIORITY:` field. Lower values are evaluated first. ```yaml theme={null} HANDOFF: - TO: Compliance_Check WHEN: transaction_amount > 10000 PRIORITY: 0 EXPECT_RETURN: true CONTEXT: pass: [transaction_id, transaction_amount, customer_id] summary: "High-value transaction requires compliance review." history: none ON_RETURN: action: continue MAP: compliance_cleared: review_result - TO: Payment_Agent WHEN: intent.category == "payment" PRIORITY: 10 EXPECT_RETURN: false CONTEXT: pass: [customer_id, transaction_id] summary: "Process payment request." ``` ### CONTEXT shorthand PASS, SUMMARY, and HISTORY can also appear as direct siblings of the handoff entry instead of nested under CONTEXT. Both forms are equivalent. ```yaml theme={null} HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" PASS: [customer_id, account_id] SUMMARY: "Customer needs billing help." HISTORY: full EXPECT_RETURN: false ``` ## Verification * Parse the ABL and confirm there are no parser errors or warnings. * Compile the ABL and confirm there are no compiler errors. Review any warnings about undeclared field references — these indicate variables that must be supplied at runtime from context, tool results, or project configuration. * Test each routing path with a representative utterance: * For the authentication gate: send any message when `is_authenticated` is not set. Expect the supervisor to route to Authentication\_Agent. * For the VIP route: send "I need to speak with someone about my enterprise account" when the customer tier is platinum. Expect Priority\_Care\_Agent. * For intent routes: send "I have a billing question" and confirm the Billing\_Agent is selected. * For the fallback: send a message that does not match any defined intent. Confirm Support\_Agent is selected as the fallback. * After the auth gate returns, send a billing-related message and confirm the supervisor re-routes to Billing\_Agent (validating `resume_intent` behavior). * Inspect routing traces for: selected target, condition result, passed context fields, history strategy applied, and return behavior when `EXPECT_RETURN: true` is used. ## Production readiness checklist * Every specialist route has a clear owner and a concise context summary. * All variables referenced in PASS fields are declared in MEMORY:SESSION or are explicitly documented as project-local assumptions that will be populated at runtime. * Deterministic conditions use declared, gathered, tool-result, runtime, or returned fields. * Semantic conditions are quoted as natural-language WHEN text. * Temporary child agents produce every field mapped in ON\_RETURN. * A fallback route with `WHEN: true` catches unmatched messages explicitly. * ON\_FAILURE is set on critical routes to handle dispatch failures gracefully. * The HISTORY strategy is chosen deliberately for each handoff. Use `full` for permanent transfers, `auto` or `none` for temporary gates that should not receive full conversation context. * Fallback behavior is explicit and does not hide missing intent coverage. * Routes are tested with representative utterances for each path, including the fallback and the post-gate resume path. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Omitting HISTORY on a gate handoff and leaking full conversation context to a temporary child agent | The default HISTORY strategy is `full`, which passes the entire parent conversation to the child | Set `HISTORY: auto` or `HISTORY: none` on temporary gate agents that do not need the full conversation | | Making the specialist responsible for checking a mandatory parent gate | It seems simpler to let each child check auth, but this duplicates logic and risks inconsistency | Keep mandatory gates in the supervisor HANDOFF block, above all specialist routes | | Mapping returned fields that the child agent does not produce | ON\_RETURN MAP references a field name that is not gathered, set, or returned by the child | Ensure the child agent declares or produces every field referenced in ON\_RETURN MAP | | Using an unquoted expression for externally supplied values | Unquoted conditions are parsed as structured expressions and may produce undefined-variable warnings | Use a quoted natural-language condition when the routing evidence is external business context, or declare the variables in MEMORY:SESSION | | No fallback route for unmatched messages | All WHEN conditions are specific, so an unmatched message has no explicit path | Add a `WHEN: true` fallback as the last HANDOFF entry | | PASS fields not declared in MEMORY:SESSION | The compiler resolves PASS fields from session memory for type/description metadata; undeclared fields fall back to type=string | Declare all PASS variables in MEMORY:SESSION for clear typing and documentation | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Gate agent fires on every message even after authentication succeeds | ON\_RETURN MAP does not write back the gate result to the parent session variable | Verify that the MAP key matches the child's output field and the parent's session variable name | | Semantic route is selected before the deterministic gate | The semantic route appears above the gate in the HANDOFF block (lower authored position = higher priority) | Move mandatory gates to the top of the HANDOFF block | | Child agent responds without conversation context | HISTORY is set to `none` or `summary_only`, or the summary is empty when using `auto` | Check the HISTORY strategy on the handoff entry; use `full` or provide a non-empty summary for `auto` | | Compiler warning about undeclared field reference in WHEN condition | The variable in the WHEN expression is not in GATHER, MEMORY:SESSION, or built-in variables | Declare the variable in MEMORY:SESSION, or verify it will be supplied at runtime from context or tool results | | resume\_intent does not re-route after gate return | The ON\_RETURN action is set to `continue` instead of `resume_intent`, or the MAP does not update the gate variable | Set `action: resume_intent` in ON\_RETURN and verify the MAP updates the condition variable (for example, `is_authenticated`) | | Unmatched messages produce no response | No fallback route exists in the HANDOFF block | Add a `WHEN: true` fallback entry as the last HANDOFF entry | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route conversations based on user intent](/agent-platform/abl/how-to/route-by-user-intent). * [How to route unclear requests to a fallback agent](/agent-platform/abl/how-to/route-to-fallback-agent). # How to run an authentication or profile lookup during welcome Source: https://koreai.mintlify.app/agent-platform/abl/how-to/run-auth-profile-lookup-on-start [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when the first turn should use authenticated context, such as account status or display name. ## Concept Authentication and profile lookup are different responsibilities. Authentication establishes who the user is and what context the session can trust. A profile lookup reads business data for that identity. `ON_START` can call a read-only profile tool after the session already has the identity value needed by that tool. Do not use a welcome message to reveal sensitive information before identity is verified. If the identity is missing, greet generically and ask the user to sign in before account-specific actions. ## Minimal working example ```yaml expandable=true theme={null} AGENT: Auth_Profile_Lookup_Start_Agent GOAL: "Load account context during startup before asking the first question" TOOLS: get_account_profile(accountId: string) -> {content: string, account_status: string, display_name: string} description: "Read the account profile for the authenticated user" side_effects: false confirm: never params: accountId: description: "Authenticated account identifier supplied by the channel session" ON_START: CALL: get_account_profile WITH: accountId: session.account_id AS: accountProfile RESPOND: "Hello {{accountProfile.display_name}}. Your account is {{accountProfile.account_status}}." FLOW: entry_point: service_menu steps: - service_menu service_menu: REASONING: false RESPOND: "Would you like billing help, service help, or account help?" THEN: COMPLETE ``` ## How it works `session.account_id` must already exist in runtime session context. `ON_START` calls the tool, binds the result as `accountProfile`, and then uses the profile fields in the welcome response. ## Common variations ### Guard the welcome when identity is missing ```yaml expandable=true theme={null} AGENT: Auth_Gated_Welcome_Agent GOAL: "Welcome authenticated users and avoid exposing account data before identity is present" ON_START: BRANCHES: - IF: session.account_id IS SET RESPOND: "Welcome. I found your account context." - ELSE: RESPOND: "Welcome. Please sign in before account-specific actions." FLOW: entry_point: next steps: - next next: REASONING: false RESPOND: "How can I help?" THEN: COMPLETE ``` ## Verification Test two sessions: one with `session.account_id`, and one without it. The authenticated session should call the profile tool and render the account-aware welcome. The unauthenticated session should not expose account-specific data. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Using `ON_START` as the authentication step | Profile lookup and authentication are easy to conflate. | Authenticate before account-specific startup copy, then use `ON_START` to read safe profile context. | | Passing an empty account id to the lookup tool | The channel session did not supply identity. | Add a missing-identity branch and test anonymous sessions. | | Returning sensitive account details in the greeting | The first turn feels like a convenient place for account state. | Keep the welcome high-level and move sensitive details behind verified flows. | ## Troubleshooting If the tool input is empty, inspect how the channel or authentication layer populates the session identity. If startup lookup adds latency, keep the welcome generic and move expensive profile calls into the first account-specific flow. ## Production readiness checklist * Require verified identity before account-specific responses. * Make profile lookup tools read-only. * Keep sensitive account details out of the welcome. * Include a fallback for anonymous or partially authenticated users. * Trace startup tool calls and profile lookup failures. # How to switch the active agent during a conversation Source: https://koreai.mintlify.app/agent-platform/abl/how-to/switch-active-agent [Back to how-to guides](/agent-platform/abl/how-to/overview#routing-and-multi-agent-orchestration) Use this when a supervisor should stop answering directly and let a specialist own the next turn, or when a temporary gate must run before the final specialist takes over. ## Concept Switching the active agent is done through `HANDOFF`. `EXPECT_RETURN: false` transfers ownership permanently to the child specialist — it becomes the active agent for the rest of the conversation. `EXPECT_RETURN: true` creates a temporary child thread that returns to the parent (via `ON_RETURN`), often for authentication or qualification — the supervisor stays the active agent in the long run, just pausing to delegate one sub-task. The authentication-gate pattern below relies on one subtle but important behavior: on the very first turn, `is_authenticated` doesn't exist yet. The condition `is_authenticated != true` still correctly evaluates to `true` in that case — the runtime treats an undefined variable as not equal to `true` — so the gate fires and routes to `Authentication_Agent` before the variable is ever set. You don't need to special-case "unset" separately from "false." ## Minimal working example ```yaml expandable=true theme={null} SUPERVISOR: Active_Agent_Switch_Supervisor GOAL: "Switch the active conversation owner to the right child agent" HANDOFF: - TO: Authentication_Agent WHEN: is_authenticated != true EXPECT_RETURN: true CONTEXT: pass: [customer_id] summary: "Authenticate before account work." ON_RETURN: action: resume_intent MAP: is_authenticated: is_authenticated - TO: Account_Service_Agent WHEN: intent.category == "account_service" AND is_authenticated == true EXPECT_RETURN: false CONTEXT: pass: [customer_id, account_id, conversation_summary] summary: "Account service agent now owns the conversation." - TO: Billing_Agent WHEN: intent.category == "billing" AND is_authenticated == true EXPECT_RETURN: false CONTEXT: pass: [customer_id, account_id, conversation_summary] summary: "Billing agent now owns the conversation." AGENT: Authentication_Agent GOAL: "Authenticate customers and return to the supervisor" GATHER: is_authenticated: TYPE: boolean PROMPT: "Is the customer authenticated?" REQUIRED: true COMPLETE: - WHEN: is_authenticated IS SET RESPOND: "Authentication result captured." AGENT: Account_Service_Agent GOAL: "Own account service after routing" AGENT: Billing_Agent GOAL: "Own billing service after routing" ``` `customer_id`, `account_id`, and `conversation_summary` are shown as passed context with no declared source in this example — treat them as project-local assumptions and declare them via `MEMORY` in your actual project. This example also has no fallback route if neither `account_service` nor `billing` matches after authentication — see the fallback-routing HowTo before shipping this pattern as-is. ## How it works * Before `is_authenticated` is ever set, `is_authenticated != true` evaluates to `true` (undefined is treated as not-equal-to-true), so the very first turn always routes to `Authentication_Agent`. * `Authentication_Agent` is a temporary child (`EXPECT_RETURN: true`): it gathers `is_authenticated`, completes once that field `IS SET`, and `ON_RETURN` maps its result back into the supervisor's own `is_authenticated` variable and resumes intent routing. * On the next pass through `HANDOFF`, `is_authenticated == true` now holds, so the compound conditions on `Account_Service_Agent`/`Billing_Agent` can match and transfer ownership permanently (`EXPECT_RETURN: false`). * None of these conditions are rewritten by the compiler — equality comparisons like `is_authenticated == true` and `intent.category == "account_service"` handle an unset variable correctly on their own (they simply evaluate `false`), so the trace shows your literal authored condition text. * Since `HISTORY` is omitted from every `HANDOFF`, each child (temporary or permanent) receives the full conversation history by default (the current platform default). * If you'd rather run the authentication step as a sub-agent call than a full route/return cycle, `DELEGATE` is an alternative to `HANDOFF` + `EXPECT_RETURN: true` for this kind of temporary gate — see the delegate-vs-handoff decision HowTo for the tradeoffs. ## Common variations * Temporary authentication child returns to supervisor (shown above). * Final billing or account specialist owns the conversation permanently after the gate passes. * A return handler maps child output into parent routing state (`MAP` above) so the next routing pass can use it. ## Verification * Parse and compile the ABL and confirm there are no parser or compiler errors/warnings. * Test an utterance on a fresh session (no `is_authenticated` set) and confirm it routes to `Authentication_Agent` first. * After authentication completes, test an account-service and a billing utterance and confirm permanent ownership transfer in each case. * Inspect the trace for the selected target, the evaluated condition (matches your literal authored text), and the `ON_RETURN`/`MAP` application after the authentication child returns. ## Production readiness checklist * The auth gate correctly handles the unset case on turn one (no special-casing needed, but verify it in a real trace). * `MAP` targets a variable the parent actually declares and routes on. * A fallback route exists for intents that don't match `account_service` or `billing` after authentication — this example doesn't include one. * Any variable assumed to already exist (like `customer_id` above) has a real declared source in your project. * Temporary vs. permanent switching is chosen deliberately per route, not by accident. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Trying to switch the active agent by setting a variable | Confusing session state with conversation ownership | Ownership only changes through `HANDOFF`/`DELEGATE`, never by setting a flag | | Using `EXPECT_RETURN: true` when the child should own the rest of the conversation | Copy-pasting the auth-gate pattern for a different case | Use `EXPECT_RETURN: false` for permanent ownership transfer | | Passing sensitive or irrelevant context to every child | Reusing one `CONTEXT.pass` list everywhere | Pass only what each specific child needs | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | The conversation never leaves `Authentication_Agent` | `ON_RETURN`/`MAP` isn't correctly writing `is_authenticated` back to the parent, or the child never completes | Inspect the `handoff_return_handler` trace for the actual returned value | | Every turn re-routes to `Authentication_Agent` even after authenticating | The mapped parent variable name doesn't match what the compound conditions check | Confirm `MAP`'s parent-side key exactly matches the variable used in later `WHEN` conditions | | A route unexpectedly falls through with no match | No fallback route exists in this example for unmatched intents post-authentication | Add a fallback route (see the fallback-routing HowTo) | | Child agent has more/less conversation context than expected | `HISTORY` omitted; current default (`full`) applied | Set an explicit `HISTORY` strategy if bounded/summary history is intended | *** **Related articles:** * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). * [How to route unclear requests to a fallback agent](/agent-platform/abl/how-to/route-to-fallback-agent). # How to use EXECUTION.pipeline for agent routing, classification, and short-circuiting Source: https://koreai.mintlify.app/agent-platform/abl/how-to/use-agent-execution-pipeline [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use `EXECUTION.pipeline` when a supervisor or routing agent needs classifier-assisted routing before the normal reasoning path decides what to do. ## Concept `EXECUTION.pipeline` is the agent execution pipeline, not a Knowledge Base ingestion pipeline, custom operational pipeline, or workflow. It sits in the agent execution path and can classify the user's message, compare the result with known routing targets, short-circuit obvious single-intent requests, and keep ambiguous or risky requests in the normal guided reasoning path. Use it when routing quality, latency, or tool selection depends on a fast classification pass. Do not use it to orchestrate long-running work. Durable waits, approvals, schedules, and multi-hour work belong in workflows. The pipeline is disabled by default unless the project-level gate and effective configuration enable it. Agent-level configuration can tune or opt out of behavior, but project configuration still matters for whether the pipeline actually runs. ## Minimal working example ```yaml expandable=true theme={null} SUPERVISOR: Support_Router GOAL: "Route support requests using classifier-assisted pipeline decisions" PERSONA: "Concise support routing supervisor" EXECUTION: pipeline: enabled: true mode: sequential model: "gpt-4.1-mini" shortCircuit: enabled: true confidenceThreshold: 0.9 keywordVeto: enabled: true keywords: ["refund", "cancel", "fraud"] intentBridge: enabled: true programmaticThreshold: 0.85 guidedThreshold: 0.5 outOfScopeDecline: true multiIntentSignal: true HANDOFF: - TO: Billing_Agent WHEN: "The user asks about invoices, balances, billing errors, refunds, or payments" SUMMARY: "User needs billing support" - TO: Technical_Support_Agent WHEN: "The user asks about device setup, errors, troubleshooting, or service outage" SUMMARY: "User needs technical support" - TO: General_Fallback_Agent WHEN: "The request is unclear, unsupported, or does not match a specialist" SUMMARY: "User needs clarification or general support" ``` ```yaml theme={null} AGENT: Billing_Agent GOAL: "Resolve billing, invoice, refund, and payment questions" PERSONA: "Billing specialist" ``` ```yaml theme={null} AGENT: Technical_Support_Agent GOAL: "Troubleshoot device, software, and service issues" PERSONA: "Technical support specialist" ``` ```yaml theme={null} AGENT: General_Fallback_Agent GOAL: "Clarify unclear support requests and route users to the right next step" PERSONA: "General support specialist" ``` ## How it works The classifier first tries to identify the user's intent category. If there is one high-confidence intent with a known category, short-circuiting can let the router choose the target without spending a full reasoning turn. Short-circuiting is intentionally conservative. It does not fire for low-confidence results, `null` categories, multiple intents, disabled short-circuit config, or keyword-veto matches. Keyword veto is useful for words like `refund`, `cancel`, `fraud`, or sensitive tool names where you want guided reasoning or escalation to inspect the request before direct routing. `intentBridge` controls what happens after classification: | Tier | Typical situation | Expected behavior | | - | - | - | | Programmatic | Classification is confident enough for deterministic handling | Route or decline according to configured policy | | Guided | Classification is useful but not certain | Hide or prioritize routing choices while allowing reasoning | | Autonomous | Classifier is absent, empty, or too uncertain | Let the normal reasoning path decide | Since none of the `HANDOFF` entries above declare `HISTORY`, each specialist now receives the full conversation history by default (the current platform default when `HISTORY` is omitted). ## Common variations ### Fast single-intent routing Enable `shortCircuit` for high-volume support routers where users usually ask one clear thing, such as "What is my balance?" or "My device will not start." ### Sensitive keyword veto Enable `keywordVeto` when certain words should prevent direct short-circuiting. This keeps risky requests in guided reasoning even when the classifier is confident. ### Multi-intent detection Enable `multiIntentSignal` when users often ask for more than one thing in a single turn, such as "Cancel my subscription and refund my last payment." ### Out-of-scope decline Use `outOfScopeDecline` when the classifier can confidently detect that a request does not belong to any supported category. ## Verification * Test a high-confidence single-intent request and confirm the intended specialist is selected. * Test a low-confidence or broad request and confirm it stays in guided reasoning. * Test a multi-intent request and confirm it does not short-circuit as a single route. * Test a veto keyword like `refund` or `fraud` and confirm short-circuiting is blocked. * Inspect traces for classifier result, confidence, selected category, veto keywords, and final route. ## Production readiness checklist * Routing categories are distinct and map clearly to handoff targets. * Fallback target exists for unclear, unsupported, or no-match requests. * Project-level pipeline configuration is enabled where the agent is deployed. * Thresholds are tested against real utterances, not only synthetic examples. * Sensitive keywords are reviewed by product, support, and compliance owners. * Trace dashboards monitor classifier confidence, short-circuit rate, veto rate, fallback rate, and misroutes. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Treating `EXECUTION.pipeline` as a workflow | Both use the word pipeline | Use workflows for durable orchestration; use `EXECUTION.pipeline` for agent execution routing | | Enabling short-circuit for ambiguous routing | It looks faster | Keep a fallback path and use conservative confidence thresholds | | Forgetting project-level enablement | Agent config looks complete | Verify effective runtime pipeline config, not only ABL syntax | | Missing fallback agent | The classifier cannot always match | Add a fallback route and test no-match requests | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Pipeline config compiles but does not run | Project gate is absent or disabled | Check effective project pipeline config | | Obvious request does not short-circuit | Confidence below threshold, multiple intents, null category, or veto keyword | Inspect classifier trace data | | Sensitive request routes too quickly | Keyword veto is missing or disabled | Add sensitive keywords and retest | | Classifier invents a category | Category is not in known routing set | Confirm the route categories and fallback behavior | *** **Related articles:** * [How to configure EXECUTION.pipeline models, modes, thresholds, and fallbacks](/agent-platform/abl/how-to/configure-agent-execution-pipeline). * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). # How to use behavior profiles to change agent behavior by context Source: https://koreai.mintlify.app/agent-platform/abl/how-to/use-behavior-profiles-by-context [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use behavior profiles when the same agent should keep its core responsibility but change style, constraints, tools, gathering behavior, or channel handling for a specific context. ## Concept A behavior profile is conditional behavior layered onto an agent. It has a name, a `PRIORITY`, a `WHEN` condition, and one or more behavior changes such as `INSTRUCTIONS`, `CONVERSATION`, `RESPONSE`, `VOICE`, `TOOLS`, `GATHER`, or `FLOW`. Use behavior profiles for context-sensitive adaptation, not for changing the agent's core job. If the responsibility changes, create another agent. If only the delivery mode or handling style changes, use a behavior profile. The compiler attaches profiles to the agent IR and execution contract. When profiles contain guidance, that guidance is represented as behavior-profile instructions for execution. Profiles can be inline in an agent or standalone and attached with `USE BEHAVIOR_PROFILE`. ## Minimal working example ```yaml theme={null} AGENT: Support_Assistant GOAL: "Resolve support questions with behavior matched to customer context" PERSONA: "Helpful support assistant" BEHAVIOR_PROFILE: recovery_mode PRIORITY: 10 WHEN: sentiment == "frustrated" OR repeat_contact == true INSTRUCTIONS: | Acknowledge the customer's prior effort before asking for more information. Keep the next step concrete and do not list more than two actions. BEHAVIOR_PROFILE: concise_mode PRIORITY: 3 WHEN: channel == "sms" INSTRUCTIONS: "Use short plain-text responses suitable for SMS." ``` ## Standalone profile example ```yaml theme={null} BEHAVIOR_PROFILE: voice_support_mode PRIORITY: 8 WHEN: channel == "voice" INSTRUCTIONS: | Speak in short sentences and confirm important identifiers one digit group at a time. VOICE: provider: elevenlabs speed: 0.95 ``` ```yaml theme={null} AGENT: Voice_Support_Assistant GOAL: "Resolve support questions on voice channels" PERSONA: "Clear phone support assistant" USE BEHAVIOR_PROFILE: voice_support_mode ``` ## How it works `PRIORITY` decides which profile should win when multiple profiles match. `WHEN` decides whether a profile applies. Keep conditions based on values your project actually provides, such as channel, sentiment, customer tier, locale, or repeat-contact state. Inline profiles are useful when the behavior belongs only to one agent. Standalone profiles are useful when several agents share the same channel or customer-context rule. ## Common variations ### Recovery mode Use a high-priority recovery profile for frustrated users, repeated failures, or repeat contacts. ### Channel mode Use channel profiles for SMS, voice, WhatsApp, or web when response length, formatting, or speech pacing changes. ### Enterprise customer mode Use customer-tier profiles when premium or enterprise users require a different tone, faster escalation, or more formal explanations. ## Verification * Test each `WHEN` condition with a context value that should match and one that should not. * Confirm higher-priority profiles override lower-priority profiles when both are true. * Inspect the compiled agent behavior profiles and execution contract for the expected profile count. * Test channel-specific outputs, especially voice and SMS, in the target channel rather than only in text chat. ## Production readiness checklist * Every profile has a non-negative integer `PRIORITY`. * Every profile has a `WHEN` condition. * Conditions reference context values that are reliably populated. * Shared standalone profiles are versioned and reviewed because changing one can affect multiple agents. * Tests cover overlapping profiles, no-match behavior, and channel-specific response constraints. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Using profiles as hidden agents | The profile starts changing the job, not the behavior | Split into another agent when responsibility changes | | Missing `PRIORITY` or `WHEN` | The profile looks like a simple instruction block | Add both fields to every behavior profile | | Referencing unavailable context | Conditions never match | Document where each condition value is set | | Overlapping profiles fight each other | Multiple profiles match the same turn | Use clear priorities and test overlap | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Profile never applies | `WHEN` references an unset value | Confirm the context value exists at runtime | | Wrong profile wins | Priority order is wrong | Compare all matching profile priorities | | Shared behavior changed unexpectedly | A standalone profile is reused | Find all agents using the profile before editing | | Voice behavior is inconsistent | Text profile does not define voice-specific settings | Add `VOICE` or channel-specific instructions | *** **Related articles:** * [How to write effective agent goals, personas, instructions, and limitations](/agent-platform/abl/how-to/write-agent-goals-personas-instructions). * [How to design channel-specific welcome experiences](/agent-platform/abl/how-to/design-channel-specific-welcome). # How to use ON_START to greet users before the first message Source: https://koreai.mintlify.app/agent-platform/abl/how-to/use-on-start-welcome [Back to how-to guides](/agent-platform/abl/how-to/overview#conversation-entry-and-welcome-experience) Use this pattern when the agent must run startup logic before the user sends the first turn. ## Concept `ON_START` is the session-start lifecycle handler. It can set session values, call a tool, choose a response branch, return voice or rich payloads, and delegate to another agent. It runs once per initialized runtime session and is idempotent: if the session is already initialized, startup does not run again. Use `ON_START` for work that belongs to session initialization: greeting, reading channel context, loading a profile, setting counters, or selecting a first-turn experience. Do not use it as a replacement for normal per-turn routing. ## Minimal working example ```yaml expandable=true theme={null} AGENT: On_Start_Welcome_Agent GOAL: "Run startup logic before the first user message" ON_START: SET: startup_source = session.channel RESPOND: "Hello. I am ready to help." FLOW: entry_point: first_step steps: - first_step first_step: REASONING: false RESPOND: "You reached us from {{startup_source}}." THEN: COMPLETE ``` ## How it works `SET` executes before the response. That means values copied from runtime context can be interpolated into the welcome or into the first flow step. The runtime emits `dsl_set` traces for assignments and `dsl_respond` traces for the startup response. ## Common variations ### Personalize the welcome with startup values A startup response can include placeholders. The welcome renders with values that `SET` assigns earlier in the same `ON_START` block, so the user sees real session data in the first message rather than a generic greeting. ```yaml expandable=true theme={null} AGENT: On_Start_Personalized_Agent GOAL: "Greet the user with values set during startup" ON_START: SET: startup_source = session.channel RESPOND: "Hello. You reached us from {{startup_source}}." FLOW: entry_point: ready steps: - ready ready: REASONING: false RESPOND: "How can I help?" THEN: COMPLETE ``` This works on every channel that triggers a welcome. On Kore Agent Assist, Agent AI triggers it when you select **Use 'Trigger Welcome message' as the first request to Agentic**. For details, see [Agent AI integration](/agent-platform/integrations/agent-ai#choose-first-request-trigger). ### Choose a branch at startup Branches are evaluated in order. The first matching branch wins. An `ELSE` branch is the default branch. ```yaml expandable=true theme={null} AGENT: On_Start_Branch_Agent GOAL: "Choose the first welcome turn from session context" ON_START: BRANCHES: - IF: session.interaction.current.language == "fr" RESPOND: "Bonjour. Comment puis-je vous aider ?" - ELSE: RESPOND: "Hello. How can I help?" FLOW: entry_point: ready steps: - ready ready: REASONING: false RESPOND: "Ready." THEN: COMPLETE ``` ## Verification Create sessions with different interaction language values and confirm the selected welcome changes. In traces, check `dsl_on_start_branch` for the matched branch index. Confirm `ON_START` runs once by sending a second message in the same session and checking that no second startup trace is emitted. For a personalized welcome, confirm the first message shows the assigned value rather than the raw placeholder. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Putting `SET` or `CALL` inside a branch | Branches look like mini flows. | Keep side effects at top-level `ON_START`; use branches only to select startup responses. | | Expecting startup to rerun on every message | `ON_START` is a session initialization hook. | Use normal flow, handoff, or reasoning logic for per-turn behavior. | | Branching on values that do not exist yet | The first user turn has not happened. | Branch only on runtime context, constants, memory recall, or values set during startup. | ## Troubleshooting If a branch condition is malformed, the runtime fails closed and falls back to the top-level `ON_START` response when one exists. If no startup response is returned, the flow can still run from its entry point. ## Production readiness checklist * Put side effects at top-level `ON_START`; branches can choose responses but cannot run `SET`, `CALL`, or `DELEGATE`. * Use an `ELSE` branch or a top-level fallback response. * Keep branch conditions based on values that are present before the first user turn. * Validate traces for `dsl_on_start`, `dsl_set`, and `dsl_on_start_branch`. # How to write effective agent goals, personas, instructions, and limitations Source: https://koreai.mintlify.app/agent-platform/abl/how-to/write-agent-goals-personas-instructions [Back to how-to guides](/agent-platform/abl/how-to/overview#agent-architecture-and-design) Use this pattern when an agent needs a clear job boundary, a consistent voice, and explicit limits that prevent it from taking work owned by another agent, tool, workflow, or human team. ## Concept An agent definition needs more than a name. The `GOAL` says what outcome the agent owns. `PERSONA` describes how it should communicate while doing that work. `LIMITATIONS` define what it must not do. Together, these sections give the runtime enough identity to build useful prompts and give your project team a stable contract for routing, testing, and escalation. Write the goal as an owned responsibility, not a slogan. Write the persona as observable behavior, not a brand adjective list. Write limitations as enforceable boundaries that point to the next action when possible. ## Minimal working example ```yaml theme={null} AGENT: Refund_Policy_Agent GOAL: "Answer refund policy questions and explain next steps without creating refund requests" PERSONA: | Calm policy specialist. Uses short answers first, then offers details. Separates confirmed policy facts from suggested next steps. LIMITATIONS: - "Does not approve refunds" - "Does not change order or payment records" - "Routes payment disputes to Billing_Specialist" HANDOFF: - TO: Billing_Specialist WHEN: "The user asks to dispute a charge, reverse a payment, or override a refund decision" PASS: [order_id, customer_id] SUMMARY: "User needs billing or payment-dispute help" ``` ```yaml theme={null} AGENT: Billing_Specialist GOAL: "Handle billing and payment-dispute requests" PERSONA: "Billing specialist" ``` ## How it works The refund agent owns policy explanation, not payment actions. Its limitation is not just "do not process refunds"; it also names the correct boundary for disputes. The `HANDOFF` makes that boundary executable by routing charge disputes to `Billing_Specialist`. This separation matters in enterprise assistants because customers do not care which internal team owns an issue. They ask naturally. The agent goal and limitations keep the self-service experience helpful while preventing the wrong agent from taking a sensitive action. Since the `HANDOFF` above omits `HISTORY`, `Billing_Specialist` now receives the full conversation history by default (the current platform default when `HISTORY` is omitted) — set an explicit `HISTORY` strategy if you want bounded or summary-only context instead. ## Common variations ### Short persona Use a one-line persona for simple specialists: ```yaml theme={null} AGENT: Billing_Answer_Agent GOAL: "Answer billing questions without changing account records" PERSONA: "Concise billing specialist who explains account actions clearly" ``` ### Multiline persona Use a multiline persona when tone, format, and operating style matter: ```yaml theme={null} AGENT: Identity_Verification_Agent GOAL: "Verify user identity before sensitive account actions" PERSONA: | Direct and reassuring. Confirms critical identifiers before explaining next steps. Avoids legal or financial promises. ``` ### IDENTITY section Some projects use `IDENTITY:` to group role, persona, expertise, and limitations. Use it only when the project standard prefers that shape; otherwise, `GOAL`, `PERSONA`, and `LIMITATIONS` are easier to scan. ## Verification * Test an in-scope request and confirm the agent answers directly. * Test a boundary request and confirm it hands off, delegates, or declines instead of improvising. * Test a sensitive request, such as refund approval or payment reversal, and confirm the limitation is respected. * Inspect the handoff target and passed fields so the receiving agent has enough context. ## Production readiness checklist * Every agent has one primary responsibility. * Limitations name prohibited work and the correct next path. * Routing conditions align with the same boundaries described in limitations. * Sensitive actions are handled by the right specialist, workflow, tool, or human escalation. * Test cases cover in-scope, adjacent, out-of-scope, and ambiguous requests. ## Common mistakes | Mistake | Why it happens | How to avoid it | | - | - | - | | Goal is too broad | The agent becomes a catch-all | Use one owned outcome per agent | | Persona is only branding | It does not guide behavior | Describe response style, confirmation behavior, and tone | | Limitations have no route | The agent knows what not to do but not what next | Pair important limits with handoff, delegate, escalation, or refusal behavior | | Limitations conflict with tools | A tool enables an action the agent says it cannot do | Review tools and limitations together | ## Troubleshooting | Symptom | Likely cause | What to check | | - | - | - | | Agent answers outside its scope | Goal or limitations are too broad | Tighten the goal and add boundary tests | | Agent refuses too much | Limitations are written as general warnings | Rewrite them as specific prohibited actions | | Handoff happens without useful context | `PASS` fields are missing or unset | Confirm field sources and target-agent needs | | Two agents claim the same request | Responsibilities overlap | Rewrite goals around ownership, not departments | *** **Related articles:** * [How to define clear responsibilities for each enterprise agent](/agent-platform/abl/how-to/define-agent-responsibilities). * [How to design a supervisor that routes users to specialist agents](/agent-platform/abl/how-to/design-supervisor-routing-agent). # Agent declaration Source: https://koreai.mintlify.app/agent-platform/abl/reference/agent-declaration The agent declaration establishes the identity, metadata, goals, personality, and execution configuration of an ABL agent document. Every `.agent.abl` file must begin with an `AGENT:` declaration. ## AGENT keyword The `AGENT:` keyword declares the agent's name. This name must be unique within the project and is used as the primary identifier throughout the platform. ```yaml theme={null} AGENT: Wire_Transfer_Specialist ``` ### Naming rules * Use `PascalCase` with underscores to separate words * Must start with a letter * May contain letters, digits, and underscores * Must be unique within the system ### Examples ```yaml theme={null} AGENT: Hotel_Search AGENT: Payment_Processor AGENT: Customer_Support AGENT: Fraud_Detection ``` ## VERSION The `VERSION:` directive specifies the semantic version of the agent document. The value must be a quoted string in semver format (`major.minor.patch`). ```yaml theme={null} VERSION: "2.0.0" ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `VERSION` | `string` (semver) | No | `"1.0.0"` | Document version in `major.minor.patch` format | The version is stored in the document's metadata (`meta.version`) and is available for compatibility checks, deployment tracking, and audit trails. ### Example ```yaml theme={null} AGENT: Booking_Agent VERSION: "3.1.0" ``` ## DESCRIPTION The `DESCRIPTION:` directive provides a human-readable summary of the agent's purpose. It supports both single-line and multi-line (pipe block) formats. ```yaml theme={null} DESCRIPTION: "Handles flight booking and modifications" ``` ```yaml theme={null} DESCRIPTION: | Processes outbound wire transfers for retail and commercial banking customers. Handles domestic (Fedwire) and international (SWIFT) transfers with full regulatory compliance. ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `DESCRIPTION` | `string` | No | *none* | Human-readable summary of the agent's purpose | ## LANGUAGE The `LANGUAGE:` directive sets the primary language the agent operates in. The value is a quoted BCP 47 language tag. ```yaml theme={null} LANGUAGE: "en" ``` ```yaml theme={null} LANGUAGE: "es-EC" ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `LANGUAGE` | `string` | No | *none* | BCP 47 language code (for example. `"en"`, `"es-EC"`, `"fr"`) | The language directive influences the runtime's language-aware behavior including NLU processing, response generation, and entity recognition. ## Complete declaration property reference The following table summarizes all agent declaration properties: | Property | Type | Required | Default | Description | | - | - | - | - | - | | `AGENT` | `string` | **Yes** | -- | Unique agent name in `PascalCase_With_Underscores` | | `VERSION` | `string` (semver) | No | `"1.0.0"` | Document version | | `DESCRIPTION` | `string` | No | *none* | Human-readable description | | `LANGUAGE` | `string` | No | *none* | BCP 47 language code | *** ## Identity and goals The identity and goals sections define what an agent is trying to achieve, how it presents itself, and what boundaries it operates within. These sections feed directly into the system prompt that guides the LLM's behavior at runtime. ### GOAL The `GOAL:` section defines the agent's primary objective. It is the only **required** section besides `AGENT:`. The goal drives the LLM's reasoning, determines completion conditions, and provides the core purpose statement included in every system prompt. ```yaml theme={null} GOAL: "Process the customer's wire transfer request accurately and securely." ``` Multi-line goals use pipe block syntax: ```yaml theme={null} GOAL: | Process the customer's outbound wire transfer request accurately and securely. Collect all required beneficiary and payment details, screen for sanctions and fraud, enforce daily limits and cut-off windows, obtain dual authorization when required, and execute the transfer with a complete audit trail. ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `GOAL` | `string` | **Yes** | -- | The agent's primary objective. Used in system prompt and completion detection. | #### Goal guidelines * State the objective from the agent's perspective, not the user's * Be specific about what the agent should accomplish * Include success criteria when measurable outcomes exist * When an agent includes a `FLOW:` section, the goal also serves as context for any steps that have `REASONING: true` ### PERSONA The `PERSONA:` section describes how the agent should present itself -- its tone, communication style, expertise signaling, and behavioral characteristics. The persona is included in the system prompt to shape the LLM's response style. ```yaml theme={null} PERSONA: "Friendly and patient customer support representative." ``` ```yaml theme={null} PERSONA: | Senior wire operations specialist at a large commercial bank — precise with numbers, methodical about compliance steps, and transparent about processing times and fees. Never rushes a wire. Confirms every detail before execution because a released wire cannot be reversed, only recalled. ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `PERSONA` | `string` | No | `""` (empty) | Personality and communication style description | If omitted, the agent uses a neutral, professional tone determined by the LLM's default behavior. ### LIMITATIONS The `LIMITATIONS:` section declares explicit boundaries on what the agent cannot or must not do. Each limitation is a quoted string in a list. Limitations are included in the system prompt as hard constraints. ```yaml theme={null} LIMITATIONS: - "Cannot process wires to OFAC-sanctioned countries" - "Cannot override the daily wire transfer limit" - "Cannot process wires after the daily cut-off" - "Cannot reverse a wire after execution" ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `LIMITATIONS` | `string[]` | No | `[]` (empty list) | List of explicit boundaries the agent must not cross | #### Limitation guidelines * State what the agent **cannot** do, not what it can do * Be specific -- vague limitations are difficult for the LLM to enforce * Include the reason or consequence when it helps clarity * Limitations complement `CONSTRAINTS:` (which are machine-enforced); limitations are LLM-enforced behavioral rules ### INSTRUCTIONS The `INSTRUCTIONS:` section provides operational guidance that supplements the goal. Instructions are appended to the goal in the system prompt. Use instructions for procedural guidance that does not fit naturally in the goal or persona. ```yaml theme={null} INSTRUCTIONS: | Always verify the customer's identity before collecting wire details. For international wires, confirm the purpose code. If the customer seems uncertain about the SWIFT/BIC code, recommend they verify with the beneficiary before proceeding. ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `INSTRUCTIONS` | `string` | No | *none* | Operational guidance appended to the agent's goal | Instructions and the goal are combined at parse time: the instructions text is appended under an `Instructions:` heading within the goal's description. ### IDENTITY The `IDENTITY:` section is an alternative, structured format that combines role, persona, expertise, and limitations into a single block. When used, its fields are mapped to the equivalent top-level sections. ```yaml theme={null} IDENTITY: role: "Senior wire operations specialist" persona: "Methodical, precise, and transparent about compliance" expertise: [wire transfers, compliance screening, FX conversion] limitations: ["Cannot override daily limits", "Cannot reverse executed wires"] ``` #### IDENTITY field reference | Field | Type | Required | Default | Maps to | | - | - | - | - | - | | `role` | `string` | No | `""` | `GOAL:` (sets the goal description) | | `persona` | `string` | No | `""` | `PERSONA:` (sets persona description) | | `expertise` | `string[]` | No | `[]` | Appended to `PERSONA:` as "Expertise: ..." | | `limitations` | `string[]` | No | `[]` | `LIMITATIONS:` | #### Mapping behavior When the parser encounters an `IDENTITY:` block: 1. The `role` value becomes the `GOAL:` description 2. The `persona` value becomes the `PERSONA:` description 3. The `expertise` list is appended to the persona as a comma-separated string 4. The `limitations` list populates `LIMITATIONS:` You can use either the `IDENTITY:` block or the individual `GOAL:`, `PERSONA:`, `LIMITATIONS:` sections, but if both are present, the individual sections take the values set last in document order. ### Combining identity sections The recommended approach is to use the individual sections (`GOAL:`, `PERSONA:`, `LIMITATIONS:`) for clarity. The `IDENTITY:` block is available as a compact alternative. #### Recommended style ```yaml expandable=true theme={null} AGENT: Hotel_Search GOAL: | Help the customer find and book a hotel that matches their preferences, budget, and travel dates. PERSONA: | Enthusiastic travel advisor who knows every major hotel chain and boutique property. Provides honest comparisons and highlights trade-offs between price, location, and amenities. LIMITATIONS: - "Cannot guarantee room availability — availability changes in real time" - "Cannot process payments directly — redirects to the booking system" - "Cannot access loyalty program accounts for other hotel chains" ``` #### Compact style (using IDENTITY) ```yaml theme={null} AGENT: Hotel_Search IDENTITY: role: "Help the customer find and book a hotel matching their preferences" persona: "Enthusiastic travel advisor with deep hotel industry knowledge" expertise: [hotel chains, boutique properties, price comparison] limitations: - "Cannot guarantee room availability" - "Cannot process payments directly" ``` ### SYSTEM\_PROMPT The `SYSTEM_PROMPT:` section is an advanced escape hatch that supplies a **complete, custom system prompt**, replacing the prompt the platform would otherwise generate from `GOAL:`, `PERSONA:`, and `LIMITATIONS:`. ```yaml theme={null} SYSTEM_PROMPT: | You are a wire operations specialist. Follow the bank's compliance procedures exactly. Never release a wire without dual authorization. ``` When `SYSTEM_PROMPT:` is present, the auto-generated prompt is **not** used — the goal, persona, and limitations are no longer automatically composed into the system prompt. Prefer the structured `GOAL:`/`PERSONA:`/`LIMITATIONS:` sections unless you specifically need full control of the prompt text. *** ## Execution configuration The `EXECUTION:` section configures runtime behavior for the agent, including LLM model selection, temperature, token limits, timeouts, extended thinking, and per-operation model routing. ### Syntax ```yaml theme={null} EXECUTION: model: "claude-sonnet-4-5-20250929" temperature: 0.3 max_tokens: 4096 tool_timeout: 30000 fallback_model: "claude-haiku-4-5-20251001" ``` All properties are optional. When omitted, the platform applies its built-in defaults. ### Configuration properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `model` | `string` | No | Platform default | Primary LLM model identifier | | `temperature` | `number` | No | Platform default | Sampling temperature (0.0--1.0). Lower values produce more deterministic output. | | `top_p` | `number` | No | Platform default | Nucleus-sampling probability mass. | | `top_k` | `number` | No | Platform default | Top-k sampling cutoff. | | `frequency_penalty` | `number` | No | Platform default | Penalize tokens by how often they have appeared. | | `presence_penalty` | `number` | No | Platform default | Penalize tokens that have appeared at all. | | `seed` | `number` | No | Platform default | Sampling seed for reproducible output (provider-dependent). | | `stop_sequences` | `string[]` | No | Platform default | Sequences that stop generation. | | `reasoning_effort` | `string` | No | Platform default | Reasoning effort: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max` (reasoning models). | | `verbosity` | `string` | No | Platform default | Output verbosity: `low`, `medium`, or `high` (supported models). | | `max_tokens` | `number` | No | Platform default | Maximum tokens in LLM response | | `tool_timeout` | `number` | No | Platform default | Timeout in milliseconds for tool execution | | `llm_timeout` | `number` | No | Platform default | Timeout in milliseconds for LLM inference calls | | `session_idle_timeout` | `number` | No | Platform default | Timeout in milliseconds before an idle session expires | | `max_reasoning_iterations` | `number` | No | Platform default | Maximum reasoning loop iterations for agents without a flow | | `max_flow_iterations` | `number` | No | Platform default | Maximum step transitions in a flow before forced exit | | `voice_latency_target` | `number` | No | Platform default | Target latency in milliseconds for voice channel responses | | `fallback_model` | `string` | No | *none* | Model to use when the primary model is unavailable or errors | | `enable_thinking` | `boolean` | No | `false` | Enable extended thinking (Anthropic Claude models) | | `thinking_budget` | `number` | No | Platform default | Token budget allocated to extended thinking | | `token_budget` | `number` | No | *none* | Cumulative LLM token budget for a single execution/session; primarily used to bound debug/test runs | | `compaction_threshold` | `number` | No | Platform default | Context-usage ratio (0.0--1.0) that triggers auto-compaction | | `compaction` | `object` | No | Platform default | Fine-grained compaction policy overrides. See [Compaction policy overrides](#compaction-policy-overrides). | | `inline_gather` | `boolean` | No | `false` | When `true`, gather fields are collected inline during conversation rather than as a separate phase | | `concurrency` | `string` | No | `serial` | Message-processing strategy: `serial`, `preemptive`, or `parallel`. See [Message concurrency](#message-concurrency). | | `max_queue_depth` | `number` | No | `10` | Maximum pending messages held in the per-session queue. | | `max_concurrent_messages` | `number` | No | Platform default | Maximum concurrent message executions when `concurrency: parallel`. | | `conversation_history_window` | `number` | No | Platform default | Number of prior conversation messages to include in the LLM context. | | `voice` | `object` | No | *none* | Agent-level voice (TTS) configuration. See [Voice configuration](/agent-platform/abl-reference/rich-content#voice-configuration). | | `pipeline` | `object` | No | disabled | Pre-classification / tool-filter pipeline (supervisors). See [Multi-Agent and Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor#execution-pipeline-pre-classification). | ### Extended thinking Extended thinking allows Claude models to perform internal reasoning before producing a response. This is useful for complex multi-step reasoning tasks where the agent needs to plan before acting. ```yaml theme={null} EXECUTION: model: "claude-sonnet-4-5-20250929" enable_thinking: true thinking_budget: 8000 ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `enable_thinking` | `boolean` | No | `false` | Enable the extended thinking capability | | `thinking_budget` | `number` | No | Platform default | Maximum tokens the model can use for internal reasoning | When enabled, the model uses a portion of its context window for internal chain-of-thought reasoning before producing the visible response. The `thinking_budget` controls how many tokens are allocated to this internal reasoning. ### Context compaction As conversations grow, the context window fills with message history. Context compaction automatically summarizes older messages when the context usage exceeds a threshold, preventing context-window overflow. ```yaml theme={null} EXECUTION: compaction_threshold: 0.75 ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `compaction_threshold` | `number` | No | Platform default | Ratio (0.0--1.0) of context usage that triggers compaction | When the ratio of used tokens to available tokens exceeds this threshold, the runtime compacts older conversation history into a summary, freeing space for new messages while preserving essential context. #### Compaction policy overrides For fine-grained control, the `compaction:` sub-block overrides how three categories of context are compacted. This is advanced tuning — most agents only need `compaction_threshold`. ```yaml expandable=true theme={null} EXECUTION: compaction: tool_results: strategy: structured # none | truncate | structured | summarize max_chars: 2000 keep_recent: 3 prior_turns: strategy: compact # none | placeholder | compact | summarize assistant_preview_chars: 200 prompt_context: projection_strategy: summary # preview | summary | metadata value_max_chars: 500 total_max_chars: 4000 ``` | Sub-block | Purpose | `strategy` values | | - | - | - | | `tool_results` | How historical tool-call results are compacted | `none`, `truncate`, `structured`, `summarize` | | `prior_turns` | How older conversation turns are compacted | `none`, `placeholder`, `compact`, `summarize` | | `prompt_context` | How injected prompt-context values are projected | `projection_strategy`: `preview`, `summary`, `metadata` | Each sub-block accepts additional character-budget and retention keys (for example. `max_chars`, `keep_recent`, `assistant_preview_chars`, `value_max_chars`, `total_max_chars`). ### Message concurrency The `concurrency` property controls how a session handles multiple inbound messages. ```yaml theme={null} EXECUTION: concurrency: preemptive # serial | preemptive | parallel max_queue_depth: 10 max_concurrent_messages: 3 ``` | Value | Behavior | | - | - | | `serial` | (Default) Process one message at a time; additional messages queue. | | `preemptive` | A new message can interrupt/replace in-flight processing of a superseded turn. | | `parallel` | Process up to `max_concurrent_messages` messages concurrently. | `max_queue_depth` (default `10`) bounds how many messages wait in the per-session queue. `concurrency`, `max_queue_depth`, `max_concurrent_messages`, and the `EXECUTION.voice` block are supported in the `.agent.abl` format. The `.agent.yaml` format does not yet parse these keys. ### Per-operation model routing The `models:` sub-block within `EXECUTION:` allows you to route different operations to different LLM models. This enables cost optimization by using faster, cheaper models for simple tasks and more capable models for complex reasoning. ```yaml theme={null} EXECUTION: model: "claude-sonnet-4-5-20250929" fallback_model: "claude-haiku-4-5-20251001" models: extraction: "claude-haiku-4-5-20251001" response_gen: "claude-sonnet-4-5-20250929" reasoning: "claude-sonnet-4-5-20250929" coordination: "claude-sonnet-4-5-20250929" ``` The `models:` block maps operation names to model identifiers. The `model` property serves as the default for any operation not explicitly listed. #### Recognized operation names | Operation | Description | | - | - | | `extraction` | Extracting structured data from user messages (for example. gather field extraction) | | `validation` | Validating extracted or gathered values | | `tool_selection` | Selecting which tool to call (also used by the classification pipeline) | | `response_gen` | Generating user-facing responses | | `summarization` | Summarizing conversation history during compaction | | `reasoning` | Complex reasoning, planning, and decision-making | | `realtime_voice` | Low-latency voice-channel turns | | `coordination` | Multi-agent coordination and routing decisions | | `embedding` | Embedding generation | | `title` | Conversation/title generation | Any other operation name is accepted by the parser but only resolved at runtime if the platform recognizes it. `EXECUTION.compaction.model` is deprecated — use `EXECUTION.models.summarization` instead to set the model used when summarizing history. ### Voice coordinator policy For realtime voice agents, `EXECUTION.voice.coordinator_policy` sets developer-controlled delivery preferences — tone, pace, brevity, and accessibility of spoken output. It shapes how the voice coordinator speaks; the semantic content still comes from the agent's goal and prompts. ```yaml theme={null} EXECUTION: voice: coordinator_policy: language: en-US tone: warm # neutral | warm | formal | energetic pace: slow # slow | normal | fast brevity: concise # concise | standard interruption: acknowledge_then_resume # resume_latest | acknowledge_then_resume greeting_style: brief # brief | warm | none accessibility: confirm_critical_values: true spell_sensitive_values: true ``` | Field | Type | Description | | - | - | - | | `language` | `string` | BCP-47 language tag for delivery (for example, `en-US`). | | `tone` | `"neutral"` \| `"warm"` \| `"formal"` \| `"energetic"` | Speaking tone. | | `pace` | `"slow"` \| `"normal"` \| `"fast"` | Speaking pace. | | `brevity` | `"concise"` \| `"standard"` | How terse responses should be. | | `interruption` | `"resume_latest"` \| `"acknowledge_then_resume"` | How to resume after the caller interrupts. | | `greeting_style` | `"brief"` \| `"warm"` \| `"none"` | Opening-greeting style. | | `accessibility.confirm_critical_values` | `boolean` | Read back critical values (amounts, dates) for confirmation. | | `accessibility.spell_sensitive_values` | `boolean` | Spell out sensitive values (codes, IDs) character by character. | Every key also accepts its camelCase spelling (`greetingStyle`, `confirmCriticalValues`, `spellSensitiveValues`). This delivery policy is separate from the per-output [scoped `barge_in`](/agent-platform/abl/reference/flow#scoped-barge-in) policy. ### Complete execution example ```yaml expandable=true theme={null} EXECUTION: model: "claude-sonnet-4-5-20250929" temperature: 0.1 max_tokens: 4096 max_reasoning_iterations: 8 tool_timeout: 30000 llm_timeout: 60000 session_idle_timeout: 900000 fallback_model: "claude-haiku-4-5-20251001" enable_thinking: true thinking_budget: 4000 compaction_threshold: 0.8 models: extraction: "claude-haiku-4-5-20251001" response_gen: "claude-sonnet-4-5-20250929" reasoning: "claude-sonnet-4-5-20250929" coordination: "claude-sonnet-4-5-20250929" ``` *** ## Complete example ```yaml expandable=true theme={null} AGENT: Wire_Transfer_Specialist VERSION: "2.0.0" DESCRIPTION: | Processes outbound wire transfers for retail and commercial banking customers. Handles domestic (Fedwire) and international (SWIFT) transfers with full regulatory compliance. LANGUAGE: "en" GOAL: | Process the customer's outbound wire transfer request accurately and securely. PERSONA: | Senior wire operations specialist at a large commercial bank — precise with numbers, methodical about compliance steps, and transparent about processing times and fees. LIMITATIONS: - "Cannot process wires to OFAC-sanctioned countries" - "Cannot override the daily wire transfer limit" - "Cannot reverse a wire after execution" EXECUTION: model: "claude-sonnet-4-5-20250929" temperature: 0.1 max_tokens: 4096 tool_timeout: 30000 fallback_model: "claude-haiku-4-5-20251001" ``` *** **Related articles:** * [Language overview](/agent-platform/abl-reference/language-overview) -- file structure, syntax rules, and auto-detection * [Tools](/agent-platform/abl-reference/tools) -- tool-level timeout configuration * [FLOW](/agent-platform/abl-reference/flow) -- per-step GOAL overrides and `MAX_TURNS` for reasoning steps # Behavior profiles Source: https://koreai.mintlify.app/agent-platform/abl/reference/behavior-profiles Behavior profiles are standalone ABL documents. Agents attach them with `USE BEHAVIOR_PROFILE: `, and the compiler attaches the matching profile IR to that agent. ## File contract Behavior profile files belong in `behavior_profiles/` and should use the `.behavior_profile.abl` suffix: ```text theme={null} behavior_profiles/voice_vip.behavior_profile.abl ``` The manifest records them under `behavior_profiles`, while `layers_included` still uses `core`. Do'nt add `behavior_profiles` to `layers_included`. The manifest entry is a pointer, not the profile definition. Import preview compiles the actual file at `behavior_profiles..path`. A package that only lists a profile in `project.json` but omits the referenced `.abl` file doesn't create the profile; agents with `USE BEHAVIOR_PROFILE: ` will fail resolution with a missing profile diagnosis. ```json theme={null} { "format_version": "2.0", "layers_included": ["core"], "behavior_profiles": { "voice_vip": { "name": "voice_vip", "path": "behavior_profiles/voice_vip.behavior_profile.abl", "priority": 100, "when_summary": "channel == voice", "used_by": ["Support_Agent"] } } } ``` ## Grammar ```yaml expandable=true theme={null} BEHAVIOR_PROFILE: voice_vip PRIORITY: 100 WHEN: channel == "voice" INSTRUCTIONS: | Speak concisely and confirm one thing at a time. CONVERSATION: speaking: tone: warm max_sentences: 2 VOICE: instructions: "Use a calm voice with clear pauses." TOOLS: HIDE: [internal_debug_tool] GATHER: FIELD_OVERRIDES: account_id: prompt: "Please say or enter your account number." ``` Required declarations: | Declaration | Required | Notes | | - | - | - | | `BEHAVIOR_PROFILE:` | yes | Case-sensitive profile name | | `PRIORITY:` | yes | Non-negative integer. Higher priority wins when profiles conflict. | | `WHEN:` | yes | CEL expression evaluated against context (`WHEN: true` for a default profile). | ## Profile blocks Beyond the required declarations, a profile can override several aspects of agent behavior. All blocks are optional. ### CONSTRAINTS Adds business-rule constraints while the profile is active (a list of strings). ```yaml theme={null} CONSTRAINTS: - "Never reveal internal tool names." - "Always confirm amounts above $500." ``` ### RESPONSE Shapes response rendering. | Sub-key | Type | Description | | - | - | - | | `max_buttons` | `number` | Maximum interactive buttons to render. | | `fallback_format` | `string` | Format to fall back to (for example. `text`). | | `media_types` | `string[]` | Allowed media types. | | `max_response_length` | `number` | Maximum response length. | ### VOICE Voice (TTS) overrides while the profile is active. Accepts the full [voice configuration](/agent-platform/abl-reference/rich-content#voice-configuration) — `instructions`, `ssml`, `plain_text`, `provider`, `voice_id`, `speed`, `pace`, `temperature`, and `auto_tts_language_switch`. ### TOOLS `HIDE:` removes tools from the agent's tool set; `ADD:` injects new tool definitions while the profile is active. ```yaml theme={null} TOOLS: HIDE: [internal_debug_tool] ADD: priority_callback(reason: string) -> {ticket_id: string} description: "Open a VIP priority callback ticket" PARAMETERS: - reason: string ``` ### CONVERSATION `CONVERSATION:` is also a valid **top-level agent section** — an agent can declare it directly (`doc.conversation`) with the exact same grammar shown here. Use the profile form to apply conversation overrides conditionally (via the profile's `WHEN`), or the top-level form to set an agent's baseline conversation behavior. Conversation-style overrides, organized into three sub-sections: * **`speaking`** — output style: `style`, `tone`, `emotion`, `pace`, `variety`, `max_sentences`, `one_thing_at_a_time`, `language_policy` (`interaction_context` / `agent_default` / `fixed`), `fixed_language`, `tool_lead_in`, `tool_results`, `readback`, `handoffs`, `phrases_ref`, `pronunciations_ref`. * **`listening`** — input handling: `barge_in`, `backchannels`, `on_pause`, `on_overlap`, `on_unclear_audio`, `on_self_correction`, `use_audio_cues`, and the voice sub-blocks `on_no_input`, `on_caller_backchannel`, `on_hold`, and `barge_in_continuation` (see [Voice turn-taking](#voice-turn-taking)). * **`interaction`** — dialogue behavior: `answer_shape`, `detail`, `initiative`, `grounding`, `clarification`, `confirmation`, `uncertainty`, `empathy`, `repair`, `context`, `closure`, `assumption_handling`, `flow_mode`, and more. ```yaml theme={null} CONVERSATION: speaking: tone: warm max_sentences: 2 one_thing_at_a_time: true listening: barge_in: allow interaction: clarification: max_questions: 1 ``` #### Voice turn-taking For voice/telephony channels, four `listening` sub-blocks control turn-taking behavior: **`on_no_input`** — handle caller silence. | Field | Type | Description | | - | - | - | | `enabled` | `boolean` | Toggle no-input handling. | | `mode` | `"static"` , `"dynamic"` | Static prompts vs. LLM-generated re-prompts. | | `timeout_ms` | `number` | Silence duration before triggering. | | `relaxed_interval_ms` | `number` | Extended interval between retries. | | `max_retries` | `number` | Maximum no-input retries. | | `on_exhausted` | `"hangup"` , `"transfer"` , `"custom"` | Action when retries run out. | | `exhausted_message` | `string` | Message when retries are exhausted. | | `order` | `"sequence"` , `"random"` | Prompt play order. | | `play_style` | `"looped"` , `"once"` , `"non_looped"` | Whether prompts repeat. | | `messages` | `object[]` | Static re-prompt texts. | | `generation` | `object` | Dynamic prompt generation (`mode: dynamic`): `enabled`, `model_source`, `model_id`, `prompt_ref`, `max_output_tokens`. | **`on_caller_backchannel`** — recognize caller acknowledgements ("uh-huh", "okay") without treating them as a turn: `enabled`, `phrases`, `max_words`. `max_duration_ms` on `on_caller_backchannel` is accepted but has **no effect** — the voice gateway cannot enforce a back-channel duration cap, so back-channels are matched on the phrase list alone. Authoring it now emits a compile warning (`CONVERSATION_BACKCHANNEL_DURATION_CAP_IGNORED`); remove it. **`on_hold`** — handle caller hold requests ("hold on a sec"): `enabled`, `phrases`, `phrases_by_locale`, `grace_ms`, `max_holds`, `suppress_response`. `phrases_by_locale` is a `{ : string[] }` map of additional hold phrases selected by the caller's detected locale; its phrases are unioned with the language-neutral `phrases` list rather than replacing them. Locale keys are canonicalized at parse time (`en_us` → `en-US`). ```yaml expandable=true theme={null} CONVERSATION: listening: on_no_input: enabled: true mode: static timeout_ms: 5000 max_retries: 2 on_exhausted: transfer messages: - text: "Are you still there?" on_caller_backchannel: enabled: true phrases: ["uh-huh", "okay", "go on"] on_hold: enabled: true phrases: ["hold on", "give me a sec"] phrases_by_locale: es-MX: ["un momento", "espérame un segundo"] fr-CA: ["un instant", "attends une seconde"] grace_ms: 15000 ``` `phrases_by_locale` is currently authored only in the `.agent.yaml` format (`CONVERSATION.listening.on_hold.phrases_by_locale`); the legacy `.agent.abl` parser does not yet accept this key. **`barge_in_continuation`** — when a caller barges in over the bot and the speech recognizer segments their continuous speech into multiple phrase-finals (for example "change to 2 years" then, without a real pause, "recalculate the EMI"), hold each eligible final briefly and merge a following one into a single turn instead of committing each phrase as its own turn. | Field | Type | Range | Description | | - | - | - | - | | `enabled` | Boolean | — | Main switch. Defaults to `on` when the block is omitted entirely. | | `window_ms` | Number | 300–2000 | Silence window held after a final before committing it, waiting for a possible continuation. | | `max_hold_ms` | Number | 500–8000 | Absolute ceiling on how long one turn may be held, regardless of how many finals merge into it. | | `max_merges` | Number | 1–20 | Maximum number of finals folded into one committed turn. | | `max_chars` | Number | 1–20000 | Maximum merged-transcript length before forcing a commit. | ```yaml theme={null} CONVERSATION: listening: barge_in: allow barge_in_continuation: enabled: true window_ms: 700 max_hold_ms: 4000 max_merges: 4 max_chars: 2000 ``` `barge_in_continuation` refines what happens to an *already-allowed* interruption's speech segmentation — it's a sibling of, not a replacement for, the scalar `barge_in: allow`/`disallow` policy above, and distinct from the per-output scoped `BARGE_IN` covered in [Scoped barge-in](/agent-platform/abl-reference/flow#scoped-barge-in). A project-level default can be overlaid beneath this per-agent block. ### GATHER Overrides gather behavior. Top-level keys `validation_style` and `confirmation`, plus a `FIELD_OVERRIDES:` block (note the uppercase keyword) that adjusts individual fields. ```yaml theme={null} GATHER: validation_style: lenient confirmation: always FIELD_OVERRIDES: account_id: prompt: "Please say or enter your account number." required: true skip: false validation: "len(account_id) == 10" extraction_hints: ["Read digits individually on voice"] ``` ### FLOW Overrides the agent's flow while the profile is active. | Sub-keyword | Shape | Description | | - | - | - | | `SKIP` | `string[]` | Step names to skip. | | `REPLACE` | `string` | Replace the flow with a named flow. | | `OVERRIDES` | per-step `{ respond?, transition?, VOICE?, RICH_CONTENT? }` | Override specific steps (alias `OVERRIDE`). | | `INSERTIONS` | list of `- BEFORE: ` / `- AFTER: ` + step body | Insert steps relative to existing ones. | ```yaml theme={null} FLOW: SKIP: [upsell_step] OVERRIDES: greeting: respond: "Welcome back — I'll keep this quick." INSERTIONS: - BEFORE: payment REASONING: false RESPOND: "Confirming your VIP discount first." ``` An **inline** `BEHAVIOR_PROFILE:` declared inside an agent file (rather than as a standalone document) supports only `PRIORITY`, `WHEN`, `INSTRUCTIONS`, and `CONVERSATION`. The other blocks (`CONSTRAINTS`, `RESPONSE`, `VOICE`, `TOOLS`, `GATHER`, `FLOW`) are available only in standalone profile files. ## Agent attachment ```yaml theme={null} AGENT: Support_Agent GOAL: "Resolve customer support requests" USE BEHAVIOR_PROFILE: voice_vip ``` The attached profile name must match the `BEHAVIOR_PROFILE:` header, not just the filename. Both `USE BEHAVIOR_PROFILE:` (space) and `USE_BEHAVIOR_PROFILE:` (underscore) forms are accepted. ## Compile and import behavior During import preview, the platform: 1. Reads `project.json` and `abl.lock`. 2. Categorizes `behavior_profiles/.behavior_profile.abl` as part of the `core` layer. 3. Parses the profile with the canonical ABL parser. 4. Compiles profiles before agents. 5. Resolves every `USE BEHAVIOR_PROFILE` reference against compiled profile names. 6. Reports missing or malformed profiles as import/compiler issues. Common fixes: | Symptom | Fix | | - | - | | `Unknown layer "behavior_profiles" in layers_included` | Remove it from `layers_included`; keep `core` and the manifest map. | | Manifest has `behavior_profiles` but preview adds none | Include the referenced `behavior_profiles/.behavior_profile.abl` file. | | `Missing BEHAVIOR_PROFILE: header` | Start the profile file with `BEHAVIOR_PROFILE: `. | | `BEHAVIOR_PROFILE requires a PRIORITY declaration` | Add `PRIORITY: `. | | `BEHAVIOR_PROFILE requires a WHEN declaration` | Add `WHEN: `, or `WHEN: true` for a default profile. | | Agent references a missing `profile_use` target | Add the standalone profile file or remove the `USE` reference. | # Data types and utilities Source: https://koreai.mintlify.app/agent-platform/abl/reference/data-types-and-utilities This page documents the ABL type system, lookup tables for reference-based validation, and attachment handling for file and media uploads. *** ## Data types ABL has a type system used for variable declarations, gather field types, tool parameter signatures, and runtime validation. Types are divided into primitives and complex types. ### Primitive types Primitive types represent single scalar values. | Type | Description | Example values | | - | - | - | | `string` | Unicode text of arbitrary length. | `"hello"`, `"USD"`, `""` | | `number` | IEEE 754 double-precision floating point. | `42`, `3.14`, `-1`, `0` | | `boolean` | True or false. | `true`, `false` | | `date` | Calendar date without time. | `"2024-03-15"` | | `datetime` | Date with time and timezone. | `"2024-03-15T10:30:00Z"` | #### String Strings are the most common type in ABL. They are used for text input, identifiers, messages, and any unstructured data. ```yaml theme={null} GATHER: customer_name: prompt: "What is your name?" type: string required: true ``` #### Number Numbers represent all numeric values, including integers and floating-point numbers. ABL does not distinguish between integer and float types. ```yaml theme={null} GATHER: amount: prompt: "How much would you like to transfer?" type: number required: true validate: min(1) ``` #### Boolean Booleans represent true/false values. In conditions, the following values are treated as falsy: `false`, `0`, `""`, `"false"`, `null`, `undefined`, empty arrays, and empty objects. Everything else is truthy. ```yaml theme={null} MEMORY: session: - customer_verified TYPE: boolean INITIAL: false ``` #### Date Dates represent calendar dates without a time component. They're stored as ISO 8601 format (`YYYY-MM-DD`). ```yaml theme={null} GATHER: departure_date: prompt: "When would you like to depart?" type: date required: true ``` #### Datetime Datetimes include both date and time with timezone information. They're stored in ISO 8601 format. ```yaml theme={null} MEMORY: session: - session_start_time TYPE: datetime INITIAL: NOW() ``` ### Complex types Complex types represent structured or composite values. #### array\ An ordered collection of elements, optionally typed by item type. ```yaml theme={null} # In type definitions type: array # Array of strings type: array # Array of numbers type: array # Array of objects ``` In memory declarations: ```yaml theme={null} MEMORY: session: - cart_items TYPE: array INITIAL: [] ``` ##### Array type definition (IR) ```yaml theme={null} { kind: 'array', itemType: TypeDefinition } ``` | Property | Type | Description | | - | - | - | | `kind` | `'array'` | Discriminant for array type. | | `itemType` | `TypeDefinition` | Type of each element in the array. | #### object `<{...}>` A structured record with named, typed fields. ```yaml theme={null} # In type definitions type: object<{name: string, age: number, active: boolean}> ``` In tool return types: ```yaml theme={null} TOOLS: get_user(id: string) -> {name: string, email: string, role: string} ``` ##### Object type definition (IR) ```yaml theme={null} { kind: 'object', properties: Record } ``` | Property | Type | Description | | - | - | - | | `kind` | `'object'` | Discriminant for object type. | | `properties` | `Record` | Map of field names to their types. | #### enum\<\[...]> A constrained set of allowed string values. ```yaml theme={null} GATHER: transfer_type: prompt: "Is this domestic or international?" type: string validate: enum(domestic, international) ``` ##### Enum type definition (IR) ```yaml theme={null} { kind: 'enum', values: string[] } ``` | Property | Type | Description | | - | - | - | | `kind` | `'enum'` | Discriminant for enum type. | | `values` | `string[]` | Allowed values. | #### union\<\[...]> A value that can be one of several types. Useful for fields that accept different formats. ```yaml theme={null} # In type definitions type: union<[string, number]> # Either a string or a number ``` ##### Union type definition (IR) ```yaml theme={null} { kind: 'union', types: TypeDefinition[] } ``` | Property | Type | Description | | - | - | - | | `kind` | `'union'` | Discriminant for union type. | | `types` | `TypeDefinition[]` | Possible types for the value. | #### nullable\ Wraps another type to indicate it can also be `null`. ```yaml theme={null} # In type definitions type: nullable # String or null type: nullable # Number or null ``` ##### Nullable type definition (IR) ```yaml theme={null} { kind: 'nullable', innerType: TypeDefinition } ``` | Property | Type | Description | | - | - | - | | `kind` | `'nullable'` | Discriminant for nullable type. | | `innerType` | `TypeDefinition` | The underlying type that can also be null. | ### Type definitions in ABL contexts #### Variable declarations In `MEMORY:` session and persistent variable declarations, use the `TYPE` property: ```yaml theme={null} MEMORY: session: - order_total TYPE: number INITIAL: 0 - items TYPE: array - customer_profile TYPE: object ``` #### Gather fields Gather fields use the `type` property with primitive type names: ```yaml expandable=true theme={null} GATHER: name: prompt: "What is your name?" type: string required: true age: prompt: "How old are you?" type: number required: true departure_date: prompt: "When do you depart?" type: date required: true ``` #### Tool parameters Tool parameters declare types inline in the signature: ```yaml theme={null} TOOLS: search_flights(origin: string, destination: string, date: date, passengers: number) -> {flights: {id: string, price: number, departure: datetime}[], total: number} ``` Supported parameter types: | Type | Description | | - | - | | `string` | Text parameter. | | `number` | Numeric parameter. | | `boolean` | Boolean parameter. | | `date` | Date parameter. | | `datetime` | Datetime parameter. | | `object` | Structured object. | | `string[]` | Array of strings. | | `number[]` | Array of numbers. | #### Tool return types Tool return types use object literal notation with field names and types: ```yaml theme={null} -> {field1: type1, field2: type2, nested: {sub_field: type3}[]} ``` The `ToolReturn` structure supports nested objects and arrays: | Property | Type | Description | | - | - | - | | `type` | `string` | Type name (`string`, `number`, `object`, etc.). | | `fields` | `Record` | Sub-fields (for `object` type). | | `items` | `ToolReturn` | Item type (for array types). | | `optional` | `boolean` | Whether this field is optional in the response. | ### Variable sources Variables in ABL have a source that indicates their origin: | Source | Description | | - | - | | `system` | Set by the runtime (for example, session ID, timestamp). | | `user` | Set by user input (gather fields, direct input). | | `agent` | Set by agent logic (SET assignments, tool results). | | `computed` | Derived from other variables via expressions. | ```yaml theme={null} STATE: user: language: type: string source: user system: session_id: type: string source: system ``` ### Type coercion at runtime The runtime applies type coercion in these contexts: | Context | Behavior | | - | - | | Comparison | Strings are parsed to numbers for numeric comparisons. Booleans become 0/1. | | Assignment | Values are stored as-is. No implicit conversion on write. | | Function calls | Math functions coerce string arguments to numbers. String functions coerce null to `""`. | | Tool parameters | Values are coerced to match the declared parameter type before sending. | | Gather validation | User input is validated and coerced to the declared gather field type. | For detailed coercion rules in expression evaluation, see [Expressions & functions -- Type coercion rules](/agent-platform/abl-reference/expressions-and-functions#type-coercion-rules). ### Complete TypeDefinition reference The full `TypeDefinition` type is a union of primitive type names and complex type objects: ```yaml expandable=true theme={null} TypeDefinition = | 'string' | 'number' | 'boolean' | 'date' | 'datetime' | { kind: 'array', itemType: TypeDefinition } | { kind: 'object', properties: Record } | { kind: 'enum', values: string[] } | { kind: 'union', types: TypeDefinition[] } | { kind: 'nullable', innerType: TypeDefinition } ``` `TypeDefinition` is the **structural** type algebra used for `MEMORY:` variables and tool signatures. GATHER fields and [`ENTITIES:`](/agent-platform/abl-reference/nlu) entries use a broader **entity type** set that additionally includes `text`, `free_text`, `integer`, `float`, `currency`, `email`, `phone`, `enum`, `pattern`, and `location`. Those extra type names are not part of `TypeDefinition`. *** ## Lookup tables Lookup tables provide reference-based validation for gather fields and expressions. They define sets of valid values that the runtime uses to validate user input, suggest corrections, and perform fuzzy matching. The `LOOKUP_TABLES:` block declares named tables with their data source and matching configuration. ### Overview ABL supports three lookup table sources: * **Inline** -- static values defined directly in the ABL file. * **Collection** -- values stored in a tenant-scoped database collection. * **API** -- values fetched from an external HTTP endpoint. ```yaml expandable=true theme={null} LOOKUP_TABLES: airport_codes: source: inline values: [JFK, LAX, ORD, SFO, MIA, ATL, DFW, SEA] case_sensitive: false fuzzy_match: true fuzzy_threshold: 0.85 hotel_chains: source: collection table_name: "hotel_chains" field: "name" case_sensitive: false fuzzy_match: true fuzzy_threshold: 0.8 currency_codes: source: api endpoint: "https://api.reference.com/v1/currencies" field: "code" timeout_ms: 5000 case_sensitive: true fuzzy_match: false ``` ### Inline lookup tables Inline tables define their values directly in the ABL file. Use them for small, stable reference sets. #### Syntax ```yaml theme={null} LOOKUP_TABLES: transfer_types: source: inline values: [domestic, international] case_sensitive: false fuzzy_match: false ``` #### When to use inline tables * The value set is small (fewer than \~100 entries). * The values rarely change. * No external dependency is acceptable. ### Collection lookup tables Collection tables read values from a tenant-scoped database collection. The platform resolves the `table_name` to the correct storage location based on the current tenant. #### Syntax ```yaml theme={null} LOOKUP_TABLES: product_catalog: source: collection table_name: "products" field: "product_name" case_sensitive: false fuzzy_match: true fuzzy_threshold: 0.85 ``` #### When to use collection tables * The value set is large or changes frequently. * Values are managed through an admin interface or data pipeline. * Different tenants have different valid values. ### API lookup tables API tables fetch values from an external HTTP endpoint at runtime. The response is expected to contain an array of objects; the `field` property specifies which object field to match against. #### Syntax ```yaml expandable=true theme={null} LOOKUP_TABLES: exchange_rates: source: api endpoint: "https://api.rates.example.com/v1/currencies" field: "currency_code" timeout_ms: 3000 case_sensitive: true fuzzy_match: false ``` #### When to use API tables * Values come from a third-party system that maintains its own data. * Real-time accuracy is important (for example, live exchange rates, inventory). * The dataset is too large to store locally. ### Lookup table properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `source` | `string` | Yes | `inline` | Data source type: `inline`, `collection`, or `api`. Legacy aliases: `mongodb` → `collection`, `http` → `api`. | | `values` | `string[]` | No | -- | Static values (required for `source: inline`). | | `map` | `Record` | No | -- | Inline key → value mapping (an alternative to `values`; keys become the valid values). | | `table_name` | `string` | No | -- | Logical table name (required for `source: collection`). Alias: `collection`. | | `endpoint` | `string` | No | -- | HTTP endpoint URL (required for `source: api`). | | `field` | `string` | No | -- | Field within the data source to match against. | | `headers` | `Record` | No | -- | Custom HTTP headers for `source: api` (for example, an `Authorization` header). | | `timeout_ms` | `number` | No | `5000` | Request timeout in milliseconds (for `source: api`). | | `case_sensitive` | `boolean` | No | `false` | Whether matching is case-sensitive. | | `fuzzy_match` | `boolean` | No | `false` | Whether fuzzy (approximate) matching is enabled. | | `fuzzy_threshold` | `number` | No | `0.85` | Minimum similarity score (0.0--1.0) for fuzzy matches to be accepted. | Table names must match `^[a-z_][a-z0-9_]*$` and field names `^[a-zA-Z_][a-zA-Z0-9_.]*$`. A small inline map table can also be written in shorthand: `my_table: { key1: value1, key2: value2 }`. ### Fuzzy matching When `fuzzy_match: true`, the runtime uses string similarity algorithms to find the closest match when an exact match is not found. This handles typos, abbreviations, and minor variations. #### Similarity threshold The `fuzzy_threshold` controls how similar a user's input must be to a valid value for the match to be accepted: | Threshold | Behavior | | - | - | | `0.95` | Very strict. Only near-exact matches accepted. | | `0.85` | Default. Tolerates minor typos and abbreviations. | | `0.75` | Lenient. Accepts more variation but increases false positive risk. | | `0.50` | Very lenient. Useful for short strings where small edits are significant. | #### Example: fuzzy matching for airport codes ```yaml theme={null} LOOKUP_TABLES: airports: source: inline values: [JFK, LAX, ORD, SFO, MIA, ATL, DFW, SEA] case_sensitive: false fuzzy_match: true fuzzy_threshold: 0.8 ``` With this configuration: * `"jfk"` matches `JFK` (case-insensitive exact match). * `"SFP"` matches `SFO` (fuzzy match, 1 character difference). * `"XYZ"` doesn't match any value (below threshold). ### Field mapping The `field` property specifies which field in the data source to match with. For collection and API sources, the data is typically an array of objects. The `field` tells the runtime which property to compare: ```yaml theme={null} # Given API response: [{"code": "USD", "name": "US Dollar"}, {"code": "EUR", "name": "Euro"}] LOOKUP_TABLES: currencies: source: api endpoint: "https://api.reference.com/v1/currencies" field: "code" case_sensitive: true fuzzy_match: false ``` User input is matched with the `code` field of each object in the response. ### Using lookup tables A GATHER field references a lookup table through its `semantics.lookup` metadata, which names the table used to validate and normalize the collected value (applying the table's case-sensitivity, fuzzy matching, and threshold): ```yaml theme={null} GATHER: destination_airport: prompt: "What's your destination airport code?" type: string required: true semantics: lookup: airports ``` Tool parameters can source a value from a lookup table via `SOURCE: lookup()` in a `.tools.abl` parameter block (see [Tools](/agent-platform/abl-reference/tools)); this resolves to a `{{lookup:
:}}` value at runtime. ### Timeout and error handling For API-sourced tables, specify a `timeout_ms` to control how long the runtime waits for the external service: ```yaml theme={null} LOOKUP_TABLES: products: source: api endpoint: "https://catalog.internal/api/v1/products" field: "sku" timeout_ms: 3000 case_sensitive: true fuzzy_match: false ``` If the API call times out or returns an error, the runtime: 1. Logs a warning. 2. Falls back to accepting the user's input without validation. 3. Emits a trace event indicating the lookup failure. *** ## Attachments Attachments define file and media upload fields that the agent collects from the user. The `ATTACHMENTS:` block declares named attachment fields with their content category, processing options, and validation constraints. Attachments work alongside [GATHER](/agent-platform/abl-reference/gather) fields to collect structured data through conversation. ### Overview ABL supports four attachment categories, each with specialized processing capabilities: | Category | Use cases | Processing features | | - | - | - | | `document` | PDFs, Word docs, spreadsheets, text files | OCR, text extraction | | `image` | Photos, screenshots, scanned documents | OCR, image analysis | | `audio` | Voice recordings, audio messages | Transcription | | `video` | Screen recordings, instructional clips | Keyframe extraction, transcription | ```yaml expandable=true theme={null} ATTACHMENTS: id_document: prompt: "Please upload a photo of your government-issued ID." category: image required: true max_file_size_mb: 10 allowed_types: [image/jpeg, image/png, image/webp] ocr_enabled: true supporting_docs: prompt: "Upload any supporting documents (invoices, contracts, etc.)." category: document required: false max_file_size_mb: 25 allowed_types: [application/pdf, image/jpeg, image/png] ocr_enabled: true ``` ### Attachment field properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `name` | `string` | Yes | -- | Unique identifier for the attachment field (the YAML key). | | `prompt` | `string` | Yes | -- | Message displayed to the user when requesting the upload. | | `category` | `string` | No | `document` | Content category: `document`, `image`, `audio`, or `video`. | | `required` | `boolean` | No | `true` | Whether the attachment is mandatory. | | `max_file_size_mb` | `number` | No | -- | Maximum file size in megabytes. Alias: `max_size_mb`. | | `allowed_types` | `string[]` | No | -- | Allowed MIME types. If omitted, all types for the category are accepted. | | `ocr_enabled` | `boolean` | No | `false` | Enable OCR text extraction for `document` and `image` categories. | | `transcription_enabled` | `boolean` | No | `false` | Enable audio transcription for `audio` and `video` categories. Alias: `transcription`. | | `key_frame_extraction` | `boolean` | No | `false` | Enable keyframe extraction for `video` category. | The DSL keyword for the MIME allowlist is **`allowed_types`** (it populates the field's allowed MIME types). `allowed_mime_types` is not recognized by the `.agent.abl` parser. ### Document attachments Document attachments handle file uploads such as PDFs, Word documents, spreadsheets, and plain text files. When OCR is enabled, the runtime extracts text content from the uploaded document and makes it available in the session context. ```yaml theme={null} ATTACHMENTS: bank_statement: prompt: "Please upload your most recent bank statement." category: document required: true max_file_size_mb: 25 allowed_types: [application/pdf] ocr_enabled: true ``` #### Common document MIME types | MIME type | Description | | - | - | | `application/pdf` | PDF documents | | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | Word (.docx) | | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | Excel (.xlsx) | | `text/plain` | Plain text | | `text/csv` | CSV files | ### Image attachments Image attachments handle photo and screenshot uploads. OCR extracts text from images (useful for scanned documents, receipts, and ID cards). ```yaml theme={null} ATTACHMENTS: receipt_photo: prompt: "Take a photo of your receipt or upload an image." category: image required: true max_file_size_mb: 10 allowed_types: [image/jpeg, image/png, image/webp, image/heic] ocr_enabled: true ``` #### Common image MIME types | MIME type | Description | | - | - | | `image/jpeg` | JPEG images | | `image/png` | PNG images | | `image/webp` | WebP images | | `image/heic` | HEIC images (iOS) | | `image/gif` | GIF images | | `image/tiff` | TIFF images | ### Audio attachments Audio attachments handle voice recordings and audio messages. When transcription is enabled, the runtime converts the audio to text. ```yaml theme={null} ATTACHMENTS: voice_message: prompt: "Record or upload a voice message describing the issue." category: audio required: false max_file_size_mb: 50 allowed_types: [audio/mpeg, audio/wav, audio/ogg, audio/webm] transcription_enabled: true ``` #### Common audio MIME types | MIME type | Description | | - | - | | `audio/mpeg` | MP3 files | | `audio/wav` | WAV files | | `audio/ogg` | OGG audio | | `audio/webm` | WebM audio | | `audio/mp4` | AAC/M4A files | ### Video attachments Video attachments handle video file uploads. Two processing features are available: * **Transcription** extracts speech from the video's audio track. * **Keyframe extraction** captures representative frames from the video for visual analysis. ```yaml expandable=true theme={null} ATTACHMENTS: screen_recording: prompt: "Upload a screen recording showing the issue." category: video required: false max_file_size_mb: 100 allowed_types: [video/mp4, video/webm, video/quicktime] transcription_enabled: true key_frame_extraction: true ``` #### Common video MIME types | MIME type | Description | | - | - | | `video/mp4` | MP4 video | | `video/webm` | WebM video | | `video/quicktime` | QuickTime (MOV) | ### OCR processing When `ocr_enabled: true`, the runtime processes uploaded documents and images through an OCR pipeline that: 1. Detects text regions in the file. 2. Extracts text content. 3. Stores the extracted text in the session context under the attachment field name. The extracted text is available in expressions and template strings: ```yaml theme={null} # After OCR, the text is available as the attachment field name SET: id_text = id_document.extracted_text ``` ### Transcription processing When `transcription_enabled: true`, the runtime processes audio and video files through a transcription pipeline: 1. Extracts the audio track (for video files). 2. Runs speech-to-text transcription. 3. Stores the transcript in the session context. ```yaml theme={null} SET: message_text = voice_message.transcript ``` ### Keyframe extraction When `key_frame_extraction: true`, the runtime extracts representative frames from video files: 1. Analyzes the video for scene changes. 2. Captures keyframes at scene boundaries. 3. Stores the keyframes as an array of image references. ```yaml theme={null} SET: frames = screen_recording.keyframes ``` ### File size and MIME type validation The runtime validates uploaded files against the declared constraints before processing: * If the file exceeds `max_file_size_mb`, the upload is rejected with an error message. * If the file's MIME type is not in `allowed_types`, the upload is rejected. * If `allowed_types` is omitted, all standard MIME types for the category are accepted. ### Complete attachment example ```yaml expandable=true theme={null} ATTACHMENTS: government_id: prompt: "Please upload a clear photo of your government-issued ID (passport, driver's license, or national ID card)." category: image required: true max_file_size_mb: 10 allowed_types: [image/jpeg, image/png, image/webp] ocr_enabled: true proof_of_address: prompt: "Upload a utility bill or bank statement from the last 3 months as proof of address." category: document required: true max_file_size_mb: 25 allowed_types: [application/pdf, image/jpeg, image/png] ocr_enabled: true voice_authorization: prompt: "Record a voice message stating: 'I authorize this wire transfer of {{amount}} {{currency}} to {{beneficiary_name}}.'" category: audio required: false max_file_size_mb: 10 allowed_types: [audio/mpeg, audio/wav, audio/webm] transcription_enabled: true ``` *** ## Destinations The `DESTINATIONS:` block declares named **outbound HTTP targets** for delivering data or notifications to external systems. Each destination has a name (the entry key), a `url`, and optional method/auth/headers. ```yaml expandable=true theme={null} DESTINATIONS: doc_processor: url: "https://api.docprocessor.com/ingest" method: POST auth: bearer_token headers: X-Source: "abl-agent" Authorization: "Bearer {{config.PARTNER_API_KEY}}" archive: url: "https://archive.example.com/upload" method: PUT ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `name` | `string` | Yes | -- | Destination identifier (the entry key). | | `url` | `string` | Yes | -- | Outbound URL. Validated for SSRF at compile time. | | `method` | `string` | No | -- | HTTP method (`GET`, `POST`, `PUT`, …), upper-cased by the parser. | | `auth` | `string` | No | -- | Auth strategy reference (resolved from project config — no secrets in ABL). | | `headers` | `Record` | No | -- | Custom HTTP headers. Supports `{{config.X}}` placeholders. | *** **Related articles:** * [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions) -- type coercion in expressions and built-in type-checking functions * [Memory and Constraints](/agent-platform/abl-reference/memory-and-constraints) -- TYPE declarations in session and persistent variables, attachment data in session * [GATHER](/agent-platform/abl-reference/gather) -- gather field types and lookup-based validation * [NLU](/agent-platform/abl-reference/nlu) -- entity extraction that may reference lookup tables # Expressions and functions Source: https://koreai.mintlify.app/agent-platform/abl/reference/expressions-and-functions This page documents ABL's expression language: operators, variable paths, `{{}}` template-string interpolation, the 43 built-in functions, and type coercion rules. Expressions are used in conditions (`WHEN`, `CHECK`, constraint rules), value assignments (`SET`), template interpolation (`{{}}`), and function calls. For rich content output formats (voice, cards, carousels, interactive actions, templates), see [Rich content](/agent-platform/abl-reference/rich-content). *** ## Expression syntax ### Comparison operators | Operator | Syntax | Description | | - | - | - | | `==` | `a == b` | Equal to. | | `!=` | `a != b` | Not equal to. | | `>` | `a > b` | Greater than. | | `<` | `a < b` | Less than. | | `>=` | `a >= b` | Greater than or equal to. | | `<=` | `a <= b` | Less than or equal to. | | `in` | `a IN [x,y,z]` | Value is in the list. | | `not_in` | `a NOT IN [x]` | Value is not in the list. | | `matches` | `a matches r` | Value matches a regular expression. | | `contains` | `a contains b` | String contains substring, or array contains element. | | `startsWith` | `a startsWith b` | String starts with a prefix. | | `endsWith` | `a endsWith b` | String ends with a suffix. | ### Logical operators | Operator | Syntax | Description | | - | - | - | | `AND` | `a AND b` | Both conditions must be true. | | `OR` | `a OR b` | At least one must be true. | | `NOT` | `NOT a` | Negates the condition. | Write logical operators in uppercase (`AND`, `OR`, `NOT`). `!` is also accepted as a prefix negation. `contains`, `matches`, `startsWith`, and `endsWith` are infix **operators** (`a contains b`), not callable functions — a condition that calls one as a bare function (`CONTAINS(ani_national, "1234567890")`) parses but has no matching overload, so it can never evaluate true; the compiler warns `CEL_CONDITION_UNRESOLVED_CALL`. Use the infix form, or the `abl.*` CEL method form (`x.contains("y")`). ### Unary operators | Operator | Syntax | Description | | - | - | - | | `NOT` / `!` | `NOT condition` / `!condition` | Negates a condition. | | `IS SET` | `var IS SET` | True if the variable is not null/undefined (empty string counts as set). | | `IS NOT SET` | `var IS NOT SET` | True if the variable is null or undefined. | | `is_number` | `var is_number` | True if the variable is a number. | ### Operator precedence 1. Parentheses `()` 2. Unary operators (`NOT`, `IS SET`, `IS NOT SET`) 3. Comparison operators (`==`, `!=`, `>`, `<`, `>=`, `<=`, `contains`, `matches`) 4. `AND` 5. `OR` Use parentheses to override default precedence: ```yaml theme={null} WHEN: (status == "active" OR status == "pending") AND amount > 0 ``` ### Quoting a condition A `WHEN` value — in `HANDOFF`, `ON_RETURN WHEN`, `ESCALATE` triggers, `COMPLETE`, and hook `WHEN`/`RESPOND` branches — is unwrapped from its quotes and evaluated as a condition whenever it contains a condition operator: `==`, `!=`, `<`, `>`, `&&`, `||`, `!`, `IS [NOT] SET`, `contains`, `matches`, `startsWith`, `endsWith`, `in`, or a function call. So `WHEN: "dispute == true"` and `WHEN: dispute == true` are the same condition. A quoted value with none of these — a natural-language phrase such as `WHEN: "task done"` — stays a quoted literal string, which most deterministic evaluators treat as always-truthy. Write a genuinely deterministic condition either unquoted or quoted-with-an-operator; reserve a bare quoted phrase for constructs that intentionally accept free text (a prompt-only/semantic condition). A deterministic routing or escalation entry's `when` must be an operator expression regardless of quoting — see [Deterministic-only routing](/agent-platform/abl-reference/multi-agent-and-supervisor#conditional-routing-entries). ## Variable paths and dot notation Reference variables using dot notation to access nested values. ```yaml theme={null} user.name # Access nested property user.addresses[0].city # Array index access acctResult.status # Tool result field ``` ### Path resolution rules * The evaluator looks up the full path in the session context. * The Platform resolves each segment left to right: `user.address.city` resolves `user`, then `address` on the result, then `city`. * If any segment resolves to `null` or `undefined`, the entire path resolves to `undefined`. * Array access uses bracket notation: `items[0]`, `items[2].name`. ## Template strings Template strings use `{{}}` syntax for variable interpolation within `RESPOND`, `summary`, and other string properties -- including inside a [`TEMPLATES:` block](/agent-platform/abl-reference/rich-content#templates) body: ```yaml theme={null} RESPOND: "Hello, {{customer_name}}! Your balance is {{available_balance}}." ``` ### Conditional sections Templates support conditional rendering with `{{#if}}...{{/if}}`: ```yaml theme={null} RESPOND: | Transfer complete. {{#if fx_rate}}Exchange rate: {{fx_rate}}{{/if}} Estimated arrival: {{estimated_arrival}}. ``` ### Function calls in templates You can call built-in functions within template strings: ```yaml theme={null} SET: formatted_balance = FORMAT_CURRENCY(available_balance, "USD") RESPOND: "Your balance is {{formatted_balance}}." ``` ## Built-in function reference ABL provides 36 built-in functions organized into seven categories (plus the CEL-only content-analysis helpers described below). All functions are called with `FUNCTION_NAME(arg1, arg2, ...)` syntax. Function names are **uppercase**. ### Math functions | Function | Signature | Description | Example | | - | - | - | - | | `ADD` | `ADD(a, b) -> number` | Add two numbers. | `ADD(2, 3)` returns `5` | | `SUB` | `SUB(a, b) -> number` | Subtract b from a. | `SUB(10, 3)` returns `7` | | `MUL` | `MUL(a, b) -> number` | Multiply two numbers. | `MUL(4, 5)` returns `20` | | `DIV` | `DIV(a, b) -> number` | Divide a by b. Returns `null` for division by zero. | `DIV(10, 2)` returns `5` | | `ROUND` | `ROUND(n, decimals?) -> number` | Round to specified decimal places. Default: 0. | `ROUND(3.14159, 2)` returns `3.14` | | `ABS` | `ABS(n) -> number` | Absolute value. | `ABS(-5)` returns `5` | | `MIN` | `MIN(a, b) -> number` | Return the smaller of two numbers. | `MIN(3, 7)` returns `3` | | `MAX` | `MAX(a, b) -> number` | Return the larger of two numbers. | `MAX(3, 7)` returns `7` | Math functions coerce string arguments to numbers automatically: `ADD("2", "3")` returns `5`. ### String functions | Function | Signature | Description | Example | | - | - | - | - | | `UPPER` | `UPPER(s) -> string` | Convert to uppercase. | `UPPER("hello")` returns `"HELLO"` | | `LOWER` | `LOWER(s) -> string` | Convert to lowercase. | `LOWER("HELLO")` returns `"hello"` | | `TRIM` | `TRIM(s) -> string` | Strip leading/trailing whitespace. | `TRIM(" hi ")` returns `"hi"` | | `SUBSTRING` | `SUBSTRING(s, start, end?) -> string` | Extract a substring. | `SUBSTRING("hello", 0, 3)` returns `"hel"` | | `REPLACE` | `REPLACE(s, find, repl) -> string` | Replace all occurrences. | `REPLACE("a,b,c", ",", "-")` returns `"a-b-c"` | | `SPLIT` | `SPLIT(s, delimiter) -> string[]` | Split string into array. | `SPLIT("a,b,c", ",")` returns `["a","b","c"]` | | `JOIN` | `JOIN(arr, delimiter) -> string` | Join array into string. | `JOIN(["a","b"], ", ")` returns `"a, b"` | | `PAD_START` | `PAD_START(s, length, char?) -> string` | Left-pad to specified length. | `PAD_START("42", 6, "0")` returns `"000042"` | | `PAD_END` | `PAD_END(s, length, char?) -> string` | Right-pad to specified length. | `PAD_END("42", 6, "0")` returns `"420000"` | | `REPEAT` | `REPEAT(s, count) -> string` | Repeat string N times. | `REPEAT("*", 4)` returns `"****"` | | `REGEX_REPLACE` | `REGEX_REPLACE(text, pattern, replacement) -> string` | RE2-backed replace-all; `$1`-`$9` in `replacement` insert capture groups. Matching is single-pass, so `^`/`\b` anchor against the *original* string, not partial replacement output. Returns `text` unchanged on an invalid pattern — never throws. | `REGEX_REPLACE("00123", "^0", "")` returns `"0123"` | | `REGEX_EXTRACT` | `REGEX_EXTRACT(text, pattern, group?) -> string \| null` | First match, or capture group `group` when given. Returns `null` when nothing matches or the pattern is invalid — never throws. | `REGEX_EXTRACT("order-4821", "\\d+")` returns `"4821"` | The string functions gracefully handle `null` and `undefined` and return an empty string: `UPPER(null)` returns `""`. To prevent memory issues, the limit on string output is 100,000 characters. ### Identity-matching functions These compare what a caller actually said against a stored value — they are deterministic similarity scoring, not a semantic/model judgment, built for voice identity verification (see [Write authority](/agent-platform/abl-reference/memory-and-constraints#write-authority) for a `READONLY` field that stores the result). | Function | Signature | Description | | - | - | - | | `MATCH_SCORE` | `MATCH_SCORE(said, stored, kind?) -> number` | Similarity 0-100 between `said` and `stored`. Returns `-1` when there's nothing to judge — a missing/empty value, a PII vault token, or an input over 1,024 characters. `-1` is below every threshold, so `MATCH_SCORE(...) >= 70` fails closed. | | `MATCHES` | `MATCHES(said, stored, kind?) -> boolean` | Pass/fail form of `MATCH_SCORE` on the platform's own match bands (below). **Not a regex test** — the infix `a MATCHES "pattern"` operator (see [Comparison operators](#comparison-operators)) is the regex form. | | `MATCH_RESULT` | `MATCH_RESULT(said, stored, kind?) -> string` | `"match"`, `"retry"` (a tight near-miss only), `"mismatch"`, or `"missing"`. | `kind` is optional — omitted, it defaults to `text` (unchanged behavior, no warning) — and otherwise one of: | `kind` | Meaning | | - | - | | `name` / `person_name` | A personal name, scored part-by-part on how it sounds. | | `email` | An email address, read from spoken forms ("at the rate", "dot", spelled letters). | | `address` | A street address, read part by part in any order. | | `phone` / `digits` | Digits only, with spoken-digit and trunk/country-code handling (`phone`), or an exact digit-for-digit identifier like a member number (`digits` — no national-number truncation). | | `text` | Plain text similarity (the default). | | `auto` | Detects the kind from the **stored** value's shape: an `@` with a dotted domain → `email`; mostly digits with 7-10 digits or a leading `+` → `phone`; digits only, under 7 or 11+ with no `+` → `digits`; a house-number-plus-street-type pattern → `address`; 2-4 capitalized words → `person_name`; otherwise `text`. An explicit kind (including `"text"`) is never auto-detected — `"auto"` only applies when you pass it. | | `exact` | Turns off every voice allowance: matches only after Unicode normalization, trimming, case-folding, and collapsing spaces/trailing punctuation — "Steven" does not match "Stephen". | Platform-owned match bands (authors never pick a threshold): | Kind | `match` | `retry` | `mismatch` | | - | - | - | - | | `phone`, `digits` | every digit matches (100) | exactly one digit slip, or two neighbouring digits swapped (50) | anything else | | `email`, `name`/`person_name`, `address`, `text` | score ≥ 70 | score 50-69 (a tight near-miss only) | score \< 50 | A `-1` score (nothing to judge) always reports `"missing"`. The guiding principle: values that *sound* the same cannot be told apart by a caller (speech-to-text just picks a spelling), so they score as a `"match"` — only values that sound genuinely different can fail. For example `email` compares the domain by sound but matches its ending exactly (`kore.ai` is not `kore.com`); `person_name` matches common respellings (`Jon`/`John`, `Steven`/`Stephen`) and scores a near-same name (`Ahmed`/`Ahmad`) as `"retry"`, never a straight mismatch, while a genuinely different name (`Mark`/`Mary`) is a `"mismatch"`; `address` reads house/unit letters and matches street words that sound alike. The matcher reads the value itself, not the sentence around it — filler words ("no", "sorry", "I mean") are not stripped, so an answer that contains them alongside the correct value still fails to match as written. `said` must be the extracted field (for example `supplied_email`), not the raw caller utterance. An extracted field is written by the model and may already hold the stored value (for example, after a lookup tool returned the profile) — a match on the field alone does not prove the caller actually said it. A rule that grants access on a match should also confirm the caller's own words contained the value. The **function** `MATCHES(said, stored[, kind])` and the **infix operator** `a MATCHES "pattern"` are unrelated — do not mix up their second argument. Writing `MATCHES(supplied, "^gold$")` (the function form with a regex-shaped literal) is almost certainly meant to be the infix form `supplied MATCHES "^gold$"`, and the compiler warns `MATCHES_FUNCTION_REGEX_LITERAL`. Conversely, writing `tier MATCHES "gold"` (the infix form with a plain literal) is true whenever `"gold"` appears anywhere in `tier` as a substring — not a fuzzy spoken-answer comparison — and the compiler warns `MATCHES_INFIX_LITERAL_AMBIGUOUS` since this is easy to mean the other way. ### Formatting functions | Function | Signature | Description | Example | | - | - | - | - | | `MASK` | `MASK(s, pattern, char?) -> string` | Mask a string. See Mask Patterns below. | `MASK("4111111111111111", "last4")` returns `"************1111"` | | `FORMAT_CURRENCY` | `FORMAT_CURRENCY(n, currency, locale?) -> string` | Format a number as currency. | `FORMAT_CURRENCY(1234.5, "USD")` returns `"$1,234.50"` | | `FORMAT_DATE` | `FORMAT_DATE(d, format, tz?) -> string` | Format a date string. | `FORMAT_DATE("2024-03-15T10:30:00Z", "YYYY-MM-DD")` returns `"2024-03-15"` | | `ORDINAL` | `ORDINAL(n) -> string` | Convert number to ordinal string. | `ORDINAL(1)` returns `"1st"`, `ORDINAL(22)` returns `"22nd"` | The mask patterns are explained below. | Pattern | Behavior | Example result | | - | - | - | | `last4` | Mask all characters except the last 4. | `"************1111"` | | `first4` | Mask all characters except the first 4. | `"4111************"` | | `N*N` | Show N chars at start, mask middle, show N at end. | `MASK(s, "4*4")` shows 4+4 | The default mask character is `*`. Pass a third argument to use a different character: `MASK(ssn, "last4", "x")`. The date format uses the following notations: | Token | Description | Example | | - | - | - | | `YYYY` | Four-digit year | `2024` | | `MM` | Two-digit month | `03` | | `DD` | Two-digit day | `15` | | `HH` | Two-digit hour (24h) | `10` | | `mm` | Two-digit minute | `30` | | `ss` | Two-digit second | `00` | Example: `FORMAT_DATE("2024-03-15T10:30:00Z", "YYYY-MM-DD HH:mm")` returns `"2024-03-15 10:30"`. `FORMAT_DATE` formats in **UTC by default** — a date-time string without an explicit offset is interpreted as UTC, so the output is deterministic regardless of where the runtime executes. Pass an optional IANA timezone name as the third argument to format in that zone instead: ``` FORMAT_DATE("2024-03-15T10:30:00Z", "YYYY-MM-DD HH:mm", "America/New_York") # "2024-03-15 06:30" ``` An unrecognized timezone falls back to UTC. ### Type checking and coercion functions | Function | Signature | Description | Example | | - | - | - | - | | `IS_ARRAY` | `IS_ARRAY(x) -> boolean` | True if the value is an array. | `IS_ARRAY([1,2])` returns `true` | | `IS_NUMBER` | `IS_NUMBER(x) -> boolean` | True if the value is a number (not NaN). | `IS_NUMBER(42)` returns `true` | | `IS_STRING` | `IS_STRING(x) -> boolean` | True if the value is a string. | `IS_STRING("hi")` returns `true` | | `TO_NUMBER` | `TO_NUMBER(x) -> number` | null | Convert to number. Returns `null` if conversion fails. `TO_NUMBER("42")` returns `42` | | `TO_STRING` | `TO_STRING(x) -> string` | Convert to string. Returns `""` for null/undefined. | `TO_STRING(42)` returns `"42"` | ### Array functions | Function | Signature | Description | Example | | - | - | - | - | | `LENGTH` | `LENGTH(x) -> number` | Array length or string length. Returns 0 for other types. | `LENGTH([1,2,3])` returns `3` | | `ARRAY_FIND` | `ARRAY_FIND(arr, field, value) -> object` | null | Find first element where `field == value`. `ARRAY_FIND(users, "id", 42)` returns the matching object | | `ARRAY_FIND_INDEX` | `ARRAY_FIND_INDEX(arr, field, value) -> number` | Find index of first match. Returns -1 if not found. | `ARRAY_FIND_INDEX(items, "type", "b")` returns the index | ### Object functions | Function | Signature | Description | Example | | - | - | - | - | | `OBJECT_KEYS` | `OBJECT_KEYS(obj) -> string[]` | Return array of object keys. | `OBJECT_KEYS({a:1, b:2})` returns `["a","b"]` | | `OBJECT_VALUES` | `OBJECT_VALUES(obj) -> any[]` | Return array of object values. | `OBJECT_VALUES({a:1, b:2})` returns `[1, 2]` | | `OBJECT_MERGE` | `OBJECT_MERGE(obj1, obj2, ...) -> object` | Merge objects. Right-side values win on conflict. | `OBJECT_MERGE({a:1}, {b:2})` returns `{a:1, b:2}` | `OBJECT_KEYS` and `OBJECT_VALUES` return an empty array for non-object inputs (null, arrays, strings). `OBJECT_MERGE` accepts any number of arguments and skips non-object values. ### Utility functions | Function | Signature | Description | Example | | - | - | - | - | | `COALESCE` | `COALESCE(a, b, ...) -> any` | Return the first non-null, non-undefined value. Returns `null` if all arguments are null. | `COALESCE(null, undefined, "hello")` returns `"hello"` | | `FIRST_NON_EMPTY` | `FIRST_NON_EMPTY(a, b, ...) -> any` | Return the first value that is not null, undefined, `""`, or blank. Unlike `COALESCE`, it skips an empty/blank string. | `FIRST_NON_EMPTY("", null, "hello")` returns `"hello"` | | `IF` | `IF(cond, a, b) -> any` | Eager conditional — both `a` and `b` are evaluated regardless of `cond`. Use the ternary `cond ? a : b` for lazy evaluation. | `IF(amount > 100, "high", "low")` | | `NOW` | `NOW() -> string` | Return the current timestamp as an ISO 8601 string. | `NOW()` returns `"2024-03-15T10:30:00.000Z"` | | `UNIQUE_ID` | `UNIQUE_ID(length?) -> string` | Generate a random alphanumeric string. Default length: 6. | `UNIQUE_ID(12)` returns `"aB3kM9pQ2xLw"` | `COALESCE` considers `0` and `false` as valid (non-null) values: `COALESCE(0, "fallback")` returns `0`. `UNIQUE_ID` generates a random alphanumeric string suitable for reference numbers. It is **not** cryptographically secure — do not use it for tokens or secrets. ### Content-analysis functions (CEL guardrails) CEL-based [guardrail](/agent-platform/abl-reference/guardrails) checks have access to an additional `abl.*` namespace of content-analysis helpers: | Function | Description | | - | - | | `abl.contains_pii(text)` | True if the text contains PII. | | `abl.detect_pii(text)` | Structured PII detection result. | | `abl.redact_pii(text)` | Returns the text with PII redacted. | | `abl.matches_pattern(text, pattern)` | Regex match (RE2-backed). | | `abl.not_matches_pattern(text, pattern)` | Negation of `matches_pattern`. | | `abl.word_count(text)` | Word count. | | `abl.sentence_count(text)` | Sentence count. | | `abl.contains_url(text)` | True if the text contains a URL. | | `abl.contains_email(text)` | True if the text contains an email address. | | `abl.contains_code(text)` | True if the text contains a fenced code block. | | `abl.pii_equal(left, right)` | True if two **PII-vault tokens** hold the same underlying value. See below. | In CEL expressions the core functions above are also available under the `abl.` namespace with lowercase names (for example, `abl.upper(x)`), and CEL-native operators (`&&`, `||`, `!`, `%`, `in`, ternary `? :`) and macros (`size()`, `has()`) can be used. `abl.pii_equal(left, right)` compares two values **without ever exposing the underlying plaintext to the expression** — both `left` and `right` must be live PII-vault tokens (the opaque form a PII-classified value takes once captured, for example, via a `sensitive: true` GATHER field), and the comparison happens inside the vault itself. Values are canonicalized before comparing (email lowercased/trimmed, phone/SSN/credit-card digits-only, etc.), so `abl.pii_equal(session.data.values.email, caller_provided_email)` matches `Member@Example.com` against `member@example.com`. ```yaml theme={null} CHECK: abl.pii_equal(session.data.values.email, caller_provided_email) ON_FAIL: identity_mismatch ``` If either operand isn't a live vault token for the same PII type — missing, expired, or a plain string — `abl.pii_equal` **throws** rather than returning `false`. This is deliberate: silently returning `false` on a fault would make `!abl.pii_equal(x, y)` evaluate `true` on a comparison that couldn't actually be performed. Guard against this with `IS SET` checks on both operands before comparing, or route the resulting evaluation error through your usual error handling. ### Nested function calls Functions can be nested as arguments to other functions: ```yaml theme={null} SET: result = ADD(MUL(2, 3), SUB(10, 4)) # Returns 12 name = UPPER(TRIM(" hello ")) # Returns "HELLO" fallback = COALESCE(user.lastName, "Guest") ``` Nesting is limited to a maximum depth of 32 to prevent stack overflow. ## Using expressions in ABL ### In conditions (WHEN, CHECK, constraint rules) ```yaml theme={null} WHEN: user.age >= 18 AND user.verified == true CHECK: amount <= available_balance - REQUIRE sanctions_clear == true ``` ### In SET assignments ```yaml theme={null} SET: balance_formatted = FORMAT_CURRENCY(available_balance, "USD") wire_reference = UNIQUE_ID(12) greeting = COALESCE(user.name, "valued customer") ``` `recent_input` is a built-in available in `SET` contexts: the caller's last 3 utterances, joined. `input_transcript` is a built-in, read-only `SET` value on realtime voice channels — the provider's transcript of the current caller turn (empty when there is none). An authored session variable with either name takes precedence over the built-in. ### In template interpolation ```yaml theme={null} RESPOND: "Hello, {{customer_name}}! Balance: {{FORMAT_CURRENCY(available_balance, 'USD')}}." ``` ### In TRANSFORM pipelines ```yaml theme={null} TRANSFORM: SOURCE: search_results AS: item INTO: filtered_results FILTER: item.price <= budget AND item.available == true MAP: name: item.hotel_name price: FORMAT_CURRENCY(item.price, "USD") SORT_BY: price ASC LIMIT: 5 ``` ## Type coercion rules The evaluator applies these coercion rules during expression evaluation: | Context | Rule | | - | - | | Equality (`==`) | null/undefined == null/undefined is true. Strings compared case-sensitively. Numbers parsed from strings. | | Inequality (`!=`) | If either side is undefined/null, returns true. | | Numeric (`>`, `<`, etc.) | Strings parsed to numbers. Booleans become 0/1. Arrays become length. Undefined becomes 0. | | Truthiness (bare variable) | `false`, `0`, `""`, `"false"`, `null`, `undefined`, empty arrays, empty objects are falsy. Everything else is truthy. | | IS SET / IS NOT SET | Checks for null/undefined only. Empty string IS SET. | | Arithmetic (`+ - * / %`) | Works between a decimal session number and an integer literal, in either order, producing a decimal result (for example `turn_count == followup_offer_turn + 1`). Division or modulus by zero is an error — the condition does not hold — never `Infinity`. | *** **Related articles:** * [Rich content](/agent-platform/abl-reference/rich-content) -- voice configuration, format-specific output, carousels, interactive actions, and templates * [Memory and constraints](/agent-platform/abl-reference/memory-and-constraints) -- condition expressions in constraint rules, variable paths and session context * [Multi-Agent and supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor) -- condition syntax for WHEN clauses in routing/escalation * [Data Types and utilities](/agent-platform/abl-reference/data-types-and-utilities) -- types used in function signatures, lookup validation functions # FLOW (structured execution steps) Source: https://koreai.mintlify.app/agent-platform/abl/reference/flow The `FLOW:` section adds structured execution steps to any agent. It defines a step-by-step execution graph where each step declares actions (collect information, call tools, respond, branch) and transitions to other steps. Agents operate in reasoning mode by default, where the LLM autonomously decides actions based on the goal. Adding a `FLOW:` section gives an agent a structured step graph, with each step declaring whether it uses LLM reasoning or deterministic execution via the `REASONING:` toggle. ## Flow structure ### Basic syntax ```yaml expandable=true theme={null} FLOW: entry_point: start steps: - start - collect_info - process - complete start: REASONING: false SET: status = "pending" RESPOND: "Welcome! Let me help you get started." THEN: collect_info collect_info: REASONING: false GATHER: - name: required prompt: "What is your name?" THEN: process process: REASONING: false CALL: process_request WITH: name: name AS: result RESPOND: "Done! Your request has been processed." THEN: complete complete: REASONING: false RESPOND: "Thank you for using our service. Goodbye!" ``` ### Entry point The `entry_point:` property declares which step the flow begins at: ```yaml theme={null} FLOW: entry_point: greeting ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `entry_point` | `string` | No | First step in the `steps` list | The step name where execution begins | If omitted, execution starts at the first step listed in the `steps` array. ### Step list The `steps:` array declares the ordered list of step names. While steps can transition to any other step (not just the next in order), the list establishes the canonical ordering: ```yaml theme={null} FLOW: steps: - greeting - collect_account - verify - process - complete ``` ### Step definitions Each step is defined as a named block under `FLOW:` with its properties indented: ```yaml theme={null} FLOW: greeting: REASONING: false RESPOND: "Hello! How can I help you today?" THEN: collect_account ``` ### Per-step REASONING toggle Every step in a `FLOW:` section **must** declare `REASONING: true` or `REASONING: false`. This controls whether the step uses LLM reasoning or deterministic execution. ```yaml theme={null} FLOW: analyze_request: REASONING: true GOAL: "Analyze the customer's request and determine the best course of action" AVAILABLE_TOOLS: [search_knowledge, classify_intent] EXIT_WHEN: intent_classified == true MAX_TURNS: 5 THEN: route_request route_request: REASONING: false CHECK: intent == "billing" ON_FAIL: general_support THEN: billing_flow ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `REASONING` | `boolean` | **Yes** | -- | `true` for LLM-driven reasoning, `false` for deterministic execution | #### Reasoning step properties When `REASONING: true`, the following additional properties are available: | Property | Type | Required | Default | Description | | - | - | - | - | - | | `GOAL` | `string` | No | Agent-level `GOAL:` | Step-specific goal (overrides the agent's goal for this step) | | `AVAILABLE_TOOLS` | `string[]` | No | All agent tools | Subset of agent tools available in this step | | `EXIT_WHEN` | `string` | No | *none* | Condition that ends the reasoning loop | | `MAX_TURNS` | `number` | No | `10` | Maximum reasoning turns before forced exit | | `STEP_CONSTRAINTS` | `string[]` | No | *none* | Constraints specific to this reasoning zone | **Validation rules:** * A step with `REASONING: true` must have either a step-level `GOAL` or an agent-level `GOAL:` defined. * A step with `REASONING: false` should not have a `GOAL` (it has no effect on deterministic steps). * A step with `REASONING: false` should not have `AVAILABLE_TOOLS` (use `CALL` to invoke tools deterministically). ### Entry guards The `WHEN:` property on a step defines a condition that must be true for the step to execute. If the condition is false, the step is skipped: ```yaml theme={null} FLOW: international_details: REASONING: false WHEN: "transfer_type == 'international'" GATHER: - swift_code: required prompt: "What is the SWIFT/BIC code?" THEN: validate_details ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `WHEN` | `string` | No | *none* | Condition expression that must be true for the step to execute | ### Attempt limiting Steps can limit the number of times they execute (useful for retry loops): ```yaml theme={null} FLOW: collect_pin: REASONING: false MAX_ATTEMPTS: 3 ON_EXHAUSTED: lockout GATHER: - pin: required prompt: "Enter your PIN." THEN: verify_pin ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `MAX_ATTEMPTS` | `number` | No | *none* | Maximum number of times this step can execute | | `ON_EXHAUSTED` | `string` | No | *none* | Step to transition to after max attempts exhausted | *** ## Step actions ### SAY / RESPOND The `RESPOND:` action sends a message to the user. It supports template interpolation with `{{variable}}` syntax: ```yaml theme={null} FLOW: greeting: REASONING: false RESPOND: "Hello, {{customer_name}}! Your balance is {{balance}}." THEN: next_step ``` Multi-line responses use pipe block syntax: ```yaml theme={null} FLOW: summary: REASONING: false RESPOND: | Here is your transfer summary: From: {{source_account}} To: {{beneficiary_name}} Amount: {{amount}} {{currency}} THEN: confirm ``` #### MODEL\_CONTEXT on a response An optional `MODEL_CONTEXT` sits beside a FLOW step's `RESPOND` (or a `COMPLETE` condition's `RESPOND`) and replaces that response **only in future model context** — it never changes what's actually sent to the user, the stored transcript, or session values: ```yaml theme={null} FLOW: show_products: REASONING: false RESPOND: "{{products_widget}}" MODEL_CONTEXT: "Displayed products {{product_ids}}." THEN: next_step ``` This is useful when the delivered response carries a large presentation payload (a widget, a card, a big table) that would otherwise bloat every later model call for no benefit — `MODEL_CONTEXT` lets the model keep a short textual summary of what the customer saw instead. `MODEL_CONTEXT` is a text scalar; a non-text value fails validation (`MODEL_CONTEXT_INVALID`). An explicitly empty value omits the response from later model requests entirely. Omit the property to keep the historical full-response behavior. See also [MODEL\_CONTEXT on a tool result](/agent-platform/abl-reference/tools#model_context-on-a-tool-result) for the equivalent policy on a tool's own result. A successfully rendered, non-empty `MODEL_CONTEXT` view is **preserved exactly** through later history compaction, summarization, and automatic conversation windowing — it's never auto-shortened, replaced with a summary, or dropped for being old. It's evaluated once, when the response is produced; later turns reuse that same rendered text rather than re-evaluating the template against newer data. This doesn't raise the model's context window or bypass the usual budget: an authored view may exceed the ordinary per-value/aggregate *soft* budget, but must still fit the aggregate *hard* capacity, or the runtime reports `AUTHORED_MODEL_CONTEXT_CAPACITY_EXCEEDED` rather than silently shrinking it. Keep `MODEL_CONTEXT` to the identifiers, status, and decision facts a later turn actually needs, and fetch further detail through a read tool — a large preserved view repeats in every later prompt, which adds up in prompt size, model cost, and latency. (This guarantee needs a matching Runtime version on every process handling the session; a rollback or a mixed-version deployment can silently lose it.) ### PRESENT The `PRESENT:` action displays a formatted presentation before collection. It is used alongside `GATHER:` to show the user what has been collected so far: ```yaml theme={null} FLOW: review: REASONING: false PRESENT: | Beneficiary: {{beneficiary_name}} Account: {{MASK(beneficiary_account, "last4")}} Bank: {{beneficiary_bank_name}} GATHER: - confirmation: required THEN: process ``` ### GATHER in flow steps Within a flow step, `GATHER:` uses a list syntax different from the top-level `GATHER:` section: ```yaml theme={null} FLOW: collect_details: REASONING: false GATHER: - source_account: required prompt: "Your account number?" THEN: next_step ``` #### Multi-field collection Collect multiple fields in a single step using the `FIELDS:` sub-block: ```yaml expandable=true theme={null} FLOW: collect_beneficiary: REASONING: false GATHER: FIELDS: - beneficiary_name: required prompt: "Full legal name on the account." - beneficiary_account: required prompt: "Account number or IBAN." - beneficiary_bank: required prompt: "Name of the bank." - country: required prompt: "Country of the bank." default: "US" infer: true STRATEGY: llm CORRECTIONS: true COMPLETE_WHEN: beneficiary_name AND beneficiary_account AND beneficiary_bank AND country THEN: validate ``` #### Flow gather field properties Each field in a flow `GATHER:` supports these properties: | Property | Type | Required | Default | Description | | - | - | - | - | - | | `required` / optional value after name | `boolean` | No | `true` | Whether the field must be collected | | `prompt` | `string` | No | *none* | Question shown to the user | | `type` | `string` | No | `"string"` | Data type | | `default` | `any` | No | *none* | Default value | | `validation` | `string` | No | *none* | Validation expression | | `infer` | `boolean` | No | `false` | Allow LLM inference | | `infer_confidence` | `number` | No | `0.8` | Minimum inference confidence | | `infer_confirm` | `boolean` | No | `true` | Confirm inferred values | | `extraction_hints` | `string[]` | No | *none* | LLM extraction guidance | | `range` | `boolean` | No | `false` | Collect as range | | `list` | `boolean` | No | `false` | Collect as array | | `activation` | `string` , `{when: string}` | No | *none* | Activation mode | | `depends_on` | `string[]` | No | *none* | Field dependencies | | `prompt_mode` | `"ask"` , `"extract_only"` | No | `"ask"` | Prompt usage mode | Flow gather fields also accept the same additional properties as top-level [GATHER](/agent-platform/abl-reference/gather) fields: `entity_ref`, `options`, `message_key`, `satisfied_by`, `validation_process`, `retry_prompt`, `max_retries`, `preferences`, `sensitive`, `sensitive_display`, `mask_config`, `pii_type`, `transient`, `extraction_pattern`, `extraction_group`, and `semantics`. In addition, a flow gather field may carry `rich_content` format variants for its prompt. Note that flow gather uses `validation:` where the top-level section uses `validate:`. #### Field-level listening settings (`ASR:`) An `ASR:` block — a sibling of `VOICE:` on a single field — sets how the bot **listens** while that one field is being asked on a KoreVG voice call. Once the field is no longer being asked, the call's normal listening settings return automatically: ```yaml theme={null} FLOW: get_county: REASONING: false GATHER: - county: required prompt: "Which county is the dig site in?" ASR: vocabulary: mode: replace terms: [Guilford, Mecklenburg, Wake] no_input_timeout_ms: 8000 get_street: REASONING: false GATHER: - street_name: required prompt: "What is the street name?" ASR: provider: deepgram model: nova-3 vocabulary: mode: extend terms: default: [Boulevard] en: [{ phrase: Parkway, boost: 40 }] es-MX: [Privada] ``` | Key | Meaning | | - | - | | `provider` | Speech engine for this field (for example `deepgram`, `google`, `azure`) — must already be configured on the project's voice channel. | | `model` | Recognizer model for the field. Not validated at deploy; a model the engine rejects falls back to the normal settings. | | `language` | Listening language for this question only — doesn't change the conversation language. | | `vocabulary.mode` | `replace` (only this field's words), `extend` (the call's words plus this field's, field words kept first under a limit), or `none` (no special words). Without `vocabulary`, the call's words are unchanged. | | `vocabulary.terms` | A plain list of words for every language, or a map of language tag (or `default`) to a list. Each entry is a string or `{ phrase, boost: 0-100 }`. The list is picked by exact tag, then base language, then `default` — lists are never merged across languages. | | `no_input_timeout_ms` | Silence wait for this question, `1000`-`60000` ms. Has no effect when no-input handling is off for the channel. | Rules: * Keys are strict — an unknown key is a compile error (`GATHER_ASR_UNKNOWN_KEY`). Credentials and service IDs never go in ABL. * At most 500 vocabulary entries per field across every language, each at most 100 characters. * **One listening-settings field per step.** `ASR:` applies only when its field is asked on its own; a field sharing its step with other required fields gets warning `GATHER_ASR_SHARED_STEP` and uses the normal settings when asked together. * **FLOW steps only.** `ASR:` on a top-level (reasoning) `GATHER` field is preserved but has no effect, with compile warning `GATHER_ASR_REASONING_UNSUPPORTED`. * **Deploy checks.** A `provider` that no voice channel bound to the target environment supports blocks create/promote (`GATHER_ASR_PROVIDER_UNRESOLVED`); restore/rollback warn instead. An unsupported channel (non-KoreVG voice, realtime speech-to-speech), an unsupported language, too many words for the engine, a no-input wait with no-input off, or an uncovered language list are deploy warnings. * At runtime, anything that can't be applied (an unknown engine, a rejected setting, a gateway that doesn't confirm the change) falls back silently to the call's normal listening settings — the call continues. * There's no platform switch for this feature; to stop using it, remove the `ASR:` block and redeploy. #### GATHER block properties Alongside `FIELDS:`, a flow `GATHER:` block accepts these block-level properties: | Property | Type | Description | | - | - | - | | `STRATEGY` | `"auto"` , `"ml"` , `"llm"` , `"hybrid"` , `"pattern"` | Collection approach (see below). Also settable per field. | | `PROMPT` | `string` | A single prompt used to collect the whole block. | | `MESSAGE_KEY` | `string` | Locale catalog key for the block prompt. | The `STRATEGY:` value controls the collection approach: | Value | Description | | - | - | | `auto` | Let the platform choose the best available strategy | | `ml` | Use the ML/NLU entity model | | `llm` | Use LLM to extract values from natural conversation | | `pattern` | Use pattern matching and extraction rules | | `hybrid` | Combine ML and LLM extraction | #### CORRECTIONS When `CORRECTIONS: true`, the user can naturally correct previously collected values without restarting the step: ```yaml theme={null} FLOW: collect_info: REASONING: false GATHER: - name: required - email: required CORRECTIONS: true THEN: next_step ``` #### COMPLETE\_WHEN The `COMPLETE_WHEN:` condition specifies when the gather step is considered complete: ```yaml theme={null} FLOW: collect_all: REASONING: false GATHER: FIELDS: - field_a: required - field_b: required - field_c: optional COMPLETE_WHEN: field_a AND field_b THEN: process ``` ### CALL...WITH...AS The `CALL:` action invokes a tool. Use `WITH:` to pass parameters and `AS:` to bind the result to a variable: ```yaml theme={null} FLOW: verify_step: REASONING: false CALL: verify_account WITH: account_id: source_account AS: acctResult RESPOND: "Account verified: {{acctResult.owner_name}}" THEN: next_step ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `CALL` | `string` | -- | -- | Tool name to invoke | | `WITH` | `Record` | No | *none* | Key-value parameter mapping | | `AS` | `string` | No | *none* | Variable name to bind the tool result | #### WITH parameters The `WITH:` block maps tool parameter names to values or variable references: ```yaml theme={null} CALL: calculate_fees WITH: transfer_type: transfer_type amount: amount currency: currency destination_country: beneficiary_country AS: feeResult ``` Values can be: * Variable references: `amount` (resolves the session variable `amount`) * Literal strings: `"domestic"` * Expressions: `COALESCE(reference, "")` * **Nested objects** — a key with no inline value followed by an indented sub-block becomes a nested object argument, for tools whose schema requires object parameters: ```yaml theme={null} CALL: ivr_digit_input WITH: prompt: "Enter your account number" noInputConfig: timeoutMs: 5000 maxRetries: 2 noMatchConfig: message: "That didn't match. Please try again." AS: digitResult ``` Nesting is preserved by indentation. Scalar leaves keep their raw form and are coerced at runtime (`"30"` → `30`, `"[15, 16]"` → `[15, 16]`). #### Localized WITH arguments A `WITH` value can take its text from the project's locale files (`locales//.json` or `locales//_shared.json`) with `"{{locale.KEY}}"`, or a bare `locale.KEY`. The text is chosen for the caller's current interaction locale (with the same fallback chain as `MESSAGE_KEY` — for example `hi-IN`, then `hi`), and `{{…}}` placeholders inside the locale text are rendered too: ```yaml theme={null} FLOW: collect_card_number: REASONING: false CALL: ivr_digit_input WITH: prompt: "{{locale.CARD_PROMPT}}" maxDigits: 19 endingKeyPress: "#" noInputConfig: timeout: 30 maxRetries: 2 message: "{{locale.CARD_NO_INPUT}}" AS: cardNumberResult ON_RESULT: - IF: cardNumberResult.success == false THEN: payment_input_failed - ELSE: THEN: validate_card_number ``` If a referenced key isn't defined for the caller's locale (and no less-specific locale in the chain provides it), the tool is **not** called — the `CALL` result is `success: false` with `error.code: "LOCALE_KEY_MISSING"`, so the step's failure branch runs and raw `{{locale.KEY}}` text never reaches the tool or the caller. Only authored text is treated as a locale reference; a session value that happens to contain `{{locale.…}}` passes through as plain data. Supported forms are `"{{locale.KEY}}"` (optionally with filters, like `"{{locale.KEY | upper}}"`, and mixed with other text) and a bare `locale.KEY`; an expression such as `locale["KEY"]` or `locale.KEY + "!"` is not supported and the `CALL` is not run. ### SET The `SET:` action assigns values to session variables: ```yaml theme={null} FLOW: init: REASONING: false SET: status = "pending" retry_count = 0 sanctions_clear = false THEN: next_step ``` Each assignment uses `variable = expression` syntax. The expression is resolved at execution time and can reference: * Literal values: `"pending"`, `0`, `true`, `false` * Variable references: `acctResult.balance` * Function calls: `FORMAT_CURRENCY(amount, "USD")`, `COALESCE(value, "default")`, `ADD(a, b)`, `SUB(a, b)`, `ROUND(n, decimals)` * Unique ID generation: `UNIQUE_ID(12)` * Current timestamp: `NOW()` An unquoted JSON array or object literal is stored as a structured value. A JSON string's string-typed leaves may contain `{{variable}}` templates — the collection is parsed first, then its string values are rendered, so substituted data is never reinterpreted as JSON: ```yaml theme={null} SET: filters = [{"field":"customer","operator":"in","value":["Public", "{{employer_name}}"]}] ``` This stores an array of objects correctly even when `employer_name` contains quotes or backslashes. Prefer a direct variable reference when constructing a computed collection instead of interpolating one into JSON-shaped text: ```yaml theme={null} SET: filters = [{"field":"customer","operator":"in","value":["Public", employer_name]}] ``` Surround the whole value with quotes when JSON-shaped *text* is intentional — `SET: payload_text = '[{"customer":"{{employer_name}}"}]'` stores a string, not a structured value. A session `TYPE` declaration alone doesn't validate every `SET` write; validate structured arguments at the tool boundary (pass the variable to a typed parameter, such as `filters: object[]`, through `WITH`). ### CHECK The `CHECK:` action evaluates a condition. If the condition is false, execution transitions to the `ON_FAIL:` step: ```yaml theme={null} FLOW: check_limits: REASONING: false CHECK: amount <= available_balance AND ADD(daily_used, amount) <= daily_limit ON_FAIL: over_limit THEN: proceed ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `CHECK` | `string` | -- | -- | Condition expression to evaluate | | `ON_FAIL` | `string` | No | *none* | Step to go to if the condition is false | ### CLEAR The `CLEAR:` action removes variables from the session context: ```yaml theme={null} FLOW: reset_beneficiary: REASONING: false CLEAR: [beneficiary_name, beneficiary_account, beneficiary_id] THEN: collect_beneficiary ``` ### TRANSFORM The `TRANSFORM:` action filters, maps, sorts, and limits an array from the session context: ```yaml theme={null} FLOW: filter_results: REASONING: false TRANSFORM: SOURCE: search_results AS: hotel INTO: filtered_hotels FILTER: "hotel.rating >= 4" MAP: name: hotel.name price: hotel.price_per_night rating: hotel.rating SORT_BY: price asc LIMIT: 5 THEN: show_results ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `SOURCE` | `string` | Yes | -- | Dot-path to the source array | | `AS` | `string` | Yes | -- | Loop variable name | | `INTO` | `string` | Yes | -- | Output variable name | | `FILTER` | `string` | No | *none* | Condition expression using the loop variable | | `MAP` | `Record` | No | *none* | Field mapping expressions | | `SORT_BY` | `string` | No | *none* | Sort field and direction (`asc` or `desc`) | | `LIMIT` | `number` | No | *none* | Maximum number of items in the output | ### LOG The `LOG:` action emits a trace-only diagnostic (not shown to the user). Use the inline or block form: ```yaml theme={null} FLOW: audit: REASONING: false LOG: "Entering audit step" # or, with a level: # LOG: # MESSAGE: "Balance below threshold" # LEVEL: warn # debug | info | warn | error THEN: next_step ``` ### AWAIT\_ATTACHMENT The `AWAIT_ATTACHMENT:` action pauses the step until the user uploads a file. ```yaml theme={null} FLOW: collect_id_doc: REASONING: false AWAIT_ATTACHMENT: NAME: id_document PROMPT: "Please upload a photo of your ID." CATEGORY: image # image | document | audio | video REQUIRED: true TIMEOUT: 120 ON_TIMEOUT: manual_review THEN: verify_id ``` | Property | Type | Description | | - | - | - | | `NAME` | `string` | Variable the uploaded attachment is stored under. | | `PROMPT` | `string` | Message asking the user to upload. | | `CATEGORY` | `string` | `image`, `document`, `audio`, or `video`. | | `REQUIRED` | `boolean` | Whether the upload is mandatory. | | `TIMEOUT` | `number` | Seconds to wait for the upload. | | `ON_TIMEOUT` | `string` | Step to transition to if the upload times out. | ### SECURE\_INPUT The `SECURE_INPUT:` action pauses the step to collect one or more **sensitive** values — a card number, CVV, or similar — via DTMF and/or speech, and delivers them **directly to one tool**, without the raw values ever passing through the LLM, transcript, session state, logs, call recordings, or traces: speech recognition is suppressed and recording is paused while the caller enters them, and the values are wiped from memory once the delivery call returns. It's the PCI-safe alternative to `GATHER` for data that must never reach the model. `SECURE_INPUT` is currently authored only in the legacy `.agent.abl` (line-based) format. The `.agent.yaml` parser does not yet accept this action. It also requires a **live voice call** and the `SECURE_INPUT_ENABLED` runtime setting (off by default): without a live voice session the step takes `ON_FAIL`; with the setting off, the step is skipped entirely. Both the delivery `CALL` and any validation `CALL` must be **HTTP project tools** (configured in Project Tools, not the agent's own `TOOLS:` section) that send a JSON body. ```yaml theme={null} FLOW: collect_card: REASONING: false SECURE_INPUT: MODE: [dtmf] # dtmf | speech, one or both FIELDS: - card_number: PROMPT: { en: "Please enter your 16-digit card number." } COLLECT: { digits: 13..19, finishOnKey: "#" } VALIDATE: [ luhn ] - cvv: PROMPT: { en: "And the 3-digit security code." } COLLECT: { digits: 3..4 } MAX_ATTEMPTS: 3 CANCEL: { dtmf_key: "*" } CALL charge_card WITH: { pan: "{{secure.card_number}}", cvv: "{{secure.cvv}}" } AS: payment_result RESULT_FIELDS: [Status, TransactionIdentifier] SUCCESS_WHEN: { Status: "Success" } ON_SUCCESS: confirm ON_TIMEOUT: retry_by_agent ON_CANCEL: retry_by_agent ON_FAIL: human_agent THEN: confirm confirm: REASONING: false RESPOND: "Your payment is complete. Reference: {{payment_result.TransactionIdentifier}}." ``` | Property | Type | Description | | - | - | - | | `MODE` | `("dtmf" \| "speech")[]` | Which collection channel(s) are accepted. Required; at least one. | | `FIELDS` | list | One or more fields to collect, in order. Each is a `- :` entry with `PROMPT`, `COLLECT`, and optional `VALIDATE`. | | `MAX_ATTEMPTS` | `number` | Failed collection attempts allowed before `ON_FAIL` (default 3). | | `CANCEL` | `object` | Cancellation trigger: `dtmf_key` and/or `keywords` (a keyword must be spoken as its own word). | | `CALL WITH: {...}` | — | The **delivery** call — the single tool invoked with the collected values. Only the inline-object `WITH:` form is supported. Reference a collected field as a whole-value placeholder `{{secure.}}`. | | `AS` | `string` | Session variable that receives the delivery response. Requires `RESULT_FIELDS`. | | `RESULT_FIELDS` | `string[]` (with `AS`) | Top-level response fields that may be bound to `AS`; nothing else in the response is ever read. Exact top-level keys only — nested paths aren't supported. | | `SUCCESS_WHEN` | `object` | `{ Field: "value" }` or `{ Field: [value1, value2] }` — which delivery responses count as success. See below. | | `ON_SUCCESS` | `string` | Step to transition to after successful collection and delivery. | | `ON_TIMEOUT` | `string` | Step to transition to if collection times out. | | `ON_CANCEL` | `string` | Step to transition to if the caller cancels. | | `ON_FAIL` | `string` | Step to transition to if validation runs out of attempts, delivery fails, or `SUCCESS_WHEN` doesn't match. | `AS`, `RESULT_FIELDS`, and `SUCCESS_WHEN` may be written indented under the `CALL` line or beside it; `AS: ` may also end the `CALL` line itself. Every outcome route (`ON_SUCCESS`, `ON_TIMEOUT`, `ON_CANCEL`, `ON_FAIL`) takes a **step name only** — a `RESPOND` or any other block indented under a route isn't part of `SECURE_INPUT`; the parser reports the ignored line (`W208`) and an empty route is a compile error (`SECURE_INPUT_EMPTY_OUTCOME_ROUTE`). Put the response in the step the route names, as in the `confirm` step above. **Success.** Without `SUCCESS_WHEN`, any 2xx delivery response takes `ON_SUCCESS`; a non-2xx status, a network failure, a timeout, or an unreadable body takes `ON_FAIL`. With `SUCCESS_WHEN`, a 2xx response must also match every listed field (compared as text, no case folding) or the step takes `ON_FAIL` — use this when an API can report a decline inside an HTTP 200. A delivery that timed out after the request was sent also takes `ON_FAIL`, even though the charge may have gone through — don't retry payment from that step. **Reading the response.** `AS` binds only the `RESULT_FIELDS` the response contains, as top-level scalar values. A field is dropped (never bound) when its value contains any collected value's digits, contains a valid card number, or is an object or list — the step's trace names the bound/dropped fields, never their values. The bound fields are then ordinary session values for later steps, as `{{payment_result.Status}}` above. #### Field properties | Property | Type | Description | | - | - | - | | `PROMPT` | `{ : string }` or `{ template: string }` | Locale-keyed prompt text, or a reference to a named template. At least one non-empty entry is required. A locale reference such as `"{{locale.KEY}}"` also works. | | `COLLECT` | `object` | `digits: N..M` (min/max digit count), `finishOnKey`, `interDigitTimeoutMs`, `asrTimeoutMs` (spoken-path silence window). | | `VALIDATE` | list | Validation rules: a bare rule name (`luhn`), an inline object (`{ length: 4..4 }`, `{ date_mmYY: "not_past" }`), or a delegated `CALL WITH: {...}` rule. | A failed check asks for the field again, up to `MAX_ATTEMPTS`. A `CALL` validation rule passes only when the tool returns `valid: true`, and may reference the field being checked plus any fields collected before it. A field accepts only `PROMPT`, `COLLECT`, and `VALIDATE` — anything else is ignored with a warning (`W210`). References inside the delivery `WITH:` block must be a **whole-value** placeholder — `pan: "{{secure.card_number}}"` is valid; embedding it in a larger string (`pan: "card:{{secure.card_number}}"`) or referencing an undeclared field is a compile error. Every collected value lives only in a local secure buffer for the duration of the gate and is **zeroized on every exit path** (success, timeout, cancel, or failure) — it is never written to session state, so it cannot be referenced by `{{ }}` outside the delivery/validation calls, and it never appears in a trace, span, or log. | Diagnostic | Fires when | | - | - | | `SECURE_INPUT_INVALID_MODE` | `MODE` is missing, empty, or contains a value other than `dtmf`/`speech`. | | `SECURE_INPUT_MISSING_DELIVER_TOOL` | No delivery `CALL`. | | `SECURE_INPUT_MISSING_PROMPT` | No `FIELDS` are declared, or a field's `PROMPT` has neither a locale entry nor a `template` reference. | | `SECURE_EMBEDDED_PLACEHOLDER` | A `{{secure.*}}` reference is part of a longer argument string instead of the whole value. | | `SECURE_UNKNOWN_FIELD` | A `{{secure.*}}` reference names a field the step doesn't collect. | | `SECURE_INPUT_EMPTY_OUTCOME_ROUTE` | An outcome route (`ON_SUCCESS`/`ON_FAIL`/`ON_TIMEOUT`/`ON_CANCEL`) is written without a step name. | | `SECURE_INPUT_INVALID_RESULT_CONTRACT` | `AS` that isn't a plain variable name, `AS` without `RESULT_FIELDS` (or the reverse), a nested or repeated field name, an empty `SUCCESS_WHEN`, or a `SUCCESS_WHEN` field with no value. | | `RESERVED_VARIABLE_NAME` / `IMMUTABLE_SYSTEM_IDENTIFIER` | `AS` names a reserved or system-owned variable such as `session_id`. | | `W208` / `W210` | A line inside the block, or a field property, that `SECURE_INPUT` doesn't read — it's ignored. | | `W211` | `RESULT_FIELDS` or `SUCCESS_WHEN` written across several lines; write them inline. | ### MESSAGE\_KEY and BEHAVIOR Two additional step-level properties: * **`MESSAGE_KEY:`** — a locale catalog key used to localize the step's `RESPOND:` message. * **`BEHAVIOR:`** — free-form guidance text appended to the step's reasoning goal (a convenience alias that folds into `GOAL`). ### RESOLVE\_SIP\_DESTINATION `RESOLVE_SIP_DESTINATION:` is a step action that computes a dynamic SIP transfer authority (VDN/host/port) **before** a `call_transfer` step runs. It runs a *feeder* — a workflow tool or a session memory key — matches the candidate against the `approved_authorities` allow-list declared on the `call_transfer` tool's `sipTransferId` parameter, and commits the approved authority to a trusted server-side routing channel that the transfer then reads. The model never sees or sets the destination. ```yaml theme={null} FLOW: resolve_destination: REASONING: false RESOLVE_SIP_DESTINATION: FROM_WORKFLOW: "lookup_vdn_by_region_language" # or: FROM_MEMORY: routing_decision TIMEOUT_MS: 1500 THEN: transfer_step transfer_step: REASONING: false CALL: call_transfer(callTransferType: "sip") THEN: COMPLETE ``` | Sub-key | Type | Description | | - | - | - | | `FROM_WORKFLOW` | `string` | Name of a workflow tool invoked as the destination feeder. | | `FROM_MEMORY` | `string` | Session memory key to read as the feeder (use instead of `FROM_WORKFLOW`). | | `TIMEOUT_MS` | `number` | Hard timeout for the feeder, in milliseconds. | The feeder's result supplies the `{{route.vdn}}` / `{{route.ip}}` / `{{route.port}}` values for the `sipTransferId` `template`. The two-zone template and the `approved_authorities` allow-list are declared on the tool parameter — see [Dynamic destinations](/agent-platform/abl/reference/tools#dynamic-destinations) in the tools reference. *** ## Branching and control flow ### THEN / ON\_FAIL The most basic branching: `THEN:` specifies the next step on success, and `ON_FAIL:` specifies the step on failure: ```yaml theme={null} FLOW: check_status: REASONING: false CHECK: account_status == "active" ON_FAIL: inactive_account THEN: proceed ``` `ON_FAIL:` also accepts a structured block for recovery flows — `COLLECT:` (fields to re-collect), `GOTO:` (step), `RETRY: true`, `RESPOND:`, and `THEN:`: ```yaml theme={null} FLOW: validate_details: REASONING: false CHECK: routing_number_valid == true ON_FAIL: RESPOND: "That routing number looks off — let's try again." COLLECT: [routing_number] GOTO: collect_routing ``` ### ON\_RESULT `ON_RESULT:` provides multi-way branching based on the result of a `CALL:` action. Each branch has an `IF:` condition, optional actions, and a `THEN:` transition: ```yaml expandable=true theme={null} FLOW: verify_step: REASONING: false CALL: verify_account WITH: account_id: source_account AS: acctResult ON_RESULT: - IF: acctResult.status == "active" SET: customer_name = acctResult.owner_name balance = acctResult.available_balance RESPOND: "Account verified. Hello, {{customer_name}}." THEN: next_step - IF: acctResult.status == "frozen" RESPOND: "Your account is frozen. Please contact your branch." THEN: end - ELSE: RESPOND: "Account not found. Please check the number." THEN: collect_account ``` #### ON\_RESULT branch properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `IF` | `string` | No | -- | Condition expression. Omit for `ELSE` branch. | | `RESPOND` | `string` | No | *none* | Message to display | | `SET` | `Record` | No | *none* | Variable assignments | | `CALL` | `string` | No | *none* | Optional nested tool call | | `THEN` | `string` | Yes | -- | Next step | The `ELSE` branch (a branch with no `IF` condition) matches when no other condition is true. ### ON\_SUCCESS / ON\_FAIL An alternative to `ON_RESULT:` for simpler success/failure branching: ```yaml theme={null} FLOW: call_api: REASONING: false CALL: submit_request WITH: data: request_data AS: result ON_SUCCESS: RESPOND: "Request submitted: {{result.id}}" THEN: done ON_FAIL: RESPOND: "Request failed. Please try again." THEN: retry_step ``` Both `ON_SUCCESS:` and `ON_FAIL:` support conditional branches: ```yaml expandable=true theme={null} FLOW: process: REASONING: false CALL: process_order AS: orderResult ON_SUCCESS: - IF: orderResult.express == true RESPOND: "Express order confirmed!" THEN: express_fulfillment - ELSE: RESPOND: "Order confirmed." THEN: standard_fulfillment ON_FAIL: - IF: orderResult.error == "out_of_stock" RESPOND: "Item is out of stock." THEN: suggest_alternatives - ELSE: RESPOND: "An error occurred." THEN: error_handling ``` ### ON\_INPUT `ON_INPUT:` provides branching based on user input, typically used after a `GATHER:` action: ```yaml theme={null} FLOW: confirm: REASONING: false GATHER: - confirmation: required ON_INPUT: - IF: input.contains("yes") OR input.contains("confirm") THEN: execute - IF: input.contains("no") OR input.contains("cancel") RESPOND: "Cancelled." THEN: cleanup - ELSE: RESPOND: "Please type 'yes' to confirm or 'no' to cancel." THEN: confirm ``` A step that has only `ON_INPUT` branches — no `RESPOND`/prompt and no default (`ELSE`) branch — traps any unmatched input on the same step forever. The compiler warns (`FLOW_PROMPTLESS_ON_INPUT_UNMATCHED_TRAP`): add a default `ELSE` route or an authored prompt unless the wait is intentional. ### Digressions Digressions are intent-based escapes that can interrupt the current step. They match user intent patterns and respond accordingly: ```yaml expandable=true theme={null} FLOW: collect_details: REASONING: false GATHER: - account: required DIGRESSIONS: - INTENT: "what is swift" RESPOND: "A SWIFT code is an 8 or 11 character code identifying a bank internationally." RESUME: true - INTENT: "cancel" RESPOND: "Transaction cancelled." GOTO: cleanup - INTENT: "speak to agent" RESPOND: "Connecting you with a specialist." DELEGATE: Live_Agent ``` #### Digression properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `INTENT` | `string` | Yes | -- | Intent pattern to match | | `KEYWORDS` | `string[]` | No | *none* | Explicit keywords for matching | | `CONDITION` | `string` | No | *none* | Additional condition to check | | `RESPOND` | `string` | No | *none* | Response message | | `MESSAGE_KEY` | `string` | No | *none* | Locale catalog key for the response | | `SET` | `Record` | No | *none* | Variable assignments (within the `DO:` block) | | `RESUME` | `boolean` | No | `false` | Return to the current step after handling | | `GOTO` | `string` | No | *none* | Transition to a specific step | | `HANDOFF` | `string` | No | *none* | Hand off to another agent (supports `RETURN:` / `ON_RETURN:`) | | `DELEGATE` | `string` | No | *none* | Delegate to another agent (requires `RETURN: true`) | | `CALL` | `string` | No | *none* | Tool to invoke | | `CLEAR` | `string[]` | No | *none* | Variables to clear | When `RESUME: true`, the user returns to the interrupted step after the digression response. When `GOTO:` is specified, the flow transitions to that step instead. `RESUME`, `GOTO`, and `HANDOFF` are terminal. For multiple ordered actions, use the canonical `DO:` block instead of flat keys. Each list item is an action (`RESPOND`, `SET`, `CLEAR`, `CALL`, `HANDOFF`, `DELEGATE`, `GOTO`, `RESUME`), and `HANDOFF`/`DELEGATE` may carry `RETURN:` and `ON_RETURN:` (with `MAP:`). Mixing flat keys with a `DO:` block is discouraged. ```yaml theme={null} DIGRESSIONS: - INTENT: "check balance" DO: - RESPOND: "Let me pull that up." - HANDOFF: Account_Info RETURN: true - RESUME: true ``` ### Global digressions Digressions declared at the flow level (under `global_digressions:`) are available at every step: ```yaml theme={null} FLOW: global_digressions: - INTENT: "cancel" RESPOND: "Transaction cancelled." GOTO: cleanup - INTENT: "help" RESPOND: "I can help with account inquiries, transfers, and balance checks." RESUME: true - INTENT: "speak to agent" RESPOND: "Connecting you with a live agent." DELEGATE: Live_Agent_Transfer ``` ### Sub-intents Sub-intents are scoped intents valid only within a specific step. They handle step-specific user requests like corrections or clarifications: ```yaml expandable=true theme={null} FLOW: collect_beneficiary: REASONING: false GATHER: FIELDS: - name: required - account: required - bank: required SUB_INTENTS: - INTENT: "change name" CLEAR: [name] RESPOND: "What is the correct name?" - INTENT: "change account" CLEAR: [account] RESPOND: "What is the correct account number?" - INTENT: "change bank" CLEAR: [bank, routing_code] RESPOND: "What is the correct bank name?" ``` #### Sub-intent properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `INTENT` | `string` | Yes | -- | Intent pattern to match | | `RESPOND` | `string` | No | *none* | Response message | | `MESSAGE_KEY` | `string` | No | *none* | Locale catalog key for the response | | `CLEAR` | `string[]` | No | *none* | Variables to clear (triggers re-collection) | | `SET` | `Record` | No | *none* | Variables to set | | `CALL` | `string` | No | *none* | Tool to invoke | | `RESUME` | `boolean` | No | `true` | Stay in the current step | *** ## Interactive actions Flow steps can present interactive UI elements (buttons, selects, inputs) to the user. These are attached to `RESPOND:` messages and handled with `ON_ACTION:` callbacks. ### Action elements Interactive actions are defined in an `ACTIONS:` block within a step: ```yaml expandable=true theme={null} FLOW: choose_option: REASONING: false RESPOND: "How would you like to proceed?" ACTIONS: - id: "option_transfer" type: button label: "Wire Transfer" value: "wire" - id: "option_ach" type: button label: "ACH Transfer" value: "ach" - id: "option_check" type: button label: "Send a Check" value: "check" ON_ACTION: - ACTION: "option_transfer" SET: transfer_method = "wire" THEN: wire_flow - ACTION: "option_ach" SET: transfer_method = "ach" THEN: ach_flow - ACTION: "option_check" SET: transfer_method = "check" THEN: check_flow ``` ### Element types #### Buttons ```yaml theme={null} ACTIONS: - id: "confirm_btn" type: button label: "Confirm" value: "confirmed" ``` #### Select (dropdown) ```yaml theme={null} ACTIONS: - id: "currency_select" type: select label: "Select currency" options: - id: "usd" label: "US Dollar" description: "United States Dollar" - id: "eur" label: "Euro" description: "European Union Euro" - id: "gbp" label: "British Pound" ``` #### Input fields ```yaml theme={null} ACTIONS: - id: "amount_input" type: input label: "Enter amount" inputType: number placeholder: "0.00" required: true ``` ### Action element properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `id` | `string` | Yes | -- | Unique identifier for the action element | | `type` | `"button"` , `"select"` , `"input"` | Yes | -- | Element type | | `label` | `string` | Yes | -- | Display label | | `value` | `string` | No | *none* | Hidden value sent when the element is interacted with | | `description` | `string` | No | *none* | Subtitle or helper text | | `options` | `{id, label, description?}[]` | No | *none* | Options for `select` type | | `inputType` | `"text"` , `"number"` , `"date"` , `"time"` , `"email"` | No | `"text"` | Input field type (for `input` type) | | `placeholder` | `string` | No | *none* | Placeholder text (for `input` type) | | `required` | `boolean` | No | `false` | Whether the input is required (for `input` type) | ### Form submission When a step contains `input` type elements, you can configure a form submission button: ```yaml theme={null} ACTIONS: - id: "name_input" type: input label: "Your name" inputType: text required: true - id: "email_input" type: input label: "Your email" inputType: email required: true SUBMIT_LABEL: "Submit" SUBMIT_ID: "form_submit" ``` The `ACTIONS:` block also accepts an optional `RENDER_ID:` to give the whole action set a stable identifier for rendering/telemetry. ### ON\_ACTION callbacks The `ON_ACTION:` block defines handlers for user interactions with action elements: ```yaml theme={null} ON_ACTION: - ACTION: "confirm_btn" RESPOND: "Confirmed! Processing your request." SET: confirmed = true THEN: process_step - ACTION: "cancel_btn" RESPOND: "Cancelled." THEN: cleanup ``` #### ON\_ACTION handler properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `ACTION` | `string` | Yes | -- | The `id` of the action element that triggered this handler | | `CONDITION` | `string` | No | *none* | Optional value-based condition | | `RESPOND` | `string` | No | *none* | Response message | | `SET` | `Record` | No | *none* | Variable assignments | | `CLEAR` | `string[]` | No | *none* | Variables to clear | | `CALL` | `string` | No | *none* | Tool to invoke (requires `AS`) | | `LOG` | `string` | No | *none* | Trace-only diagnostic | | `HANDOFF` | `string` | No | *none* | Hand off to another agent | | `DELEGATE` | `string` | No | *none* | Delegate to another agent (`RETURN:`/`ON_RETURN:` supported) | | `COMPLETE` | `boolean` | No | *none* | Terminal — mark the flow complete | | `THEN` / `GOTO` / `TRANSITION` | `string` | No | *none* | Step to transition to (three interchangeable keywords) | For multiple ordered actions in one handler, use a canonical `DO:` block whose list items are actions (`SET`, `CLEAR`, `LOG`, `RESPOND`, `CALL`, `GOTO`/`TRANSITION`/`THEN`, `HANDOFF`, `DELEGATE`, `COMPLETE`). ### Rich content Steps can include rich content in multiple formats alongside `RESPOND:` messages. The runtime selects the appropriate format based on the channel. The block keyword is `RICH_CONTENT:` (canonical) or `FORMATS:` (original spelling, still accepted); the `.agent.yaml` format accepts `rich_content` or `formats` the same way. ```yaml theme={null} FLOW: show_summary: REASONING: false RESPOND: "Here is your transfer summary." FORMATS: MARKDOWN: | ## Wire Transfer Summary | Field | Value | |-------|-------| | From | {{source_account}} | | To | {{beneficiary_name}} | | Amount | {{amount}} {{currency}} | ADAPTIVE_CARD: | {"type": "AdaptiveCard", "body": [...]} ``` #### Rich content format properties The runtime recognizes these format keys. In addition to the messaging formats, several structured template types are available (see [Rich content](/agent-platform/abl-reference/rich-content) for their schemas): | Format | Description | | - | - | | `MARKDOWN` | Formatted markdown text | | `ADAPTIVE_CARD` | Microsoft Adaptive Cards JSON | | `HTML` | HTML content | | `SLACK` | Slack Block Kit JSON | | `AG_UI` | AG-UI / CopilotKit event JSON | | `WHATSAPP` | WhatsApp interactive message JSON | | `CAROUSEL` | Scrollable cards (title/subtitle/image/buttons); can also be authored as a standalone block after `RESPOND:` | | `QUICK_REPLIES` | Quick-reply chips | | `LIST` | Vertical list of items | | `IMAGE` | Image with caption/alt | | `VIDEO` | Video with caption/alt | | `AUDIO` | Audio with caption/alt | | `FILE` | File attachment | | `KPI` | KPI metric tile | | `TABLE` | Columns + rows | | `CHART` | Bar/line/pie chart | | `FORM` | Form fields + submit | | `PROGRESS` | Progress bar/circle | | `FEEDBACK` | Thumbs/stars/scale feedback | ### Voice configuration Steps can include voice-specific overrides for text-to-speech engines: ```yaml theme={null} FLOW: greeting: REASONING: false RESPOND: "Welcome to wire transfer services." VOICE: SSML: | Welcome to wire transfer services. INSTRUCTIONS: "Speak in a warm, professional tone." PLAIN_TEXT: "Welcome to wire transfer services." ``` | Property | Type | Description | | - | - | - | | `SSML` | `string` | W3C SSML markup for TTS engines | | `INSTRUCTIONS` | `string` | Natural language voice style instructions | | `PLAIN_TEXT` | `string` | Voice-optimized plaintext (fallback for all engines) | | `BARGE_IN` | `"allow"` \| `"disallow"` | Whether the caller may interrupt (barge in on) this spoken output. | | `STREAMING` | `boolean` | TTS delivery mode for this spoken output. Omitted defaults to `true` (streaming). | | `END_OF_SPEECH_TURN_TYPE` | `string` | Expected shape of the caller's next answer, used to pick an end-of-speech (pause) timeout. See [Per-turn endpointing](#per-turn-endpointing). | | `END_OF_SPEECH_TIMEOUT_MS` | `number` | Explicit end-of-speech timeout in milliseconds, overriding the preset. | #### Per-turn endpointing `END_OF_SPEECH_TURN_TYPE` is a small, deliberately-closed vocabulary describing what kind of answer the caller is expected to give next — the runtime resolves it to a concrete end-of-speech (silence/pause) timeout for the active speech recognizer, so authors don't have to guess a millisecond value per turn: | Value | Use for | | - | - | | `simple_confirmation` | A yes/no or short acknowledgement. | | `structured_value` | A structured value like an account number or date. | | `open_ended_question` | An open-ended question expecting a longer, free-form answer. | | `complex_explanation` | A caller explanation that may take a while to finish. | | `anything_else` | Anything not covered above. | It can be set at any VOICE scope — agent-wide under `EXECUTION.voice`, on a behavior profile, on a flow step, or per `RESPOND` — with the narrowest scope winning, same as `BARGE_IN`/`STREAMING`: ```yaml theme={null} FLOW: collect_value: REASONING: false VOICE: END_OF_SPEECH_TURN_TYPE: structured_value END_OF_SPEECH_TIMEOUT_MS: 900 RESPOND: "Please provide the value." VOICE: END_OF_SPEECH_TURN_TYPE: open_ended_question # per-RESPOND override END_OF_SPEECH_TIMEOUT_MS: 1400 ``` `END_OF_SPEECH_TIMEOUT_MS` overrides the preset's timeout with an explicit millisecond value when set alongside (or instead of) `END_OF_SPEECH_TURN_TYPE`. Per-turn endpointing applies to the platform's own speech-recognition pipeline. It does not affect TTS streaming, and is ignored on native realtime/speech-to-speech voice models where the provider owns turn detection. Omit it to fall back to the agent's conversation-level pause configuration. #### Scoped barge-in `barge_in` (values `allow` / `disallow`) sets whether the caller can interrupt a specific spoken output — for example, forcing a compliance disclosure to finish. It may be authored at three scopes, from broadest to narrowest, with the narrower winning: ```yaml theme={null} FLOW: notice: REASONING: false BARGE_IN: disallow # step-wide: applies to this step's outputs RESPOND: "This required notice must play in full." VOICE: BARGE_IN: allow # per-RESPOND override for this output ``` It can also be set on a named template's `VOICE:` block. Placing `barge_in` on the agent-wide `EXECUTION.voice` block, or on branch/handler voice blocks, is rejected (`VOICE_BARGE_IN_ILLEGAL_PLACEMENT`); an invalid value is `VOICE_INVALID_BARGE_IN`. This scoped, per-output policy is distinct from the agent-wide barge-in behavior authored under `CONVERSATION` → `listening`. Use scoped `barge_in` to protect (or open up) individual utterances. #### Scoped streaming `streaming` (`true` / `false`) sets the TTS delivery mode for a specific spoken output — whether audio starts playing as soon as the first tokens are ready (streaming, the default) or the runtime waits for the complete response before speaking it (non-streaming). It follows the same authoring scopes as `barge_in`: step-wide, per-`RESPOND`, or on a named template's `VOICE:` block, with the narrower scope winning. ```yaml theme={null} FLOW: read_confirmation_code: REASONING: false STREAMING: false # step-wide: wait for the full utterance before speaking it RESPOND: "Your confirmation code is {{confirmation_code}}." VOICE: STREAMING: false # equivalent per-RESPOND override ``` Omitting `streaming` defaults to `true` (streaming) at every scope. #### Step-level no-input (caller silence) A FLOW step can carry its own caller-silence policy in its own `VOICE:` block, as `ON_NO_INPUT`. It takes the same keys as `CONVERSATION.listening.on_no_input` (agent/project-wide) and applies on KoreVG pipeline voice while that step is the one waiting on the caller: ```yaml theme={null} CONVERSATION: listening: on_no_input: enabled: true mode: static timeout_ms: 5000 max_retries: 3 messages: - text: "Are you still there?" - text: "Hello?" FLOW: ask_account: REASONING: false RESPOND: "What's your account number?" GATHER: - account_number: required VOICE: ON_NO_INPUT: timeout_ms: 10000 messages: - text: "Could you tell me your account number, {{memo_name}}?" THEN: complete ``` * **Owner.** When the silence timer starts, the step currently waiting owns the window. The block applies only to that step; after `COMPLETE`/`ESCALATE`, or on a step with no block, the agent/project policy applies as usual. * **Merging.** Fields merge one at a time over the project and agent policy — a step can override just `timeout_ms` and inherit everything else. `max_retries`, `relaxed_interval_ms`, `on_exhausted`, and `exhausted_message` all follow the same rule, and `enabled: true` on a step turns silence handling on for that step even when the agent has it off. * **Messages.** Step messages don't *replace* the agent-level list — each silence plays agent message N followed by step message N as one utterance ("Are you still there? Could you tell me your account number, Sam?"). Once the step's own list is used up, the agent message plays alone (or the platform default, if the agent has none), so a step block never ends the call sooner than the agent alone would. * **Retry count.** Restarts when the caller speaks, and when a different step (or the same step entered again) takes ownership of the window. * **Variables.** `{{var}}` placeholders render in agent messages, step messages, and `exhausted_message`. A step message with a missing variable is skipped; an agent or exhausted message with a missing variable falls back to the platform default text. `ON_NO_INPUT` is only legal directly in a FLOW step's own `VOICE:` block. Placed under a GATHER field, `RESPOND`, a branch, a handler, or a response format, it's a compile error (`VOICE_NO_INPUT_ILLEGAL_PLACEMENT`). The YAML format uses a step-level `on_no_input:` key. *** ## Step-level error handling Steps can define local error handlers that override agent-level `ON_ERROR:` handlers: ```yaml expandable=true theme={null} FLOW: call_external: REASONING: false CALL: external_api WITH: query: user_query AS: apiResult ON_ERROR: tool_error: RESPOND: "The service is responding slowly. Retrying..." SUBTYPE: tool_timeout RETRY: 2 RETRY_DELAY: 1000 RETRY_BACKOFF: exponential RETRY_MAX_DELAY: 8000 THEN: CONTINUE validation_error: RESPOND: "An error occurred. Let me try a different approach." THEN: RETRY_STEP ``` The error type (the handler's key) is matched against a **fixed, canonical set** — `default`, `llm_error`, `tool_error`, `routing_error`, `validation_error`, `timeout_error`, and `unknown_error`. A key outside this set (for example the older `tool_timeout`, `invalid_input`, or `api_error`) is flagged at compile time (`ON_ERROR_HANDLER_TYPE_DRIFT`) and never matches at runtime — see [Error types](/agent-platform/abl-reference/lifecycle-and-hooks#error-types) for the full table and the deprecated-name mapping. Narrow `tool_error` with `SUBTYPE`/`SUBTYPES` (known values: `rate_limit`, `auth_failure`, `network_error`, `tool_timeout`). #### Step ON\_ERROR handler properties | Property | Type | Description | | - | - | - | | `SUBTYPE` | `string` | Narrow the handler to a specific error subtype. | | `RESPOND` | `string` | Message to show when the error fires. | | `RETRY` | `number` | Retry attempts before giving up. | | `RETRY_DELAY` | `number` | Delay (ms) between retries. | | `RETRY_BACKOFF` | `string` | Backoff strategy: `fixed`, `exponential`, or `linear`. | | `RETRY_MAX_DELAY` | `number` | Maximum delay (ms) between retries. | | `BACKTRACK_TO` | `string` | Step to backtrack to (used with `THEN: BACKTRACK`). | | `HANDOFF_TARGET` | `string` | Target agent (used with `THEN: HANDOFF`). | | `THEN` | `string` | Next action: `CONTINUE`, `COMPLETE`, `ESCALATE`, `BACKTRACK`, `RETRY_STEP`, or `HANDOFF`. A step name is **not** a valid value here — a flow step's own `THEN:` navigates to a step, but an `ON_ERROR` handler's `THEN:` takes only one of these six actions. | ## Complete example ```yaml expandable=true theme={null} AGENT: Simple_Order VERSION: "1.0.0" GOAL: "Help customers place orders." PERSONA: "Friendly order assistant." TOOLS: check_inventory(product_id: string) -> {available: boolean, quantity: number} description: "Check product availability" type: http endpoint: "/api/inventory/check" method: GET place_order(product_id: string, quantity: number) -> {order_id: string, total: number} description: "Place an order" type: http endpoint: "/api/orders" method: POST FLOW: entry_point: welcome steps: - welcome - collect_product - check_stock - out_of_stock - collect_quantity - confirm - place - done global_digressions: - INTENT: "cancel" RESPOND: "Order cancelled." GOTO: done welcome: REASONING: false RESPOND: "Welcome! What product are you looking for?" THEN: collect_product collect_product: REASONING: false GATHER: - product_id: required prompt: "What is the product ID?" THEN: check_stock check_stock: REASONING: false CALL: check_inventory WITH: product_id: product_id AS: stockResult ON_RESULT: - IF: stockResult.available == true SET: stock_quantity = stockResult.quantity RESPOND: "Great news! We have {{stock_quantity}} in stock." THEN: collect_quantity - ELSE: THEN: out_of_stock out_of_stock: REASONING: false RESPOND: "Sorry, that product is currently out of stock." THEN: done collect_quantity: REASONING: false GATHER: - quantity: required type: number prompt: "How many would you like to order?" CHECK: quantity <= stock_quantity ON_FAIL: collect_quantity THEN: confirm confirm: REASONING: false RESPOND: "You'd like to order {{quantity}} of {{product_id}}. Confirm?" ACTIONS: - id: "yes" type: button label: "Confirm" - id: "no" type: button label: "Cancel" ON_ACTION: - ACTION: "yes" THEN: place - ACTION: "no" RESPOND: "Order cancelled." THEN: done place: REASONING: false CALL: place_order WITH: product_id: product_id quantity: quantity AS: orderResult ON_SUCCESS: RESPOND: "Order placed! Order ID: {{orderResult.order_id}}, Total: {{orderResult.total}}" THEN: done ON_FAIL: RESPOND: "Failed to place order. Please try again later." THEN: done done: REASONING: false RESPOND: "Thank you for using our service!" ``` *** **Related articles:** * [Language overview](/agent-platform/abl-reference/language-overview) -- syntax rules and auto-detection * [Tools](/agent-platform/abl-reference/tools) -- tool definitions used by `CALL` actions * [GATHER](/agent-platform/abl-reference/gather) -- top-level gather field definitions * [Agent declaration](/agent-platform/abl-reference/agent-declaration) -- GOAL used by `REASONING: true` steps, `max_flow_iterations` and model settings # GATHER (information collection) Source: https://koreai.mintlify.app/agent-platform/abl/reference/gather The `GATHER:` section defines structured information that the agent needs to collect from the user during a conversation. Each field specifies a data type, prompt, validation rules, and collection behavior. The runtime orchestrates multi-turn collection, extracting values from natural conversation and prompting for missing fields. ## Syntax At the top level of an agent document, `GATHER:` defines fields as named blocks: ```yaml expandable=true theme={null} GATHER: source_account: prompt: "Which account would you like to use?" type: string required: true amount: prompt: "How much would you like to transfer?" type: number required: true validate: min(1) currency: prompt: "What currency?" type: string required: true default: "USD" infer: true ``` Within a `FLOW:` step, gather uses a different syntax. See [GATHER within FLOW](/agent-platform/abl-reference/flow#gather-in-flow-steps). ## Field properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `prompt` | `string` | Yes | -- | The question or instruction shown to the user to collect this field | | `message_key` | `string` | No | *none* | Locale catalog key used to localize the `prompt` | | `description` | `string` | No | *none* | Semantic, model-facing description of what the value represents (distinct from `prompt`). Overrides a referenced entity's description. | | `type` | `string` | No | `"string"` | Data type for the field value (see [Field types](#field-types)) | | `required` | `boolean` | No | `true` | Whether the field must be collected before proceeding | | `default` | `any` | No | *none* | Default value used if the user does not provide one | | `entity_ref` | `string` | No | *none* | Bind to a named entity from the `ENTITIES:` section (see [Entity binding](#entity-binding)) | | `options` | `string[]` | No | *none* | Allowed values for enum-style fields (see [Enum options](#enum-options)) | | `validate` | `string` | No | *none* | Validation expression (see [Validation](#validation)) | | `validation_process` | `"REGEX"` , `"CODE"` , `"LLM"` | No | *none* | How validation is performed | | `retry_prompt` | `string` | No | *none* | Custom prompt shown when validation fails | | `max_retries` | `number` | No | *none* | Maximum validation retry attempts | | `infer` | `boolean` | No | `false` | Allow the LLM to infer the value from context | | `infer_confidence` | `number` | No | `0.8` | Minimum confidence threshold for accepting inferred values (0.0--1.0) | | `infer_confirm` | `boolean` | No | `true` | Whether to confirm inferred values with the user | | `prompt_mode` | `"ask"` , `"extract_only"` | No | `"ask"` | Whether the prompt is shown to the user or used only for LLM extraction | | `range` | `boolean` | No | `false` | Collect as a range `{low, high}` instead of a scalar | | `list` | `boolean` | No | `false` | Collect as an array instead of a scalar | | `preferences` | `boolean` | No | `false` | Categorize into accept/desire/avoid/refuse preference sets | | `activation` | `string` , `{when: string}` | No | `"required"` | Activation mode (see [Progressive activation](#progressive-activation)) | | `depends_on` | `string[]` | No | *none* | Fields that must be collected before this field activates | | `satisfied_by` | `string[]` | No | *none* | Provenance classes allowed to satisfy this field (see [Value provenance](#value-provenance)) | | `sensitive` | `boolean` | No | `false` | Whether this field carries PII | | `sensitive_display` | `"redact"` , `"mask"` , `"replace"` | No | *none* | How sensitive values are displayed outside the gather context | | `mask_config` | `{showFirst, showLast, char}` | No | *none* | Masking configuration when `sensitive_display` is `"mask"` | | `pii_type` | `string` | No | *none* | Explicit PII classification for shape-preserving masking (see [Sensitive fields](#sensitive-fields)) | | `pii_pattern_ref` | `string` | No | *none* | Name of a project-defined PII pattern to bind this field to, instead of `pii_type` (see [Sensitive fields](#sensitive-fields)) | | `transient` | `boolean` | No | `false` | Whether PII auto-cleans after gather completes | | `extraction_pattern` | `string` | No | *none* | Custom regex pattern for value extraction | | `extraction_group` | `number` | No | `0` | Capture group index for `extraction_pattern` | | `extraction_hints` | `string[]` | No | *none* | Example surface forms that guide LLM extraction. Merged on top of a referenced entity's hints; an explicit empty list clears inherited hints. | | `strategy` | `"auto"` , `"ml"` , `"llm"` , `"hybrid"` , `"pattern"` | No | *none* | Per-field extraction strategy override (see [Extraction strategy](#extraction-strategy)). | | `semantics` | `object` | No | *none* | Supplemental metadata about the value (see [Semantics](#semantics)) | ## Field types The `type` property specifies the expected data type for the field value: | Type | Description | Example values | | - | - | - | | `string` | Free-text value | `"John Smith"`, `"USD"` | | `text` | Longer free-text value | `"..."` | | `free_text` | Unconstrained free text | `"..."` | | `number` | Numeric value | `42`, `150.75` | | `integer` | Whole number | `42` | | `float` | Decimal number | `3.14` | | `currency` | Monetary amount | `19.99` | | `boolean` | True/false | `true`, `false` | | `date` | Calendar date | `"2026-03-15"` | | `datetime` | Date and time | `"2026-03-15T14:30:00"` | | `email` | Email address | `"user@example.com"` | | `phone` | Phone number | `"+1-555-123-4567"` | | `enum` | One of a fixed set (see `options`) | `"domestic"` | | `pattern` | Value matching a regex | `"AB1234"` | | `location` | Geographic location | `"San Francisco, CA"` | ## Entity binding Instead of declaring `type`, `options`, and validation inline, a field can reference a reusable named entity from the [`ENTITIES:`](/agent-platform/abl-reference/nlu) section via `entity_ref`. The field then inherits the entity's type, allowed values, synonyms, and validation. ```yaml theme={null} GATHER: request: entity_ref: request_type # defined in the ENTITIES: section prompt: "What type of request do you have?" ``` A field with `entity_ref` should not also declare `type`, `options`, or values — the referenced entity owns that contract. ## Enum options For enum-style fields, `options` lists the allowed values directly. Use it as an alternative to `validate: enum(...)`. Both inline and block forms are accepted: ```yaml theme={null} GATHER: plan: prompt: "Which plan would you like?" type: enum options: [basic, pro, enterprise] region: prompt: "Which region?" type: enum options: - "us-east" - "eu-west" ``` ## Value provenance The `satisfied_by` property restricts which sources may satisfy a field — useful for fields that must come from an authoritative system rather than user claims. ```yaml theme={null} GATHER: account_balance: prompt: "What is your current balance?" type: number satisfied_by: [authoritative_tool, system] ``` | Value | Description | | - | - | | `user_input` | The value may be provided directly by the user. | | `authoritative_tool` | The value may be supplied by an authoritative tool result. | | `system` | The value may be set by the system/session. | ## Validation The `validate` property defines a validation expression that the collected value must satisfy: ```yaml expandable=true theme={null} GATHER: amount: prompt: "How much?" type: number required: true validate: min(1) email: prompt: "Your email address?" type: string required: true validate: email() transfer_type: prompt: "Domestic or international?" type: string required: true validate: enum(domestic, international) ``` ### Validation expressions | Expression | Description | Example | | - | - | - | | `min(n)` | Minimum numeric value | `validate: min(1)` | | `max(n)` | Maximum numeric value | `validate: max(10000)` | | `enum(a, b, c)` | Must be one of the listed values | `validate: enum(domestic, international)` | | `email()` | Must be a valid email format | `validate: email()` | | `pattern(regex)` | Must match a regex pattern | `validate: pattern(^\d{9}$)` | ### Validation process The `validation_process` property controls how validation is executed: | Value | Description | | - | - | | `REGEX` | Validate using a regular expression pattern | | `CODE` | Validate using a code expression | | `LLM` | Validate using LLM judgment | ```yaml theme={null} GATHER: routing_number: prompt: "What is the ABA routing number?" type: string required: true validate: pattern(^\d{9}$) validation_process: REGEX retry_prompt: "That doesn't look like a valid 9-digit routing number. Please re-enter." max_retries: 3 ``` ## LLM inference When `infer: true`, the LLM can extract or infer the field value from conversational context without explicitly asking the user. This enables natural, conversational information collection. ```yaml expandable=true theme={null} GATHER: transfer_type: prompt: "Is this domestic or international?" type: string required: true validate: enum(domestic, international) infer: true currency: prompt: "What currency?" type: string required: true default: "USD" infer: true infer_confidence: 0.9 infer_confirm: false ``` ### Inference properties | Property | Type | Default | Description | | - | - | - | - | | `infer` | `boolean` | `false` | Enable LLM inference for this field | | `infer_confidence` | `number` | `0.8` | Minimum confidence (0.0--1.0) the LLM must have to accept an inferred value | | `infer_confirm` | `boolean` | `true` | Whether to ask the user to confirm inferred values | When the LLM's confidence in an inferred value falls below `infer_confidence`, the runtime falls back to asking the user directly using the `prompt`. Use `extraction_hints` to steer extraction with example surface forms: ```yaml theme={null} GATHER: transfer_type: prompt: "Is this domestic or international?" type: string infer: true extraction_hints: ["If beneficiary country is US, infer domestic. Otherwise international."] ``` ## Extraction strategy The `strategy` property selects how a value is extracted. It can be set per field, and the same enum is available as a block-level default on flow-step gather. | Value | Behavior | | - | - | | `auto` | Let the platform choose the best available strategy (default behavior when unset). | | `ml` | Use the ML/NLU entity model. | | `llm` | Use the LLM to extract the value. | | `pattern` | Use pattern/regex extraction (pairs with `extraction_pattern`). | | `hybrid` | Combine ML and LLM extraction. | ```yaml theme={null} GATHER: phone_number: prompt: "What is your phone number?" type: phone strategy: pattern extraction_pattern: "\\d{3}-\\d{3}-\\d{4}" ``` ## Range collection When `range: true`, the field collects a range with `low` and `high` values instead of a single scalar: ```yaml theme={null} GATHER: budget: prompt: "What is your budget range?" type: number required: true range: true ``` The collected value is stored as an object: `{low: number, high: number}`. ## List collection When `list: true`, the field collects multiple values as an array: ```yaml theme={null} GATHER: dietary_restrictions: prompt: "Do you have any dietary restrictions?" type: string required: false list: true ``` The collected value is stored as an array: `["vegetarian", "no nuts"]`. ## Preference collection When `preferences: true`, the field categorizes user input into preference sets: ```yaml theme={null} GATHER: hotel_amenities: prompt: "What amenities are important to you?" type: string required: false preferences: true ``` The collected value is categorized into: `{accept: [], desire: [], avoid: [], refuse: []}`. ## Progressive activation The `activation` property controls when a field becomes active for collection: | Value | Description | | - | - | | `"required"` | Always active, must be collected (default) | | `"optional"` | Active but not required | | `"progressive"` | Activated progressively as other fields are collected | | `{when: "condition"}` | Activated only when the condition evaluates to true | ```yaml theme={null} GATHER: routing_number: prompt: "What is the ABA routing number?" type: string required: true activation: required depends_on: [transfer_type] swift_code: prompt: "What is the SWIFT/BIC code?" type: string required: true activation: WHEN: "transfer_type == 'international'" depends_on: [transfer_type] ``` ### Field dependencies The `depends_on` property lists fields that must be collected before this field activates. Combined with `activation`, this creates ordered collection flows: ```yaml theme={null} GATHER: destination: prompt: "Where are you traveling?" type: string required: true hotel_class: prompt: "What class of hotel?" type: string required: true depends_on: [destination] activation: progressive ``` ## Corrections handling In flow steps, the `CORRECTIONS: true` property enables natural corrections -- the user can update previously collected values by stating corrections in natural language: ```yaml theme={null} # Within a FLOW step collect_details: GATHER: - name: required - email: required CORRECTIONS: true ``` See [FLOW](/agent-platform/abl-reference/flow) for the full flow step GATHER syntax. ## Transient and sensitive fields ### Sensitive fields Fields marked `sensitive: true` carry PII and receive special handling: `sensitive: true` controls how a value is **displayed and stored** afterward (masking, redaction) — the raw value is still extracted by the LLM and lands in session state during collection. For data that must **never** reach the model, transcript, session state, or logs at all — a full card number, CVV, and similar PCI-scoped values — use [`SECURE_INPUT`](/agent-platform/abl-reference/flow#secure_input) instead of `GATHER`. `SECURE_INPUT` is currently a **`FLOW:`-step-only** action: it has no reasoning-mode/top-level form, so it's only available to agents that have a `FLOW:` section — a pure reasoning-mode agent has no equivalent construct today. ```yaml theme={null} GATHER: ssn_last4: prompt: "Last 4 digits of your Social Security Number?" type: string required: true sensitive: true sensitive_display: mask mask_config: showFirst: 0 showLast: 4 char: "*" ``` When the field name is non-canonical, add `pii_type` so the redactor can produce a shape-preserving mask: ```yaml theme={null} GATHER: member_id: prompt: "Your membership ID?" type: string sensitive: true sensitive_display: mask pii_type: custom # email | phone | ssn | credit_card | address | name | custom ``` Alternatively, bind the field to a **named project PII pattern** with `pii_pattern_ref` instead of `pii_type`: ```yaml theme={null} GATHER: loyalty_number: prompt: "Your loyalty number?" type: string sensitive: true sensitive_display: mask pii_pattern_ref: loyalty_number_pattern ``` `pii_pattern_ref` is a free-form reference — the parser has no project scope, so it can't validate that the named pattern exists. It's resolved at runtime when the field is written; a reference that matches no configured pattern falls back to `pii_type` and emits `pii_pattern_ref_unresolved`. Studio also flags a dangling reference in the gather field editor. ### Sensitive display modes | Mode | Description | Example output | | - | - | - | | `redact` | Replace entire value with placeholder | `[REDACTED]` | | `mask` | Show partial value with masking characters | `****1234` | | `replace` | Replace with a generic description | `[SSN]` | ### Mask configuration | Property | Type | Default | Description | | - | - | - | - | | `showFirst` | `number` | `0` | Number of characters to show at the start | | `showLast` | `number` | `4` | Number of characters to show at the end | | `char` | `string` | `"*"` | Masking character | ### Transient fields Fields marked `transient: true` are automatically cleared from the session after the gather phase completes. Use this for sensitive data that should not persist beyond its immediate use: ```yaml theme={null} GATHER: card_number: prompt: "Card number for verification?" type: string required: true sensitive: true transient: true ``` ## Extraction patterns The `extraction_pattern` property defines a custom regex for extracting structured values from user input. Combined with `extraction_group`, it specifies which capture group to use: ```yaml theme={null} GATHER: routing_number: prompt: "What is the routing number?" type: string required: true extraction_pattern: "(?:routing|aba|transit).*?(\d{9})" extraction_group: 1 ``` | Property | Type | Default | Description | | - | - | - | - | | `extraction_pattern` | `string` | *none* | Regex pattern for value extraction | | `extraction_group` | `number` | `0` | Capture group index (0 = full match) | ## Semantics The `semantics` block provides supplemental metadata that helps the runtime interpret and process field values: ```yaml expandable=true theme={null} GATHER: address: prompt: "What is the delivery address?" type: string required: true semantics: format: "US postal address" components: [street, city, state, zip] transfer_amount: prompt: "How much to transfer?" type: number required: true semantics: unit: "USD" convert_to: "cents" ``` ### Semantics properties | Property | Type | Description | | - | - | - | | `format` | `string` | Expected format description | | `components` | `string[]` | Sub-components of the value | | `unit` | `string` | Unit of measurement | | `lookup` | `string` | Lookup table reference for validation | | `convert_to` | `string` | Target unit for conversion | | `locale` | `string` | Locale for parsing | | `temporal` | `string` | Temporal direction for date/datetime fields: `future`, `past`, or `any` (default). `future` biases to the next occurrence and rejects past dates; `past` rejects future dates. | | `enum_set` | `string[]` | Allowed enumeration values (alternative to top-level `options`). | | `kore_entity_type` | `string` | Kore entity type hint used for recognition. | ## Complete example ```yaml expandable=true theme={null} GATHER: source_account: prompt: "Which account would you like to send from?" type: string required: true transfer_type: prompt: "Is this a domestic or international wire?" type: string required: true validate: enum(domestic, international) infer: true beneficiary_name: prompt: "Full legal name on the beneficiary's account?" type: string required: true amount: prompt: "How much would you like to send?" type: number required: true validate: min(1) currency: prompt: "What currency? (for example. USD, EUR, GBP)" type: string required: true default: "USD" infer: true purpose_code: prompt: "Purpose of this transfer?" type: string required: true infer: true reference: prompt: "Reference or memo for the beneficiary? (optional)" type: string required: false ``` *** **Related articles:** * [Tools](/agent-platform/abl-reference/tools) -- tool definitions (tools and gather work together) * [FLOW](/agent-platform/abl-reference/flow) -- GATHER within flow steps uses a different syntax * [Language overview](/agent-platform/abl-reference/language-overview) -- syntax rules for lists and blocks # Guardrails Source: https://koreai.mintlify.app/agent-platform/abl/reference/guardrails Guardrails are safety checks that evaluate agent inputs and outputs to detect harmful, non-compliant, or malformed content. Unlike [constraints](/agent-platform/abl-reference/memory-and-constraints#constraints) (which enforce business rules), guardrails protect against safety and quality violations at the content level. The `GUARDRAILS:` block defines named guardrail rules. ## Overview ABL guardrails use a three-tier evaluation model: 1. **CEL-based** (Tier 1) -- fast, deterministic expression checks. 2. **Model-based** (Tier 2) -- pre-trained safety classification models (for example, OpenAI moderation). 3. **LLM-based** (Tier 3) -- natural language checks evaluated by an LLM. Each guardrail specifies an application point (when to check), a check expression or prompt, and an action to take when the check fails. ```yaml theme={null} GUARDRAILS: profanity_filter: kind: input check: not_contains_blocked_words(input) action: block message: "Your message was blocked. Please keep the conversation respectful." priority: 1 pii_output_prevention: kind: output check: not_contains_ssn(output) action: redact message: "Sensitive information has been redacted." priority: 0 ``` ## Application points The `kind` property determines when the guardrail is evaluated during the agent's processing pipeline. | Kind | Evaluation point | | - | - | | `input` | Before the user's message reaches the LLM. | | `output` | After the LLM generates a response, before it is sent to the user. | | `both` | Evaluated on both input and output. | | `tool_input` | Before parameters are sent to a tool call. | | `tool_output` | After a tool returns its result, before the result enters the LLM context. | | `handoff` | Before context is passed to another agent during a handoff. | ## Guardrail properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `name` | `string` | Yes | -- | Unique identifier for the guardrail (the YAML key). | | `kind` | `string` | Yes | -- | Application point. See [Application points](#application-points). | | `check` | `string` | No | -- | CEL expression to evaluate (Tier 1). Omit for model-based or LLM-based. | | `action` | `string` | Yes | -- | Action when the check fails. See [Actions](#actions). | | `message` | `string` | No | -- | Human-readable message displayed or logged when the guardrail triggers. | | `priority` | `number` | No | `100` | Evaluation priority. Lower values are evaluated first. | | `provider` | `string` | No | -- | Model provider name for Tier 2 checks (for example. `openai_moderation`). | | `category` | `string` | No | -- | Safety taxonomy category for Tier 2 (for example. `hate`, `violence`). | | `threshold` | `number` | No | -- | Score threshold (0.0--1.0) for model-based checks. | | `llm_check` | `string` | No | -- | Natural language prompt for Tier 3 LLM-based checks. | | `severity_actions` | `object` | No | -- | Per-severity action overrides. See [Graduated actions](#graduated-actions). | | `fix_strategy` | `string` | No | -- | Fix strategy when `action: fix`. See [Fix strategies](#fix-strategies). | | `fix_expression` | `string` | No | -- | CEL expression for the `custom` fix strategy. | | `max_reasks` | `number` | No | `2` | Maximum reask attempts when `action: reask`. | | `filter_min_length` | `number` | No | -- | Minimum content length after filtering. Below this threshold, block instead. | | `streaming` | `boolean` | No | `false` | Enable mid-stream evaluation for streaming responses. | | `streaming_interval` | `string` | No | -- | Streaming evaluation granularity. See [Streaming evaluation](#streaming-evaluation). | ## Actions The `action` property determines the runtime behavior when a guardrail check fails. | Action | Behavior | | - | - | | `block` | Reject the content entirely. For input, the user message is discarded. For output, the response is withheld. | | `warn` | Allow the content through but emit a warning event. The `message` is logged, not sent to the user. | | `redact` | Replace the offending content with a redaction marker and continue. The sanitized content is passed through. | | `escalate` | Trigger human escalation for review. The content is held pending human decision. | | `fix` | Automatically repair the content using a fix strategy. See [Fix strategies](#fix-strategies). | | `reask` | Reject the LLM output and re-prompt with the guardrail's message appended as additional guidance. | | `filter` | Remove the offending portions while preserving the rest of the content. | ## Three-tier implementation ### Tier 1: CEL-based checks CEL (Common Expression Language) checks are fast, deterministic rules evaluated without calling an external model. Use the `check` property with a CEL expression. ```yaml theme={null} GUARDRAILS: length_limit: kind: output check: length(output) < 10000 action: warn message: "Response exceeds recommended length." ssn_detection: kind: input check: not_matches_pattern(input, "\\b\\d{3}-\\d{2}-\\d{4}\\b") action: redact message: "SSN detected and redacted." ``` #### Content, session-state, and tool-result variables Every `check` is evaluated with a content variable already bound to the kind's own name — `input`, `output`, `tool_input`, `tool_output`, or `handoff` — and **`text` is always bound too, to that same checked content, regardless of `kind`**. Prefer `text` for a check you intend to reuse across kinds. `content` is an alias of `text`, but it (along with two more builtins, `session_state` — a masked view of session values — and `tool_results` — this turn's bounded tool results) is bound only when the agent sets `EXECUTION: guardrail_builtins: true`: ```yaml theme={null} EXECUTION: guardrail_builtins: true GUARDRAILS: no_leak: kind: output check: not_contains_pii(content) action: block ``` Without `guardrail_builtins: true`, a check that reads `content`, `session_state`, or `tool_results` is unbound: it errors at evaluation time and the content passes through unchecked (fails open). The compiler warns `GUARDRAIL_BUILTIN_NOT_BOUND` for this — write `text` instead, or opt in. And if the agent separately declares a session variable literally named `content`, turning the opt-in on makes `content` resolve to the checked text instead of that session variable — the compiler warns `GUARDRAIL_CONTENT_SHADOWS_SESSION_VAR`; rename the session variable, or use `text` for the checked text. ### Tier 2: Model-based checks Model-based checks use a pre-trained classification model to score content. You specify a `provider`, an optional `category`, and a `threshold`. ```yaml theme={null} GUARDRAILS: toxicity_detection: kind: input provider: openai_moderation category: hate threshold: 0.7 action: block message: "Content flagged for hateful language." ``` ### Tier 3: LLM-based checks LLM-based checks use a natural language prompt evaluated by an LLM. Use the `llm_check` property with a descriptive prompt. ```yaml theme={null} GUARDRAILS: medical_advice_check: kind: output llm_check: "Does this response provide specific medical diagnoses or prescribe medication? Answer YES or NO." action: block message: "I'm not able to provide medical diagnoses. Please consult a healthcare professional." ``` ## Fix strategies When `action: fix`, the `fix_strategy` property determines how content is repaired. | Strategy | Behavior | | - | - | | `truncate` | Truncate content to the maximum allowed length. | | `strip_html` | Remove HTML tags from the content. | | `redact_pii` | Detect and replace PII patterns with redaction markers. | | `normalize` | Normalize whitespace, encoding, and special characters. | | `custom` | Apply a custom CEL expression defined in `fix_expression`. | ### Example: fix with truncation ```yaml theme={null} GUARDRAILS: response_length: kind: output check: length(response) <= 5000 action: fix fix_strategy: truncate message: "Response was trimmed to fit the maximum length." ``` ### Example: custom fix expression ```yaml theme={null} GUARDRAILS: normalize_whitespace: kind: output check: not_contains_excessive_whitespace(response) action: fix fix_strategy: custom fix_expression: "collapse_whitespace(response)" ``` ## Graduated actions Use `severity_actions` to apply different actions based on the severity of the violation. The keys are severity labels and the values are action names. ```yaml theme={null} GUARDRAILS: content_safety: kind: output provider: openai_moderation threshold: 0.5 action: warn severity_actions: low: warn medium: reask high: block message: "Content flagged by safety model." ``` ## Streaming evaluation For streaming responses, guardrails can evaluate content as it is generated rather than waiting for the complete response. | Property | Values | Description | | - | - | - | | `streaming` | `true`, `false` | Enable mid-stream evaluation. | | `streaming_interval` | `token`, `sentence`, `chunk_size` | Granularity of streaming evaluation. | ```yaml theme={null} GUARDRAILS: realtime_safety: kind: output provider: openai_moderation threshold: 0.8 action: block streaming: true streaming_interval: sentence message: "Response generation halted due to safety concern." ``` When a streaming guardrail triggers, the response generation is halted at the current point and the `message` is sent to the user. ## Reask behavior When `action: reask`, the runtime rejects the LLM output, appends the guardrail's `message` as additional guidance, and re-prompts. The `max_reasks` property controls how many times this can happen before falling back to a block. ```yaml theme={null} GUARDRAILS: factual_grounding: kind: output llm_check: "Does this response make claims not supported by the provided context?" action: reask max_reasks: 3 message: "Stick to information from the provided context. Do not make unsupported claims." ``` ## Priority and evaluation order Guardrails are evaluated in order of `priority` (lower values first). When multiple guardrails have the same priority, they are evaluated in declaration order. A `block` action from any guardrail stops further evaluation. `warn` actions do not stop evaluation; all subsequent guardrails continue to run. ## Built-in guardrail templates ABL ships a set of built-in, CEL-based (Tier 1) guardrail templates focused on prompt-injection and secret-leak protection: | Template | Kind | Detects | Action | | - | - | - | - | | `detect_instruction_override` | input | Attempts to override or ignore system instructions | `warn` | | `detect_role_manipulation` | input | Attempts to manipulate the AI's role or persona | `warn` | | `detect_system_prompt_extraction` | input | Attempts to extract the system prompt | `warn` | | `detect_encoding_tricks` | input | Encoding-based obfuscation (base64, rot13, hex) | `warn` | | `detect_credential_leak` | output | Leaked credentials, API keys, or tokens in output | `redact` | You can also author your own guardrails for domain-specific concerns (account-number masking, SSN redaction, profanity, etc.) using the tiers described above. ## Complete example ```yaml expandable=true theme={null} GUARDRAILS: account_number_masking: kind: output check: not_contains_full_account_number(response) action: redact message: "Account numbers are masked. Only the last 4 digits are displayed." priority: 0 credential_input: kind: input check: not_contains_credentials(input) action: redact message: "Please never share passwords or PINs in this chat." priority: 0 credit_card_detection: kind: input check: not_matches_pattern(input, "\\b\\d{4}[- ]?\\d{4}[- ]?\\d{4}[- ]?\\d{4}\\b") action: redact message: "Credit card number redacted for your security." toxicity_check: kind: output check: toxicity_score(response) < 0.5 action: block message: "Response blocked due to potential harmful content." priority: 1 ``` *** **Related articles:** * [Memory & Constraints](/agent-platform/abl-reference/memory-and-constraints#constraints) -- business rule enforcement (distinct from content safety) * [Expressions & functions](/agent-platform/abl-reference/expressions-and-functions) -- CEL expression syntax for `check` properties * [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor) -- ESCALATE action for human review # ABL overview Source: https://koreai.mintlify.app/agent-platform/abl/reference/language-overview Agent Blueprint Language (ABL) is the enterprise control plane for agentic AI — a schema-driven language purpose-built for multi-agent orchestration where deterministic governance meets autonomous reasoning. ABL spans the full control spectrum: delegate autonomously, supervise selectively, or lock down as a deterministic state machine. Agent definitions compile into immutable artifacts, and AI can author blueprints just as humans do. ## Design principles ABL agents are goal-driven rather than script-driven. Rather than encoding an explicit numbered state machine, you declare the outcome the agent should achieve and the rules it must respect, and the runtime reasons toward that goal — falling back to deterministic execution wherever you need it. 1. **Goal-oriented** — agents work toward a declared `GOAL:`, not through hard-coded scripts. 2. **Constraint-guarded** — business rules are enforced by `CONSTRAINTS:` and `GUARDRAILS:`, independent of step order. 3. **Memory-enabled** — agents can remember user and session state across turns and sessions. 4. **Composable** — agents can `DELEGATE:` to sub-agents or `HANDOFF:` to peers and supervisors. 5. **Human-in-the-loop** — `ESCALATE:` provides explicit paths to human operators. Reasoning and determinism are not either/or. An agent reasons by default; adding a `FLOW:` section lets you make any part of the conversation deterministic, step by step (see [Auto-detection](#auto-detection)). ## File structure An ABL document is a plain-text file composed of top-level **sections**, each introduced by an uppercase keyword followed by a colon. Sections can appear in any order, but by convention the `AGENT:` declaration comes first. ```yaml expandable=true theme={null} AGENT: Customer_Support GOAL: | Help customers resolve billing questions. PERSONA: | Friendly, patient support representative. TOOLS: lookup_account(account_id: string) -> {name: string, balance: number} description: "Retrieve account details" type: http endpoint: "/api/accounts/lookup" method: POST GATHER: account_id: prompt: "What is your account number?" type: string required: true ``` ### File extensions | Extension | Contents | | - | - | | `.agent.abl` | Agent definition (most common) | | `.tools.abl` | Reusable tool library (see [Tool file imports](/agent-platform/abl-reference/tools#tool-file-imports)) | | `.agent.yaml` | Agent definition in YAML format | ### Required sections Every agent document must contain: * `AGENT:` -- the agent name * `GOAL:` -- the agent's objective All other sections are optional. If a section is omitted, its value defaults to empty (empty list, empty object, or platform defaults as specified in each section's reference). ### Recognized top-level sections | Section | Purpose | Reference | | - | - | - | | `AGENT:` | Agent name declaration | [Agent declaration](/agent-platform/abl-reference/agent-declaration) | | `VERSION:` | Semantic version | [Agent declaration](/agent-platform/abl-reference/agent-declaration#version) | | `DESCRIPTION:` | Human-readable description | [Agent declaration](/agent-platform/abl-reference/agent-declaration#description) | | `LANGUAGE:` | Agent language code | [Agent declaration](/agent-platform/abl-reference/agent-declaration#language) | | `GOAL:` | Agent objective | [Agent declaration](/agent-platform/abl-reference/agent-declaration#goal) | | `PERSONA:` | Agent personality description | [Agent declaration](/agent-platform/abl-reference/agent-declaration#persona) | | `LIMITATIONS:` | Explicit boundaries | [Agent declaration](/agent-platform/abl-reference/agent-declaration#limitations) | | `IDENTITY:` | Combined identity block | [Agent declaration](/agent-platform/abl-reference/agent-declaration#identity) | | `INSTRUCTIONS:` | Operational instructions | [Agent declaration](/agent-platform/abl-reference/agent-declaration#instructions) | | `EXECUTION:` | Model and runtime configuration | [Agent declaration](/agent-platform/abl-reference/agent-declaration#execution-configuration) | | `TOOLS:` | Tool definitions | [Tools](/agent-platform/abl-reference/tools) | | `GATHER:` | Information collection fields | [GATHER](/agent-platform/abl-reference/gather) | | `FLOW:` | Structured execution steps | [FLOW](/agent-platform/abl-reference/flow) | | `MEMORY:` | Session and persistent state | [Memory & Constraints](/agent-platform/abl-reference/memory-and-constraints) | | `CONSTRAINTS:` | Business rule enforcement | [Memory & Constraints](/agent-platform/abl-reference/memory-and-constraints#constraints) | | `GUARDRAILS:` | Input/output safety checks | [Guardrails](/agent-platform/abl-reference/guardrails) | | `DELEGATE:` | Sub-agent delegation | [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor#delegate) | | `HANDOFF:` | Agent transfer | [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor#handoff) | | `ESCALATE:` | Human escalation | [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor#escalate) | | `COMPLETE:` | Completion conditions | [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor#complete) | | `ON_ERROR:` | Error handlers | [Lifecycle & Hooks](/agent-platform/abl-reference/lifecycle-and-hooks#on_error-handlers) | | `ON_START:` | Session initialization | [Lifecycle & Hooks](/agent-platform/abl-reference/lifecycle-and-hooks#on_start-handler) | | `MESSAGES:` | Customizable system messages | [Lifecycle & Hooks](/agent-platform/abl-reference/lifecycle-and-hooks#messages) | | `TEMPLATES:` | Reusable response templates | [Rich content](/agent-platform/abl-reference/rich-content) | | `HOOKS:` | Lifecycle event handlers | [Lifecycle & Hooks](/agent-platform/abl-reference/lifecycle-and-hooks#hooks) | | `NLU:` | Natural language understanding | [NLU](/agent-platform/abl-reference/nlu) | | `MULTI_INTENT:` | Multi-intent configuration | [NLU](/agent-platform/abl-reference/nlu#multi-intent-handling-multi_intent) | | `INTENTS:` | Intent categories (routing) | [NLU](/agent-platform/abl-reference/nlu#intent-categories-intents-section) | | `ENTITIES:` | Reusable named entities | [NLU](/agent-platform/abl-reference/nlu#named-entities-entities-section) | | `LOOKUP_TABLES:` | Reference data for validation | [Data Types & Utilities](/agent-platform/abl-reference/data-types-and-utilities#lookup-tables) | | `SYSTEM_PROMPT:` | Custom system prompt template | [Agent declaration](/agent-platform/abl-reference/agent-declaration#system_prompt) | | `ATTACHMENTS:` | File/media collection | [Data Types & Utilities](/agent-platform/abl-reference/data-types-and-utilities#attachments) | | `DESTINATIONS:` | Outbound HTTP delivery targets | [Data Types & Utilities](/agent-platform/abl-reference/data-types-and-utilities#destinations) | | `TRANSFER_FLOW_POLICIES:` | Named policies referenced by escalation routing | [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor#transfer-flow-policies) | | `RETURN_HANDLERS:` | Named parent-resume handlers for returnable handoffs | [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor#return-handlers) | | `ACTION_HANDLERS:` | Reusable interactive-action handlers | [Rich Content & Expressions](/agent-platform/abl-reference/rich-content#reusable-handlers-action_handlers) | | `CONVERSATION:` | Conversation-behavior configuration | [Behavior profiles](/agent-platform/abl-reference/behavior-profiles#conversation) | `TESTS:` is a reserved section name the parser accepts but currently **ignores** — content under it is parsed and discarded with no effect. It exists to avoid "unknown section" errors; do not rely on it for behavior. ## Syntax rules ### Indentation ABL uses indentation to express nesting. Use **spaces** (two or more) for indentation. Tabs are accepted but spaces are preferred for consistency. Content nested under a section keyword must be indented further than the keyword line. ```yaml theme={null} TOOLS: search(query: string) -> {results: string[]} description: "Search the catalog" type: http endpoint: "/api/search" method: GET ``` ### Keywords Section keywords are followed by a colon (`:`). In the `.agent.abl` format, section keywords **must be uppercase** (`AGENT:`, `GOAL:`, `TOOLS:`, …). In the `.agent.yaml` format, they **must be lowercase** (`agent:`, `goal:`, …). Mixed case is not supported, and the two formats do not accept each other's casing. ### Comments Lines beginning with `#` are comments and are ignored by the parser. Comments can appear anywhere in the file. ```yaml theme={null} # This is a comment AGENT: My_Agent GOAL: "Help with orders" # Inline comments are NOT supported in ABL format ``` ### Strings Strings can be written in several forms: | Form | Syntax | Use case | | - | - | - | | Quoted | `"value"` | Single-line values | | Unquoted | `value` | Simple values without special characters | | Pipe block | \| followed by indented lines | Multi-line text (preserves newlines) | ```yaml theme={null} GOAL: "Single line goal" GOAL: | Multi-line goal that preserves line breaks within the block. LANGUAGE: "en" ``` ### Lists Lists use YAML-style `- item` syntax with indentation: ```yaml theme={null} LIMITATIONS: - "Cannot access external systems" - "Cannot process payments directly" ``` ## Auto-detection The parser determines the document type from the first meaningful keyword it encounters: | First keyword | Document type | | - | - | | `AGENT:` | Agent document | | `SUPERVISOR:` | Supervisor document | | `BEHAVIOR_PROFILE:` | Behavior profile document | | `TOOLS:` (at root level in a `.tools.abl` file) | Tool file document | Within an agent document, execution mode is **not** declared via a global `MODE:` keyword. Instead, agents operate in reasoning mode by default -- the LLM decides which tools to call and when, guided by the `GOAL:` and `PERSONA:`. Any agent can optionally include a `FLOW:` section to add structured execution steps. Adding a `FLOW:` section gives the agent a step-by-step execution graph. Each step within the flow declares `REASONING: true` or `REASONING: false` to control whether that individual step uses LLM reasoning or deterministic execution. The `MODE:` keyword is deprecated and produces a parser error if used. Remove it and use per-step `REASONING:` declarations within `FLOW:` instead. ## YAML format ABL supports an alternative YAML format for agent definitions. YAML files use `.agent.yaml` as their extension. The parser auto-detects YAML format by checking whether the first non-comment, non-empty lines use lowercase keys matching known ABL sections (`agent`, `goal`, `persona`, `tools`, etc.). ```yaml expandable=true theme={null} agent: Customer_Support goal: | Help customers resolve billing questions. persona: | Friendly, patient support representative. tools: - name: lookup_account parameters: - name: account_id type: string returns: type: object fields: name: string balance: number description: Retrieve account details type: http endpoint: /api/accounts/lookup method: POST ``` Both formats produce the same intermediate representation (AST) and compile to identical runtime artifacts. ## Version declaration The optional `VERSION:` directive specifies the document version in semver format: ```yaml theme={null} VERSION: "2.0.0" ``` When omitted, the version defaults to `"1.0.0"`. The version is stored in the document metadata and can be used for compatibility tracking. ## Compilation ABL source files are compiled to a typed intermediate representation (**AgentIR**) at deploy time. The runtime loads and executes the IR directly — there is no code-generation step. ```yaml theme={null} ABL source (.agent.abl / .agent.yaml) │ ▼ Parser → parses the document into an AST │ ▼ Compiler → produces a CompilationOutput: a map of AgentIR │ instances (one per agent, including supervisors) │ plus the entry agent ▼ Runtime → loads the IR and executes it (reasoning loop, flow steps, routing, constraint checking) ``` Each agent (and supervisor) compiles to a single `AgentIR` that gathers the document's sections into typed configuration — identity (from `GOAL`/`PERSONA`/`LIMITATIONS`, including a generated system prompt), tools, gather, constraints and guardrails, coordination (handoffs/delegates/escalation), completion, and memory. Supervisors are ordinary agents with routing populated, so a project's agents and supervisors all live in one registry and can compose hierarchically. ## Template interpolation Throughout ABL, string values support template interpolation using double-brace syntax: ```text theme={null} "Hello, {{customer_name}}. Your balance is {{balance}}." ``` Conditional blocks use Handlebars-style helpers: ```text theme={null} "{{#if exchange_rate}}Rate: {{exchange_rate}}{{/if}}" ``` Template expressions are resolved at runtime against session variables and tool results. # Lifecycle and hooks Source: https://koreai.mintlify.app/agent-platform/abl/reference/lifecycle-and-hooks ABL provides lifecycle handlers and hooks that execute at specific points in an agent's execution cycle. These allow you to initialize state, run side effects, and handle errors without embedding that logic in the main conversation flow. ## Overview | Handler | When it fires | | - | - | | `ON_START` | Once per session, before any user input. | | `HOOKS` | At defined lifecycle points (before/after agent, turn). | | `ON_ERROR` | When a specific error type occurs during execution. | ## ON\_START handler The `ON_START` handler executes once when a new session initializes, before the agent processes any user input. Use it for greeting messages, initial tool calls, and variable initialization. ### Syntax ```yaml theme={null} ON_START: SET: session_initialized = true transfer_status = "pending" retry_count = 0 CALL: check_returning_user() RESPOND: | Welcome to Wire Transfer Services. I can help you send a domestic or international wire transfer. Which account would you like to send from? ``` ### ON\_START properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `SET` | `Record` | No | -- | Variable assignments executed at session start. | | `CALL` | `string` | No | -- | Tool to call during initialization (supports `WITH:`/`AS:`). | | `DELEGATE` | `string` | No | -- | Agent to delegate to during initialization. | | `RESPOND` | `string` | No | -- | Greeting or welcome message sent to the user. | | `MESSAGE_KEY` | `string` | No | -- | Locale catalog key used to localize the greeting. | | `VOICE` | `object` | No | -- | Voice-specific overrides for the response. See [Rich content](/agent-platform/abl-reference/rich-content). | | `RICH_CONTENT` | `object` | No | -- | Rich content format variants (the `.agent.abl` keyword is `FORMATS:`). See [Rich content](/agent-platform/abl-reference/rich-content). | | `ACTIONS` | `object` | No | -- | Interactive actions attached to the response. | | `BRANCHES` | `object[]` | No | -- | Conditional greeting branches (`IF`/`ELSE`). See [Conditional greeting](#conditional-greeting). | ### Template references in ON\_START You can reference named templates in the RESPOND value: ```yaml theme={null} ON_START: RESPOND: TEMPLATE(welcome) ``` This renders the template named `welcome` from the agent's `TEMPLATES:` block. ### ON\_START with tool call ```yaml theme={null} ON_START: CALL: check_cut_off_times(transfer_type = "all") SET: sanctions_clear = false fraud_score = 0 RESPOND: "Welcome. Let me check today's processing windows." ``` The tool call executes first, and its result is available in the session context when the RESPOND message is rendered. ### Guarding a CALL with WHEN An `ON_START` or hook `CALL` can carry an indented `WHEN:` guard so it only runs when the condition holds: ```yaml theme={null} ON_START: CALL: find_member_by_ani WITH: callerAni: ani_national WHEN: ani_national != "" HOOKS: before_agent: CALL: load_profile WHEN: member_id != "" ``` The `WHEN:` must be indented **under** `CALL:`, not written as its sibling at the same indent. A sibling `WHEN:` is not read — the call runs unconditionally, and the parser warns `W496`. ### Conditional greeting Use a `BRANCHES:` block to vary the greeting based on session context. Each branch is an `IF` (or `CONDITION`) with a fallback `ELSE`, and can carry `RESPOND`, `MESSAGE_KEY`, `VOICE`, rich content, and `ACTIONS`. ```yaml theme={null} ON_START: CALL: check_returning_user() BRANCHES: - IF: user.is_returning == true RESPOND: "Welcome back, {{user.name}}!" - ELSE: RESPOND: "Welcome! How can I help you today?" ``` ### ON\_START language resolution A reasoning agent with no FLOW can resolve the caller's language from a startup `SET`/`CALL` and apply it before the welcome renders: ```yaml theme={null} AGENT: Welcome_Agent GOAL: "Help the caller" TOOLS: resolve_runtime_context_v1() -> object description: "Resolve the caller language and greeting" ON_START: CALL: resolve_runtime_context_v1 AS: runtime_context SET_INTERACTION_LANGUAGE: "{{runtime_context.spoken_language}}" RESPOND: "{{runtime_context.greeting_text}}" ``` The tool needs an execution binding in the project. Top-level language directives resolve after top-level `SET`/`CALL` and before branch selection; a `BRANCHES` `DO:` list (below) can set the language too. On **Gemini Live** specifically, `ON_START` already runs before the initial provider setup, so the first connection uses this language in legacy, `runtime_guarded`, and `runtime_owned` orchestration modes — the channel must have the language configured in its own supported-language policy, or an unresolved/unsupported value is rejected without changing the policy default. ### Start-time HANDOFF A `BRANCHES:` entry (or a bare top-level `HANDOFF:`) can hand the session to another declared local agent at start, before the first customer message is even understood. A branch's `DO:` list runs these actions in order, by default *after* the welcome renders: | Action | Effect | | - | - | | `SET:` | Assigns session values. | | `SET_INTERACTION_LANGUAGE:` / `CLEAR_INTERACTION_LANGUAGE:` | Sets or clears the voice language, same as elsewhere. | | `CALL:` | Calls a tool (`WITH:`/`AS:`, as in a FLOW step `DO:`). A failed call stops the list. | | `HANDOFF: ` | Hands off through the normal handoff lifecycle — the target's declared `WHEN`, `RETURN`, `CONTEXT`, `EXPERIENCE_MODE`, `ON_RETURN`, and `ON_FAILURE` all apply as usual. At most one handoff per list. | If the list sets the interaction language, the prefix through the last language directive (before any `HANDOFF`) runs *before* the welcome renders instead, and the rest after: ```yaml theme={null} SUPERVISOR: Router GOAL: "Route customers by the page they came from" TOOLS: get_welcome(page: string) -> object description: "The welcome for the page the customer came from" HANDOFF: - TO: Product_Search RETURN: true CONTEXT: pass: [page_name, welcome] - TO: Order_Agent RETURN: false ON_START: CALL: get_welcome WITH: page: page_name AS: welcome BRANCHES: - IF: page_name == "beauty_hub_screen" RESPOND: "{{welcome.text}}" DO: - SET: entry_source = "app" - HANDOFF: Product_Search - ELSE: RESPOND: "Hi, how can I help?" ``` A top-level `HANDOFF: ` (bare, bulleted, or the sole entry of a top-level `DO:`) runs only when **no branch matched** — a matched branch with no `DO:` list never falls back to it. With no branches at all, a bare `HANDOFF:` is instead decided by that declared route's own `WHEN` (and `ENABLED_WHEN`), checked once at session start: ```yaml theme={null} SUPERVISOR: Router GOAL: "Send customers who came from the sales page to Sales" HANDOFF: - TO: Sales WHEN: page_name == "sales" RETURN: false ON_START: RESPOND: "Welcome." HANDOFF: Sales ``` `DELEGATE` and any other action in an `ON_START` list is dropped with a warning (`W380`-`W388`) — it is never run from here. A handoff target that isn't a declared local `HANDOFF` entry is a compile error (`ON_START_HANDOFF_TARGET_INVALID`). The start-time handoff is local only: a route `WHEN` that only a model could judge (natural language, `intent.*`) is never satisfied at start, and a remote (A2A) target is always refused. | Diagnostic | Severity | Catches | | - | - | - | | `ON_START_HANDOFF_ACTIVATED` / `ON_START_BRANCH_DO_ACTIVATED` | warning (informational) | Confirms a top-level `HANDOFF`, or a branch's `DO:` list, really does run at session start — a reminder, not a mistake. | | `ON_START_HANDOFF_UNREACHABLE` | warning | A top-level `HANDOFF:` is dead code because an `ELSE` branch always matches first, so the fallback handoff can never run. | | `ON_START_DELEGATE_WITH_HANDOFF` | warning | `ON_START` configures both a top-level `DELEGATE:` and a handoff — when the handoff runs, the child owns the session and the delegate is never returned. Keep one or the other. | **How it runs, by start type:** * **Eager start** (no customer message yet — Web SDK/web-debug connect, pipeline voice answer, an identified agent-assist welcome): the welcome is delivered, then the child starts on its own with no synthetic customer message and speaks its own opener. Order: entry agent's `before_agent` output, the welcome, the handoff announcement (if the experience mode speaks one), the child's `before_agent` output, the child's own first output. A `RETURN: true` child stays active after that unanswered opener; ordinary return rules apply from the customer's first real message on. * **Lazy start** (the first customer message starts the session — HTTP chat, LiveKit, SDK `initialMessage`): the welcome is **not** delivered, and that first message is forwarded to the child once — after the entry agent's input guardrails/constraints (and, for a FLOW entry, its `before_turn` hook) — taking precedence over the entry agent's own first-turn routing. * **Realtime speech-to-speech** sessions run the list's `SET`/`CALL` but never the handoff itself; the entry agent keeps the session. Every outcome is recorded in the `dsl_on_start_handoff` trace (`executed`, `blocked`, `failed`, `call_failed`, `superseded_by_tool_hook`, `skipped_mode`) with a reason and identifiers only — never customer data. When the handoff doesn't run: a route `WHEN` simply not met shows nothing, not even `ON_FAILURE` text; a refused or failed handoff (a handoff guardrail, an unloaded target) shows only what the route's `ON_FAILURE` authors (`RESPOND` shows its text; `CONTINUE`, or no `ON_FAILURE`, shows nothing — never a generic failure message) — except `ESCALATE`, which escalates the session instead of keeping it with the entry agent; a target an `ENABLED_WHEN` switched off follows the normal authored-route rule (use `ON_FAILURE: CONTINUE` to keep the start silent). After the start, the route behaves as an ordinary handoff — `RETURN`, `CONTEXT`, and `ON_RETURN` apply as usual — and the child never runs its own `ON_START` (which fires once per session, for the entry agent only). A route `WHEN` that reads start data stays true for the rest of the session, so a later turn can still route back to that agent after it returns. If the handoff should really happen only at start, put the condition on a branch `IF:` and leave the route's own `WHEN` unset, or clear the triggering value on return. Pairing with `ENABLED_WHEN` cleared on return (`ENABLED_WHEN: promo_open == true` with `ON_RETURN: set: promo_open = false`) also stops the model from choosing that agent on later turns. Caller data asserted by the browser (public-key SDK init) is **not verified**. When a start-time routing decision matters, mint the caller data on your own server through the customer bootstrap endpoint rather than trusting a client-asserted value in a branch `IF:` or route `WHEN`. ## HOOKS The `HOOKS:` block defines actions that run at four lifecycle points. Unlike `ON_START`, hooks fire repeatedly throughout the session. ### Syntax ```yaml expandable=true theme={null} HOOKS: before_agent: SET: agent_start_time = NOW() CALL: load_user_preferences() after_agent: CALL: save_session_summary() before_turn: SET: turn_start_time = NOW() after_turn: CALL: log_audit_event() SET: turn_count = ADD(turn_count, 1) ``` ### Hook points | Hook | When it fires | | - | - | | `before_agent` | Once when the agent is first activated (before it processes its first message). | | `after_agent` | Once when the agent completes or is deactivated. | | `before_turn` | Before each user message is processed. | | `after_turn` | After each turn completes (after the agent's response is sent). | ### Hook action properties Each hook supports the same set of action properties: | Property | Type | Required | Default | Description | | - | - | - | - | - | | `SET` | `Record` | No | -- | Variable assignments. | | `CALL` | `string` | No | -- | Tool to call (supports `WITH:`/`AS:`). | | `RESPOND` | `string` | No | -- | Message to send (uncommon in hooks, but supported). | | `VOICE` | `object` | No | -- | Voice-specific overrides. | | `RICH_CONTENT` | `object` | No | -- | Rich content format variants. | | `ACTIONS` | `object` | No | -- | Interactive actions. | | `SET_INTERACTION_LANGUAGE` | `string` | No | -- | Set the authoritative session interaction language — a literal BCP-47 tag (for example `es`, `es-ES`, `fr-CA`) or a dynamic value that resolves to one at runtime. Subsequent actions and turns use it. | | `CLEAR_INTERACTION_LANGUAGE` | `boolean` | No | -- | Remove the interaction-language override, reverting to automatic detection. Takes no value. | | `critical` | `boolean` | No | `false` | If `true`, a hook failure aborts the turn; otherwise the failure is logged and execution continues. | `SET_INTERACTION_LANGUAGE` and `CLEAR_INTERACTION_LANGUAGE` are action-list actions — like `SET` and `CLEAR`, they can appear in any action list (`ON_START`, `HOOKS`, `ON_ACTION` handlers). Use them to pin the language the agent responds in — for example, after detecting a caller's preferred language or reading it from a profile. The value is classified when the agent is compiled: 1. **Literal tag** — a value that canonicalizes as BCP-47 (`es`, `fr-CA`, …) is validated and fixed at compile time. 2. **Dynamic value** — anything else that looks like a session-variable path (`user.preferred_language`), a `{{template}}`, or a `${dotted.path}` expression is resolved at runtime, using the same value resolution as an ordinary `SET`. 3. Anything that is neither a valid literal tag nor a recognized dynamic form is a compile error. ```yaml theme={null} HOOKS: before_turn: # Dynamic — resolved from session state each time the hook runs - SET_INTERACTION_LANGUAGE: "{{user.preferred_language}}" # Equivalent dotted-path and ${...} forms are also accepted: # - SET_INTERACTION_LANGUAGE: user.preferred_language # - SET_INTERACTION_LANGUAGE: "${user.preferred_language}" ON_START: # Literal — validated once at compile time - SET_INTERACTION_LANGUAGE: "es-MX" ``` A dynamic value is resolved and re-validated at **runtime**: if it doesn't resolve to a canonical BCP-47 tag, or resolves to a language the voice channel isn't configured to support, the override is not applied and the session keeps its current language. ### WHEN / RESPOND branches A hook can respond conditionally: a `WHEN: ` with an indented `RESPOND: ""` is a **respond branch**. Branches are checked in declaration order, after the hook's own `CALL` and `SET` run, and the first branch whose condition holds wins: ```yaml theme={null} HOOKS: before_turn: WHEN: email_verified && !zip_supplied RESPOND: "Thanks. What is the ZIP code on the account?" WHEN: account_locked == true RESPOND: "This account is locked. I'll connect you with support." ``` * In a **reasoning agent's `before_turn` hook**, a matched branch's text becomes the turn's reply and the model is not called for that turn. Input guardrails and escalation triggers still run first and can still intervene. * In **other hooks** (`before_agent`, `after_agent`, `after_turn`), a matched branch's text is sent as a message at that point in the lifecycle. * A `WHEN` that can't be evaluated does not hold — evaluation falls through to the next branch (or to an unconditional `RESPOND`, if present). * A branch can sit next to an unconditional `RESPOND:` in the same hook; the branches are the conditional alternative, evaluated independently. A hook's `WHEN` must be valid CEL. An invalid `WHEN` (including old plain-English text left over from before this feature) is demoted to an unconditional `RESPOND` for backward compatibility, and the compiler warns `HOOK_WHEN_BRANCH_INVALID_CEL`. Fix the condition, or remove `WHEN:` if the response really is meant to run unconditionally. ### Example: audit logging hook ```yaml theme={null} HOOKS: after_turn: CALL: log_wire_audit_event() ``` ### Example: turn timing ```yaml theme={null} HOOKS: before_turn: SET: turn_start_time = NOW() after_turn: SET: turn_duration_ms = SUB(NOW_MS(), turn_start_time_ms) ``` ## ON\_ERROR handlers The `ON_ERROR:` block defines how the agent responds to specific error types. Each handler matches an error type and specifies a response, optional retry logic, and a follow-up action. ### Syntax ```yaml expandable=true theme={null} ON_ERROR: tool_error: RESPOND: "That system is responding slowly. Let me retry." SUBTYPE: tool_timeout RETRY: 2 THEN: CONTINUE validation_error: RESPOND: "That value doesn't look right. Could you double-check?" THEN: CONTINUE llm_error: RESPOND: "I'm having trouble processing your request." RETRY: 1 RETRY_BACKOFF: exponential RETRY_MAX_DELAY: 10000 THEN: ESCALATE ``` ### Error types The error `type` (the handler's key) is matched against a **fixed, canonical set** — a handler whose key is not one of these is flagged at compile time (`ON_ERROR_HANDLER_TYPE_DRIFT`) because it can never match at runtime: | Error type | When it occurs | | - | - | | `default` | Fallback handler used when no other type matches. | | `llm_error` | The LLM call fails or errors. | | `tool_error` | A tool call fails. Narrow it with `SUBTYPE`/`SUBTYPES` — known subtypes include `rate_limit`, `auth_failure`, `network_error`, and `tool_timeout`. | | `routing_error` | Handoff/delegation/routing resolution fails. | | `validation_error` | Gathered/extracted data fails validation. | | `timeout_error` | A non-tool operation (for example the overall turn) times out. | | `unknown_error` | An unexpected error occurred. | Three names from older examples are **not** valid handler types and are silently rewritten as warnings, never matched at runtime: `tool_timeout` (use `tool_error` with `SUBTYPE: tool_timeout`), `invalid_input` (use `validation_error`), and `api_error` (use `tool_error` with an appropriate `SUBTYPE`, for example `network_error`). Use `SUBTYPE` (single value) or `SUBTYPES` (array) to match more finely within a type — both keys are case-insensitive but conventionally written uppercase. #### Inline shorthand For a handler that only takes a terminal action, use the single-line form `error_name: ACTION`: ```yaml theme={null} ON_ERROR: unknown_error: ESCALATE tool_error: HANDOFF Live_Agent ``` ### Error handler properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `RESPOND` | `string` | No | -- | Message sent to the user when this error occurs. | | `VOICE` | `object` | No | -- | Voice-specific overrides for the response. | | `RICH_CONTENT` | `object` | No | -- | Rich content format variants. | | `ACTIONS` | `object` | No | -- | Interactive actions. | | `SET` | `Record` | No | -- | Variable assignments applied when this error is handled. | | `RETRY` | `number` | No | -- | Number of retry attempts before executing `THEN`. | | `RETRY_DELAY` | `number` | No | -- | Delay in milliseconds between retries. | | `RETRY_BACKOFF` | `string` | No | -- | Backoff strategy for retries. See [Retry strategies](#retry-strategies). | | `RETRY_MAX_DELAY` | `number` | No | -- | Maximum delay between retries in milliseconds. | | `THEN` | `string` | No | -- | Action after retries are exhausted. See [Then actions](#then-actions). | | `HANDOFF_TARGET` | `string` | No | -- | Explicit target agent for `THEN: HANDOFF` (alternative to `HANDOFF `). | | `BACKTRACK_TO` | `string` | No | -- | Target step for `backtrack` action. | | `SUBTYPE` / `SUBTYPES` | `string` / `string[]` | No | -- | Error subtype(s) for fine-grained matching within `tool_error` (for example `SUBTYPE: tool_timeout` or `SUBTYPES: [rate_limit, network_error]`). | ### Retry strategies | Strategy | Behavior | | - | - | | `fixed` | Wait the same `RETRY_DELAY` between each attempt. | | `exponential` | Double the delay after each attempt, up to `RETRY_MAX_DELAY`. | | `linear` | Increase the delay by `RETRY_DELAY` after each attempt, up to `RETRY_MAX_DELAY`. | ### Then actions | Action | Behavior | | - | - | | `CONTINUE` | Continue the conversation, skipping the failed operation. | | `ESCALATE` | Trigger human escalation. | | `HANDOFF` | Transfer to a specified agent (for example. `HANDOFF Live_Agent`, or via `HANDOFF_TARGET`). | | `COMPLETE` | End the conversation. | | `RETRY_STEP` | Re-run the current flow step from the beginning. | | `BACKTRACK` | Return to a previous step specified by `BACKTRACK_TO`. | ### Step-level error handlers Error handlers can be overridden at the step level within a flow. Step-level handlers take precedence over agent-level handlers for the same error type. ```yaml theme={null} FLOW: steps: execute_payment: CALL: process_payment ON_ERROR: - TYPE: tool_error RESPOND: "Payment processing failed. Retrying..." RETRY: 3 RETRY_BACKOFF: exponential THEN: HANDOFF Payment_Support ``` #### Step-level error handler properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `TYPE` | `string` | Yes | -- | Error type to match. | | `SUBTYPE` | `string` | No | -- | Error subtype for fine-grained matching (step-level handlers accept only the singular form). | | `RESPOND` | `string` | No | -- | Message to the user. | | `RETRY` | `number` | No | -- | Retry count. | | `RETRY_DELAY` | `number` | No | -- | Delay between retries (ms). | | `RETRY_BACKOFF` | `string` | No | -- | Backoff strategy. | | `RETRY_MAX_DELAY` | `number` | No | -- | Maximum retry delay (ms). | | `THEN` | `string` | No | -- | Action after retries are exhausted. | | `HANDOFF_TARGET` | `string` | No | -- | Explicit target agent for `THEN: HANDOFF`. | | `BACKTRACK_TO` | `string` | No | -- | Target step for backtrack. | ## Error handler resolution order 1. **Step-level handlers** are checked first, matching on error type and optional subtypes. 2. **Agent-level handlers** (`ON_ERROR:` block) are checked if no step-level handler matches. 3. If no handler matches, the runtime uses the default error message from the `MESSAGES:` block (`error_default`). *** ## MESSAGES The top-level `MESSAGES:` block overrides the default system messages the runtime uses for errors, constraint violations, gathering, handoffs, escalation, completion, and voice/telephony prompts. Any key you don't set keeps its built-in default, and the templates support `{{}}` interpolation. ```yaml theme={null} MESSAGES: error_default: "Sorry, something went wrong. Please try again or type HELP." constraint_blocked: "I'm not able to help with that request." gather_prompt: "I need a few details: {{fields}}" escalation_format: "Connecting you to a specialist. Reason: {{reason}}" conversation_complete: "Thanks for chatting!" ``` ### Common message keys | Key | Default (abridged) | Used when… | | - | - | - | | `error_default` | "An error occurred. Please try again." | An error occurs and no `ON_ERROR` handler matches. | | `constraint_blocked` | "I cannot proceed with that request." | A `CONSTRAINT` blocks the turn. | | `gather_prompt` | "Please provide: " | Prompting for outstanding gather fields. | | `escalation_format` | "Escalating to human agent. Reason: " | Formatting an escalation notice. | | `conversation_complete` | "This conversation has been completed." | A conversation reaches `COMPLETE`. | | `invalid_handoff` | "Unable to transfer to the requested agent." | A `HANDOFF` target can't be resolved. | | `self_handoff` | "Cannot hand off to self." | An agent hands off to itself. | | `tool_fallback_desc` | "Execute the requested operation." | A tool has no `description`. | | `tool_confirmation_request` | "Please confirm before I go ahead. Shall I proceed?" | A tool needs the customer's approval. Supports `{{action_suffix}}` and `{{details}}`. See [Confirmation](/agent-platform/abl/reference/tools#what-the-customer-is-asked). | | `tool_confirmation_duplicate_effect` | "I already completed this earlier in our conversation. Would you like me to do it again?" | A tool that already ran is about to run again. Supports `{{action_suffix}}`. | `MESSAGES:` accepts **any** message key, not only the ones above — including error-subtype messages (`error_tool_timeout`, `error_llm_error`, `error_validation`, …), multi-intent UX prompts (`multi_intent_disambiguate_header`, `multi_intent_queued_notice`, …), handoff/routing messages (`handoff_message`, `handoff_message_voice`, `routing_message`), voice prompts (`voice_repeat`, `voice_nomatch`, `voice_noinput`, …), and the `greeting` / `out_of_scope` messages. Set only the keys you want to override. ### Filler overrides (voice) On a voice channel, the runtime speaks a short filler phrase (for example "One moment.") to avoid dead air while a tool call, reasoning step, handoff, delegation, extraction, or constraint check runs. `MESSAGES:` lets you override these with deterministic, author-controlled text using `filler_` keys: ```yaml theme={null} MESSAGES: filler_general: "One moment, please." filler_tool_call: "Let me look that up for you." filler_reasoning: "Let me think about that." filler_handoff: "Connecting you now." filler_delegation: "Checking with a specialist." filler_extraction: "Let me pull those details." filler_constraint_check: "Verifying that for you." filler_tool_call_fr: "Je vérifie cela pour vous." filler_tool_call_fr-CA: "Un instant, je regarde ça pour vous." ``` | Key | Spoken while… | | - | - | | `filler_general` | No more specific operation filler applies (the fallback). | | `filler_tool_call` | A tool call is in flight. | | `filler_reasoning` | A reasoning step is running. | | `filler_handoff` | A `HANDOFF` is in progress. | | `filler_delegation` | A `DELEGATE` is in progress. | | `filler_extraction` | The agent is extracting/gathering structured data. | | `filler_constraint_check` | A `CONSTRAINT` is being evaluated. | Add a `_` suffix to supply a localized variant — either a bare BCP-47 primary subtag (`filler_tool_call_fr`) or a full region tag (`filler_tool_call_fr-CA`). At runtime the first match wins, checking the region tag before the bare subtag, and the operation-specific key before the general fallback: `filler__` → `filler__` → `filler_` → `filler_general_` → `filler_general_` → `filler_general` → the platform's built-in localized fallback. A key with a region tag (`filler_tool_call_fr-CA`) contains a hyphen — write it unquoted as a plain `key: value` line; both the `.agent.abl` and `.agent.yaml` formats parse the hyphen correctly. Filler overrides are **static, deterministic text** — unlike other `MESSAGES:` keys, they do **not** support `{{}}` interpolation. User input is never substituted into a filler, by design. *** **Related articles:** * [Memory & Constraints](/agent-platform/abl-reference/memory-and-constraints) -- variables initialized in ON\_START and hooks * [Multi-Agent & Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor) -- ESCALATE and HANDOFF used in THEN actions * [Rich content](/agent-platform/abl-reference/rich-content) -- voice and rich content in handler responses # Memory and constraints Source: https://koreai.mintlify.app/agent-platform/abl/reference/memory-and-constraints This page documents two related ABL constructs: * [`MEMORY:`](#memory) manages session and persistent state. * [`CONSTRAINTS:`](#constraints) enforces business rules. *** ## MEMORY Memory in ABL defines how an agent stores, retrieves, and persists information across conversation turns, sessions, and users. The `MEMORY:` block declares four categories: session variables, persistent variables, remember triggers, and recall instructions. ### Overview Every agent has access to a memory configuration that controls data lifecycle: * **Session variables** exist for the duration of a single conversation session. * **Persistent variables** survive across sessions, scoped to a user or project. * **Remember triggers** automatically store values when conditions are met. * **Recall instructions** load stored facts at specific lifecycle events. ```yaml expandable=true theme={null} MEMORY: session: - customer_id - order_total TYPE: number DESCRIPTION: "Running total for the current order" INITIAL: 0 RESET: per_session persistent: - user.preferred_language - user.loyalty_tier SCOPE: user remember: - WHEN: user_language IS SET STORE: user_language -> user.language TTL: "90d" recall: - ON: session:start ACTION: inject_context PATHS: [user.preferred_language, user.loyalty_tier] ``` ### Session variables Session variables hold data that is relevant only during a single conversation. They are created when the session starts and discarded when the session ends (unless `RESET: never` is specified). #### Syntax ```yaml theme={null} MEMORY: session: - variable_name - variable_name TYPE: string DESCRIPTION: "Human-readable description" INITIAL: "default_value" RESET: per_session ``` You can declare a session variable with a bare name (minimal form) or with additional metadata. #### Properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `name` | `string` | Yes | -- | Variable name, used as the key in session context. | | `TYPE` | `string`, `number`, `boolean`, `date`, `array`, `object` | No | -- | Value type for runtime validation and field resolution. | | `DESCRIPTION` | `string` | No | -- | Human-readable description of the variable's purpose. | | `INITIAL` | any | No | `null` | Initial value assigned when the session starts. (`initial_value` is accepted as a legacy alias.) | | `RESET` | `per_session`, `per_activation`, `per_step`, `never` | No | `per_session` | When to reset the variable. See [Reset behavior](#reset-behavior). | | `READONLY` | `boolean` | No | `false` | When `true`, the model's `__set_context__` cannot write the variable; `SET`, `on_result`, and hooks still can. See [Write authority](#write-authority). | | `MODEL_WRITERS` | `string[]` | No | -- | Restricts which agents' models may write a shared variable. See [Model writers](#model-writers). | | `MODEL_VISIBLE` | `boolean` | No | `true` | When `false`, excludes the variable from automatic model context. See [Model-visible session and persistent values](#model-visible-session-and-persistent-values). | #### Reset behavior | Value | Behavior | | - | - | | `per_session` | Variable is initialized at session start and cleared when the session ends. This is the default. | | `per_activation` | Variable is reset each time the agent is (re)activated within a session (for example. on re-entry after a handoff). | | `per_step` | Variable is reset to its initial value at the start of each flow step. Useful for step-scoped accumulators. | | `never` | Variable persists for the lifetime of the runtime process. Use sparingly; prefer persistent memory instead. | #### Example: typed session variables ```yaml theme={null} MEMORY: session: - cart_items TYPE: array DESCRIPTION: "Items currently in the shopping cart" INITIAL: [] RESET: per_session - attempt_count TYPE: number INITIAL: 0 RESET: per_step ``` #### Write authority **Removed:** `SOURCE`, `FORMAT`, and `DERIVED` on a session variable. A `SOURCE:`/`FORMAT:`/`DERIVED:` line (or the YAML `source:`/`format:`/`derived:` key) is now a compile error — `E493`, `E492`, and `E498` respectively. `DERIVED` was removed because its every-write recompute cost grew unbounded; `SOURCE`/`FORMAT` were removed alongside it. **Migration:** declare the field `READONLY: true` and compute/write it with an authored `SET`, `on_result` mapping, or hook `SET` (see below). Where you relied on `FORMAT` for kind inference, pass the kind as `MATCH_SCORE`/`MATCHES`/`MATCH_RESULT`'s explicit third argument instead — for example `MATCH_SCORE(said, stored, "email")`. A session variable can declare `READONLY: true` so the model cannot write it, leaving an authored hook `SET` (or `on_result`, or a FLOW `SET`) to compute its value: ```yaml theme={null} AGENT: Identity_Check GOAL: "Confirm the caller before sharing account details" MEMORY: SESSION: - NAME: supplied_email TYPE: string - NAME: profile_email TYPE: string - NAME: email_verified TYPE: boolean READONLY: true HOOKS: before_turn: SET: email_verified = MATCHES(supplied_email, profile_email, "email") ``` | Property | Values | Meaning | | - | - | - | | `READONLY` | `true` | Opt-in. The model's `__set_context__` cannot write the variable; `SET`, `on_result` and hooks still can. Only an explicit `true` opts in. | In YAML the same key is lowercase: `readonly: true`. `MATCH_SCORE(said, stored, kind?)` returns 0-100 (or `-1` when there's nothing to judge — a missing value, a PII vault token, or an input over 1,024 characters; `-1` is below every threshold, so a rule written as `MATCH_SCORE(...) >= 70` fails closed). `MATCHES(said, stored, kind?)` is the pass/fail form on the platform's own match bands, and `MATCH_RESULT(said, stored, kind?)` returns `"match"`, `"retry"`, `"mismatch"`, or `"missing"`. These are a **said-versus-stored identity comparison**, not a regex test — the infix `a MATCHES "pattern"` operator is the regex form. `kind` is optional (default `text`); pass `"auto"` to detect the kind from the stored value's shape, or `"exact"` to turn off every voice allowance and require equality after normalization. See [Built-in function reference](/agent-platform/abl-reference/expressions-and-functions#built-in-function-reference) for the full signatures, `kind` values, and match bands. #### Model writers In a multi-agent project, several agents can share one session field — routing state, a verification result — and by default any agent's model can write it. `MODEL_WRITERS` names exactly which agents' models may: ```yaml theme={null} AGENT: Coordinator GOAL: "Collect and route the caller's request" MEMORY: SESSION: - NAME: route_choice TYPE: string MODEL_WRITERS: [Coordinator] - NAME: verified_result MODEL_WRITERS: [] - ordinary_state ``` | Declaration | Model-write behavior | | - | - | | Omitted (project-wide) | No change — existing inferred protections (authentication, consent, gate ownership, tool-outcome fields) still apply; this is not a blanket exemption for everything else. | | `MODEL_WRITERS: [Coordinator, Worker]` | Only the listed agents' models may write the field. | | `MODEL_WRITERS: []` | Every model is denied; authored writes (`SET`, `on_result`, hooks) still work. | The YAML key is `model_writers`. Names are case-sensitive agent names; every explicit declaration of the same shared field across the project must agree, and a conflicting pair fails closed (denied for everyone) rather than picking one. A full-project compile rejects a name that isn't a real agent in the project (`SESSION_MODEL_WRITER_UNKNOWN`); compiling a single agent in isolation defers that specific check. An invalid list (not an array of plain agent-name strings) is rejected at parse/compile time (`MEMORY_MODEL_WRITERS_INVALID`, `SESSION_MODEL_WRITERS_INVALID`) — the field is **not** left silently unprotected on invalid input. A listed writer can clear an *inferred* return/gate ownership refusal, but it can never override an explicit `READONLY: true`, a private-identity refusal, already-captured consent, or a protected tool-call outcome. **Authored proof, in brief.** Once a project declares any `MODEL_WRITERS`, a value arriving at a protected field through a `HANDOFF`/`DELEGATE` return `MAP`/`SET` must be traceable to something the platform itself produced — an executed tool result, or a value a hook/`on_result`/`ON_START`/`ESCALATE` action set directly. A value that is merely *model-written* (even copied through an ordinary `SET`), or sourced from raw caller input, secure-input collection, or a confirmation/inference step, does **not** count as proof — even if it's byte-identical to a previously proven value. A return assignment that can't establish this is skipped (the field keeps its prior value) and the runtime traces `RETURN_MODEL_WRITER_UNTRUSTED_SOURCE`. Prefer a static return `MAP`/`SET` path (a literal tool-result field) over a dynamic/computed expression when the source needs to flow into a writer-protected field — an expression the runtime can't statically resolve is treated conservatively and may be refused even when the underlying value is genuinely trustworthy. An agent that declares any `MODEL_WRITERS` (including an empty `[]`) compiles to IR version `1.2`; an agent with none keeps its existing `1.0`/`1.1` version, and a project's compilation envelope carries the highest version among its agents. A Runtime that doesn't support `1.2` rejects that artifact outright — upgrade the compiler and the Runtime together before authoring or publishing `MODEL_WRITERS`. #### Model-visible session and persistent values A session or persistent entry can opt out of **automatic model context** with `MODEL_VISIBLE: false` (YAML: `model_visible: false`). Omitted or `true` preserves the existing automatic projection. The value stays fully available to deterministic expressions, conditions, tools, mappings, persistence, and recall — this is a placement control over what the model sees automatically, not a write-authority or access-scope change: ```yaml theme={null} AGENT: Catalog_Assistant GOAL: "Help the customer select an item" MEMORY: SESSION: - NAME: catalog_mapping TYPE: object MODEL_VISIBLE: false - NAME: selected_category TYPE: string PERSISTENT: - PATH: user.internal_reference SCOPE: user ACCESS: read MODEL_VISIBLE: false ``` An opted-out entry is left out of automatically serialized model context — including supported voice memory updates and LLM extraction grounding context. An explicit authored reference such as `{{catalog_mapping}}` can still place the value at the intended prompt location, subject to the usual access and sensitive-value rules — `MODEL_VISIBLE: false` is not a guarantee the value can never reach a model, only that it isn't included automatically. Deterministic rule evaluation and ML extraction always see the real value regardless of this setting. An excluded ancestor path excludes its descendants, even one marked `true`. The excluded path must be a literal, dot-separated path — numeric array positions are supported, but a wildcard, a bracket selector, an empty segment, or a `__proto__`/`prototype`/`constructor` segment is rejected (`MEMORY_MODEL_VISIBLE_PATH_UNSUPPORTED`) rather than silently left visible. Each consuming agent's own memory declaration governs its automatic context. `MODEL_VISIBLE` requires matching authoring, parser/compiler, and Runtime versions deployed together — an older Runtime does not enforce it. ### Persistent variables Persistent variables survive across sessions. They are stored in a fact store and scoped to either a specific user or to the entire project. #### Syntax ```yaml expandable=true theme={null} MEMORY: persistent: - user.preferred_chains - user.wire_history_30d SCOPE: user TYPE: array DESCRIPTION: "Recent wire transfer history" ACCESS: readwrite DEFAULT_VALUE: [] - user.ssn SCOPE: user SENSITIVE: true SENSITIVE_DISPLAY: mask MASK_CONFIG: showFirst: 0 showLast: 4 char: "*" - project.exchange_rates SCOPE: project ACCESS: read ``` Persistent variable paths use dot notation. The first segment typically indicates the scope (`user.*` or `project.*`), but you can override this with the `SCOPE` property. #### Properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `path` | `string` | Yes | -- | Dot-notation path (for example. `user.preferred_chains`). | | `SCOPE` | `user`, `project`, `execution_tree` | No | `user` | Ownership scope. See [Scoping rules](#scoping-rules). | | `ACCESS` | `read`, `write`, `readwrite` | No | `readwrite` | Access direction. Constrains whether the agent can read, write, or both. | | `TYPE` | `string`, `number`, `boolean`, `date`, `array`, `object` | No | -- | Value type for runtime validation. | | `UNIT` | `string` | No | -- | Unit of the stored value (for example. `USD`, `km`). Informational metadata only. | | `DEFAULT_VALUE` | any | No | `null` | Default value when no fact exists in the store. | | `DESCRIPTION` | `string` | No | -- | Human-readable description. | | `SENSITIVE` | `boolean` | No | `false` | Marks the value as PII for privacy-aware handling. | | `SENSITIVE_DISPLAY` | `redact`, `mask`, `replace` | No | -- | How the value is displayed outside its context (when `SENSITIVE: true`). | | `MASK_CONFIG` | `{showFirst, showLast, char}` | No | -- | Masking configuration when `SENSITIVE_DISPLAY` is `mask`. | #### Scoping rules * **User scope** (`SCOPE: user`): Values are unique per authenticated user. Two users in the same project see different values for the same path. * **Project scope** (`SCOPE: project`): Values are shared across all users within the project. Useful for reference data, feature flags, or global configuration. * **Execution-tree scope** (`SCOPE: execution_tree`): Values are shared across one handoff tree / long-running execution — visible to the agents participating in that orchestration but not persisted per-user or per-project. As shorthand, a `READS:` sub-block declares persistent paths with `ACCESS: read`, and a `WRITES:` sub-block declares them with `ACCESS: write` — an alternative to listing each path under `persistent:` with an explicit `ACCESS`. ### Remember triggers Remember triggers define rules for automatically storing values into persistent memory when a condition is met during conversation. #### Syntax ```yaml theme={null} MEMORY: remember: - WHEN: user_name IS SET STORE: user_name -> user.name TTL: "90d" - WHEN: preferred_language IS SET STORE: preferred_language -> user.language ``` Array targets can accumulate values instead of being overwritten, using the block form of `STORE`: ```yaml theme={null} MEMORY: remember: - WHEN: search_completed STORE: value: last_query target: user.recent_searches operation: append # overwrite (default) | set | append unique: true # optional — skip duplicates maxSize: 10 # optional — FIFO cap ``` The modifiers are only read from the block form; the inline `value -> target` arrow form always uses `overwrite`. `append`, `unique`, and `maxSize` apply to `array`-typed targets in `user`, `project`, and `execution_tree` scopes. #### Properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `WHEN` | `string` | Yes | -- | Condition expression that triggers the store operation. Evaluated after each turn. | | `STORE` | `object` | Yes | -- | Value expression and target path. Inline syntax: `value_expr -> target_path`; block form to add modifiers. | | `operation` | `overwrite`, `append` | No | `overwrite` | Array-write mode. `append` adds to the existing array; `overwrite` replaces the whole value. Block form only. | | `unique` | `boolean` | No | `false` | When `append`, drop duplicate items (first occurrence kept; deep-equality for objects). Block form only. | | `maxSize` | `number` | No | -- | When `append`, cap array length, evicting oldest items first (FIFO). Block form only. | | `TTL` | `string` | No | -- | Time-to-live for the stored fact. After expiration, the value is deleted. Examples: `"30d"`, `"24h"`. | #### TTL format TTL values use a duration string: | Suffix | Meaning | | - | - | | `s` | Seconds | | `m` | Minutes | | `h` | Hours | | `d` | Days | Example: `"90d"` means the stored fact expires 90 days after it was written. ### Recall instructions Recall instructions define when and how to load persistent facts back into the session context. They execute at specific lifecycle events. #### Syntax ```yaml theme={null} MEMORY: recall: - ON: session:start ACTION: inject_context PATHS: [user.name, user.language, user.preferred_agent] - ON: tool:search_flights:after ACTION: load_memory DOMAIN: "travel_preferences" - ON: session:start ACTION: prompt_llm INSTRUCTION: "Greet the user by name if known" ``` #### Properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `ON` | `string` | Yes | -- | Lifecycle event that triggers the recall. See [Events](#recall-events). | | `ACTION` | `string` | No | -- | The concrete action to execute. See [Actions](#recall-actions). | | `INSTRUCTION` | `string` | Yes | -- | Human-readable instruction for the recall behavior. | | `PATHS` | `array` | No | -- | Persistent paths to inject (for `inject_context` action). | | `DOMAIN` | `string` | No | -- | Memory domain filter (for `load_memory` action). | #### Recall events `ON:` accepts these canonical lifecycle event patterns: | Event pattern | When it fires | | - | - | | `session:start` | When a new session initializes, before any user input. | | `session:end` | When a session ends. | | `agent::before` | Before a specific agent runs (use `agent:*:before` for any). | | `agent::after` | After a specific agent runs (use `agent:*:after` for any). | | `tool::after` | After a specific tool runs (use `tool:*:after` for any). | | `entity::extracted` | When a named entity/field is extracted from user input. | | `step:enter:` | On entering a specific flow step. | | `step:exit:` | On exiting a specific flow step. | Legacy event names such as `search:before`, `ON_START`, or `ON_SEARCH` are no longer supported and are rejected during compilation — use the canonical patterns above. Note that `tool::before` is not a valid recall event (only `tool::after`). #### Recall actions | Action | Behavior | | - | - | | `inject_context` | Loads specified persistent paths directly into the session context as variables. | | `load_memory` | Loads all facts in the specified domain into context. | | `prompt_llm` | Passes the instruction text to the LLM as additional context. This is the default action. | ### Accessing memory in expressions Session variables are accessed by name in expressions and template strings: ```yaml theme={null} RESPOND: "Hello, {{customer_name}}! Your balance is {{available_balance}}." ``` Persistent variables are accessed through their full dot-notation path: ```yaml theme={null} SET: greeting = COALESCE(user.name, "valued customer") ``` Variables set via `SET` assignments in flow steps, `ON_START`, or hooks are written to session memory and available for the remainder of the session. *** ## CONSTRAINTS Constraints are deterministic business rules that the runtime evaluates against the current session state. Authors can still group constraints under named phases for readability, but phase names are currently labels only: the compiler flattens constraints into a single runtime list unless a constraint explicitly gates itself with `WHEN` or a structural checkpoint construct. ### Overview Constraints enforce guardrails on agent behavior that are separate from LLM instructions. They are deterministic checks evaluated by the runtime, not suggestions to the model. A constraint that fails blocks the current operation and triggers an `ON_FAIL` action. ```yaml theme={null} CONSTRAINTS: always: - REQUIRE customer_verified == true ON_FAIL: "Please verify your identity first." pre_booking: - REQUIRE amount <= available_balance ON_FAIL: "Insufficient funds. Your available balance is {{available_balance}}." - REQUIRE sanctions_clear == true ON_FAIL: HANDOFF Compliance_Officer ``` ### Phases Named phases are retained as authoring labels for readability, review, and organization. They do not currently create separate runtime execution phases by themselves. #### Syntax ```yaml theme={null} CONSTRAINTS: phase_name: - REQUIRE condition_expression ON_FAIL: action_or_message ``` Use labels like `always`, `pre_booking`, or `eligibility_check` when they help readers understand intent, but use `WHEN` to gate applicability and `BEFORE` only for explicit structural checkpoint targets. ### Requirement rules Each requirement within a phase uses one of three keywords: `REQUIRE`, `LIMIT`, or `RESTRICT`. #### REQUIRE The most common form. Asserts that a condition must be true. If the condition evaluates to false, the `ON_FAIL` action is triggered. ```yaml theme={null} - REQUIRE account_status == "active" ON_FAIL: "Wire transfers require an active account." ``` #### LIMIT Expresses a numeric boundary. Semantically equivalent to `REQUIRE` with a comparison, but communicates intent more clearly. ```yaml theme={null} - LIMIT daily_wire_used + amount <= daily_wire_limit ON_FAIL: "This would exceed your daily wire limit of {{daily_wire_limit}}." ``` #### RESTRICT Expresses a prohibition. The condition describes what is forbidden; when it evaluates to true, the constraint fails. ```yaml theme={null} - RESTRICT beneficiary_country IN ["CU", "IR", "KP", "SY"] ON_FAIL: "Transfers to that destination are prohibited under sanctions regulations." ``` #### WHEN Use `WHEN:` to make a constraint apply only in a specific context without overloading the phase label. ```yaml theme={null} - REQUIRE ssn IS NOT SET WHEN: channel == "voice" ON_FAIL: "SSN cannot be collected on voice." ``` #### BEFORE Use `BEFORE` for structural checkpoints that the runtime knows how to activate. ```yaml theme={null} - REQUIRE measure_field IS SET BEFORE calling search_aggregate ON_FAIL: "Select a measure before running the aggregate search." - REQUIRE aggregation_validated == true BEFORE returning results ON_FAIL: "Validate the aggregation before responding." ``` Supported structural targets today: * `BEFORE calling ` * `BEFORE returning results` Non-structural `BEFORE` targets are retained for compatibility, but they are warning-only and have no runtime effect. Prefer `IMPLIES` or `WHEN` for that form. A `BEFORE calling ` rule's `condition` (`REQUIRE`/`LIMIT`/`RESTRICT`) or `WHEN` can read the about-to-be-sent arguments via `tool_args`, but only when the agent sets `EXECUTION: guardrail_builtins: true`. Without it, `tool_args` is unbound: the rule cannot read the arguments, so it never holds, and the tool call is refused. The compiler warns `GUARDRAIL_BUILTIN_NOT_BOUND` for this case. ```yaml theme={null} EXECUTION: guardrail_builtins: true CONSTRAINTS: always: - REQUIRE tool_args.amount <= daily_wire_limit BEFORE calling wire_transfer ON_FAIL: "That exceeds your daily wire limit." ``` #### IMPLIES `IMPLIES` is an inline condition operator (not a top-level keyword) for expressing conditional requirements. `A IMPLIES B` is lowered to `NOT (A) OR (B)` — that is, "if A holds, then B must also hold." Use it inside the condition of a `REQUIRE`/`LIMIT`/`RESTRICT` rule: ```yaml theme={null} - REQUIRE transfer_type == "international" IMPLIES swift_code IS SET ON_FAIL: "International transfers require a SWIFT/BIC code." ``` #### ON\_FAIL actions `ON_FAIL` accepts a single-line action — a message string, `HANDOFF `, or `ESCALATE` — or a structured block. The block form can respond, reset state, and route in one place: ```yaml theme={null} - REQUIRE amount <= daily_wire_limit ON_FAIL: RESPOND: "That exceeds your daily limit. Let's re-enter the amount." CLEAR: [amount, raw_amount] GOTO: collect_amount ``` | Key | Type | Description | | - | - | - | | `RESPOND` | `string` | Message sent to the user on failure. | | `CLEAR` | `string[]` | Session variables to reset on failure, so stale values are recollected. | | `COLLECT` | `string[]` | Fields to re-collect before retrying. | | `GOTO` | `string` | Step to route to. A single identifier naming a declared FLOW step; anything else is spoken literally. | | `RETRY` | `boolean` | Whether to retry the failed operation. | | `MAX_REPEATS` | `number` | Failures of this constraint (per conversation) this block answers before `ON_EXHAUSTED` takes over. Omit for no cap. | | `ON_EXHAUSTED` | string or action | Inline action run once `MAX_REPEATS` is used up, for example `ESCALATE "identity_not_confirmed"`. | The `CLEAR` key lets an `ON_FAIL` block reset dependent state directly — you no longer need a separate `CLEAR`-only step solely to clear variables before a retry. An `ON_FAIL` `RESPOND` message may use `{{_abl_constraint_fail_count}}` — this failure's 1-based ordinal for the constraint within the current conversation. Pair it with `MAX_REPEATS`/`ON_EXHAUSTED` to vary the message as attempts run out, then hand off once the cap is reached: ```yaml theme={null} - REQUIRE MATCH_SCORE(supplied_email, profile_email) >= 70 ON_FAIL: RESPOND: "That doesn't match what's on file (attempt {{_abl_constraint_fail_count}})." MAX_REPEATS: 2 ON_EXHAUSTED: ESCALATE "identity_not_confirmed" ``` ### Constraint properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `condition` | `string` | Yes | -- | Expression to evaluate. See [Expressions & functions](/agent-platform/abl-reference/expressions-and-functions). | | `WHEN` | `string` | No | -- | Optional applicability gate. The compiler lowers it to implication semantics so the constraint only applies when the gate is true. | | `ON_FAIL` | `string` or action | Yes | `respond` | Action when the condition fails. See [ON\_FAIL actions](#on_fail-actions). | | `severity` | `error` or `warning` | No | `error` | `error` blocks execution; `warning` emits a warning but continues. In text ABL, use the `WARN` keyword instead of a `severity:` line. | ### ON\_FAIL actions The `ON_FAIL` value determines what happens when a constraint fails. #### String message (respond) The most common form. Sends a message to the user and halts the current operation. ```yaml theme={null} ON_FAIL: "Insufficient funds. Available balance: {{available_balance}}." ``` #### HANDOFF Transfers control to another agent. Active flow and reasoning runtime checkpoints execute the handoff immediately when the constraint fails. ```yaml theme={null} ON_FAIL: HANDOFF Compliance_Officer ``` #### ESCALATE Triggers human escalation. ```yaml theme={null} ON_FAIL: ESCALATE ``` #### Block Silently blocks the operation without a user-facing message. ```yaml theme={null} ON_FAIL: BLOCK ``` #### Structured ON\_FAIL block For more control, use a structured block that combines multiple actions. ```yaml theme={null} - REQUIRE payment_method IS SET ON_FAIL: RESPOND: "I need your payment method before proceeding." COLLECT: [payment_method] THEN: retry ``` ##### Structured ON\_FAIL properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `RESPOND` | `string` | No | -- | Message to send to the user. | | `COLLECT` | `string[]` | No | -- | Fields to collect from the user before re-evaluating. | | `GOTO` | `string` | No | -- | Flow step to jump to. | | `RETRY` | `boolean` | No | `false` | Retry the current step after handling the constraint. | | `THEN` | `continue` or `retry` | No | -- | What to do after collection: `continue` proceeds, `retry` re-checks. | | `MAX_REPEATS` | `number` | No | -- | Failures of this constraint (per conversation) answered before `ON_EXHAUSTED` takes over. | | `ON_EXHAUSTED` | string or action | No | -- | Inline action run once `MAX_REPEATS` is used up. | #### Collect and retry example ```yaml theme={null} CONSTRAINTS: pre_booking: - REQUIRE departure_date IS SET AND return_date IS SET ON_FAIL: RESPOND: "I need your travel dates to check availability." COLLECT: [departure_date, return_date] THEN: retry - REQUIRE departure_date > NOW() ON_FAIL: RESPOND: "The departure date must be in the future. What date works for you?" COLLECT: [departure_date] THEN: retry ``` #### GOTO example ```yaml theme={null} - REQUIRE user.verified == true ON_FAIL: RESPOND: "You need to verify your identity first." GOTO: identity_verification_step ``` ### Severity levels | Severity | Behavior | | - | - | | `error` | Blocks the current operation. The `ON_FAIL` action executes. This is the default. | | `warning` | Emits a warning event and continues execution. The `ON_FAIL` message is logged, not sent. | ```yaml theme={null} - WARN fraud_score < 60 ON_FAIL: "Transaction flagged for review." ``` ### Constraint evaluation order 1. Constraint phase names such as `always` or `pre_booking` are labels only; the compiler flattens all constraints into one runtime list. 2. `WHEN` gates a constraint to a specific context, and structural `BEFORE` gates it to supported checkpoints such as tool calls or final responses. 3. Inline flow-step `CHECK` expressions are separate from `CONSTRAINTS` and evaluate directly against `step.check`. 4. Declaration order is preserved in the flattened list. 5. Evaluation stops at the first `error`-severity failure. Warnings do not halt evaluation. ### Template interpolation in messages `ON_FAIL` message strings support `{{variable}}` interpolation using the current session context: ```yaml theme={null} - REQUIRE (daily_wire_used + amount) <= daily_wire_limit ON_FAIL: "This wire would exceed your daily limit of {{daily_wire_limit}}. You've sent {{daily_wire_used}} today." ``` *** **Related articles:** * [Guardrails](/agent-platform/abl-reference/guardrails) -- input/output safety checks (distinct from business rule constraints) * [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions) -- condition expression syntax * [Multi-Agent and Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor) -- HANDOFF and ESCALATE actions referenced in ON\_FAIL * [Lifecycle and hooks](/agent-platform/abl-reference/lifecycle-and-hooks) -- `ON_START` handler where initial memory setup occurs * [Data types](/agent-platform/abl-reference/data-types-and-utilities) -- type definitions used in `TYPE` declarations # Multi-agent and supervisor Source: https://koreai.mintlify.app/agent-platform/abl/reference/multi-agent-and-supervisor This page documents multi-agent orchestration constructs (`DELEGATE`, `HANDOFF`, `ESCALATE`, `COMPLETE`) and the `SUPERVISOR:` declaration for top-level routing. *** ## SUPERVISOR declaration A Supervisor is a top-level orchestrator that routes user messages to the appropriate child agent based on intent, context, and declarative rules. Supervisor documents use the `SUPERVISOR:` keyword instead of `AGENT:` and define agent references, routing rules, state schemas, policies, and communication settings. ### Overview While agents handle domain-specific tasks, the Supervisor decides which agent should handle each user message. It does not execute tools or gather information directly; it classifies intent and routes accordingly. ```yaml theme={null} SUPERVISOR: Travel_Supervisor VERSION: "2.0" DESCRIPTION: "Routes customers to booking, support, or sales specialists" GOAL: "Route requests to the right specialist with full context preservation" PERSONA: | Professional travel booking assistant. Friendly, efficient, and helpful. Routes requests quickly and transparently. ``` ### Agent references The Supervisor declares which agents are available for routing. Each reference includes a file path, an alias, and a list of capabilities. #### Syntax ```yaml expandable=true theme={null} AGENTS: - REF: ./agents/flight_search.agent.abl ALIAS: Flight_Search CAPABILITIES: [flight_booking, fare_search, seat_selection] CHANNELS: [web, mobile, voice] REQUIRES_VALIDATION: false - REF: ./agents/support.agent.abl ALIAS: Support_Agent CAPABILITIES: [booking_management, cancellation, refund] REQUIRES_VALIDATION: true ``` #### Agent reference properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `REF` | `string` | Yes | -- | File path to the agent's ABL definition. | | `ALIAS` | `string` | Yes | -- | Local name used in routing rules and handoff targets. | | `CAPABILITIES` | `string[]` | Yes | -- | List of capability tags describing what the agent can do. | | `CHANNELS` | `string[]` | No | -- | Channels this agent supports (for example. `web`, `mobile`, `voice`). | | `REQUIRES_VALIDATION` | `boolean` | No | `false` | Whether the user must be authenticated before routing to this agent. | ### Routing rules Routing rules define conditional logic for directing messages to agents. Rules are evaluated in priority order. #### Syntax ```yaml theme={null} ROUTING: - NAME: escalation_route DESCRIPTION: "Route frustrated or explicitly requesting human" PRIORITY: 1 WHEN: intent.category == "escalation" OR user.frustration_detected == true THEN: ROUTE_TO Live_Agent_Transfer FLAGS: [set_active] - NAME: booking_route PRIORITY: 5 WHEN: intent.category == "new_booking" THEN: ROUTE_TO Sales_Agent ``` Alternatively, routing can be declared using the `HANDOFF:` block within a Supervisor, following the same syntax as [agent handoffs](#handoff): ```yaml theme={null} HANDOFF: - TO: Sales_Agent WHEN: intent.category == "new_booking" CONTEXT: pass: [search_context, user_preferences, budget] summary: "User looking to book new travel" EXPECT_RETURN: false ``` #### Routing rule properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `NAME` | `string` | Yes | -- | Unique name for the routing rule. | | `DESCRIPTION` | `string` | No | -- | Human-readable description. | | `PRIORITY` | `number` | Yes | -- | Evaluation order. Lower values are evaluated first. | | `WHEN` | `string` | Yes | -- | Condition expression that must be true for this rule to activate. | | `THEN` | `RoutingAction` | Yes | -- | Action to take. See [Routing actions](#routing-actions). | | `FLAGS` | `string[]` | No | -- | Behavioral flags. See [Routing flags](#routing-flags). | | `CONSTRAINTS` | `object` | No | -- | Additional constraints on when this rule applies. | #### Routing actions | Action | Syntax | Description | | - | - | - | | Route to agent | `ROUTE_TO Agent_Name` | Send the message to a specific agent. | | Route to user | `ROUTE_TO_USER "message"` | Send a message and wait for user input. | | Route by variable | `ROUTE_TO_VARIABLE var_name` | Route to the agent named in a variable. | | Intent-based routing | `INTENT_MATCH` | Route based on detected intent. See below. | | End conversation | `END_CONVERSATION` | End the session. | | System action | `SYSTEM_ACTION action_name` | Execute a system-level action (for example. handoff). | #### Intent-based routing For more granular routing, use `INTENT_MATCH` with intent-to-agent mappings: ```yaml expandable=true theme={null} ROUTING: - NAME: intent_router PRIORITY: 10 WHEN: true THEN: INTENT_MATCH: - INTENTS: [flight_search, hotel_search] ACTION: ROUTE_TO Sales_Agent - INTENTS: [manage_booking, cancel_booking] ACTION: ROUTE_TO Support_Agent FALLBACK: ROUTE_TO Fallback_Handler ``` #### Routing flags | Flag | Effect | | - | - | | `set_active` | Mark the target agent as the active agent for subsequent messages. | | `silent` | Route without sending a user-visible message. | | `no_log` | Do not log this routing decision in the trace store. | | `priority_boost` | Apply a priority boost in the target agent's queue. | #### Conditional routing (WHEN clauses) WHEN clauses use the same expression syntax as [Expressions & functions](/agent-platform/abl-reference/expressions-and-functions). Common patterns include: ```yaml expandable=true theme={null} # Intent-based WHEN: intent.category == "complaint" # State-based WHEN: user.is_authenticated == true AND intent.category == "manage_booking" # Negation WHEN: NOT intent.has_specific_request # Compound WHEN: intent.unclear == true OR intent.confidence < 0.5 # Variable check WHEN: handoff_count >= 4 ``` #### Routing constraint blocks Add constraints to limit when a rule applies beyond the WHEN condition: ```yaml expandable=true theme={null} ROUTING: - NAME: voice_only_route PRIORITY: 3 WHEN: intent.category == "voice_action" THEN: ROUTE_TO Voice_Agent CONSTRAINTS: channels: [voice] requiresState: [user.is_authenticated] ignoreIntents: [greeting, farewell] ``` | Constraint | Type | Description | | - | - | - | | `ignoreIntents` | `string[]` | Intents that should not trigger this rule. | | `channels` | `string[]` | Channels where this rule applies (for example. `[web, voice]`). | | `requiresState` | `string[]` | State variables that must be set for this rule to activate. | ### State schema The Supervisor can declare a state schema that defines typed variables organized by namespace. ```yaml expandable=true theme={null} STATE: user: is_authenticated: type: boolean required: false default: false description: "Whether the user has been authenticated" language: type: string required: false source: user system: routing_failures: type: number required: false default: 0 source: system ``` State variables follow the same [VariableDefinition](/agent-platform/abl-reference/data-types-and-utilities) structure as agent variables, with the addition of namespace grouping. #### State variable properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | See [Data types](/agent-platform/abl-reference/data-types-and-utilities) | Yes | -- | Variable type. | | `required` | `boolean` | No | `false` | Whether the variable must have a value. | | `default` | any | No | -- | Default value. | | `description` | `string` | No | -- | Human-readable description. | | `source` | `system`, `user` , `agent`, `computed` | No | -- | Origin of the variable value. | | `updatedBy` | `string[]` | No | -- | List of agent aliases that can update this variable. | ### Policies Policies define high-level behavioral rules for the Supervisor, constraining what it is allowed and forbidden to do. ```yaml expandable=true theme={null} POLICIES: - NAME: no_direct_booking DESCRIPTION: "Supervisor must not make bookings directly" RULES: forbiddenWhen: intent.category == "booking" AND active_agent IS NOT SET behavior: "Route to Sales_Agent instead of handling directly" - NAME: auth_required_for_account DESCRIPTION: "Account operations require authentication" RULES: allowedWhen: user.is_authenticated == true triggerSignal: "auth_required" ``` #### Policy properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `NAME` | `string` | Yes | -- | Unique policy name. | | `DESCRIPTION` | `string` | No | -- | Human-readable description. | | `RULES` | `object` | Yes | -- | Policy rule definitions. See below. | #### Policy rule properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `allowedWhen` | `string` | No | -- | Condition under which the action is allowed. | | `forbiddenWhen` | `string` | No | -- | Condition under which the action is forbidden. | | `triggerSignal` | `string` | No | -- | Signal to emit when the policy is triggered. | | `behavior` | `string` | No | -- | Description of the expected behavior when triggered. | ### Communication settings Communication settings define the Supervisor's language, tone, and vocabulary preferences. ```yaml expandable=true theme={null} COMMUNICATION: language: en formality: neutral pronouns: use: "we" avoid: "I" vocabulary: prefer: [assist, help, guide] avoid: [unfortunately, regrettably] constraints: - "Never reveal internal agent names to the user" - "Always explain why a transfer is happening" ``` #### Communication properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `language` | `string` | Yes | -- | Primary language code (for example. `en`, `es`). | | `formality` | `formal`, `informal`, `neutral` | Yes | -- | Communication tone. | | `pronouns` | `object` | No | -- | Pronoun preferences (`use` and `avoid`). | | `vocabulary` | `object` | No | -- | Preferred and avoided vocabulary. | | `constraints` | `string[]` | Yes | -- | Communication behavioral constraints. | ### Behavior settings The `BEHAVIOR` block declares the Supervisor's execution **role**, which governs how it participates in coordination. ```yaml theme={null} BEHAVIOR: role: orchestrator # router | orchestrator | assistant ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `role` | `string` | No\* | -- | Execution role: `router`, `orchestrator`, or `assistant`. See [Execution roles](#execution-roles). | | `canRespondDirectly` | `boolean` | No | -- | **Deprecated** — migrate to `role`. Retained as a compatibility fallback (see below). | | `allowedDirectActions` | `string[]` | No | `[]` | **Deprecated** compatibility field; does not authorize runtime operations. | | `forbiddenActions` | `string[]` | No | `[]` | **Deprecated** compatibility field; does not authorize runtime operations. | \*`role` is optional, but omitting it on an agent that has a `BEHAVIOR` block produces a deprecation warning and falls back to legacy inference. Declaring `role` is the recommended, forward-compatible form. #### Execution roles | Role | Responds directly? | Can `DELEGATE`? | Meaning | | - | - | - | - | | `router` | No | No | Routes each message to a child agent via `HANDOFF`. Requires at least one `HANDOFF` target. | | `orchestrator` | Yes | Yes | Coordinates child agents and may also synthesize/respond directly. Required to use `DELEGATE`. | | `assistant` | Yes (leaf) | Yes | A leaf agent that handles conversations directly. **Not valid on a `SUPERVISOR` document.** | **Breaking changes in this release - Artemis 1.4.0:** * `canRespondDirectly` is deprecated in favor of `role`. If both are present and **conflict** (`canRespondDirectly: true` ↔ `orchestrator`, `false` ↔ `router`), compilation fails (`AGENT_EXECUTION_ROLE_CONFLICT`). Remove the deprecated field or pick the matching role. * A `SUPERVISOR` must be `router` or `orchestrator` — `role: assistant` fails compilation. * `role: router` requires at least one `HANDOFF` ownership target. * Only an `orchestrator` may declare `DELEGATE:` — a `router` (or `assistant`) that uses `DELEGATE` fails compilation (`AGENT_RETURNED_WORK_REQUIRES_ORCHESTRATOR`). Routers hand off; orchestrators own returned work. ### Intents In addition to `INTENT_MATCH` inside a routing rule, a Supervisor may declare a top-level `INTENTS:` block listing intent labels (and optional lexical-fallback behavior) that routing and classification draw on. Supervisors can also hand off to **other supervisors**, enabling hierarchical composition, and may declare an `ON_ERROR` block to handle routing failures. ### Execution pipeline (pre-classification) This is an advanced, opt-in optimization. Most supervisors do not need it — routing works without an `EXECUTION` block. A Supervisor can enable a pre-classification pipeline that runs a smaller, faster model before the main reasoning LLM. The classifier detects user intent and can short-circuit routing for obvious cases, avoiding the cost of a full reasoning call. ```yaml expandable=true theme={null} SUPERVISOR: Support_Router EXECUTION: model: claude-sonnet-4-5-20250929 # main reasoning model pipeline: enabled: true mode: sequential # 'parallel' | 'sequential' shortCircuit: enabled: true confidenceThreshold: 0.85 toolFilter: enabled: true maxTools: 6 keywordVeto: enabled: true keywords: [reset, cancel, undo] intentBridge: enabled: true programmaticThreshold: 0.85 guidedThreshold: 0.5 outOfScopeDecline: true multiIntentSignal: true HANDOFF: - TO: Billing_Agent WHEN: intent.category == "billing" - TO: General_Inquiry WHEN: true ``` The classifier **model** is not selected in the agent's `EXECUTION.pipeline` block — it is resolved from **project-level runtime configuration** via `modelSource` (`default` uses the platform's tool-selection model; `tenant` uses a specific tenant model identified by `tenantModelId`). A `model:` key inside the `pipeline` block is deprecated and ignored by the runtime. The top-level `EXECUTION.model` still sets the **main reasoning** model as usual. #### Pipeline options | Option | Type | Default | Description | | - | - | - | - | | `enabled` | Boolean | `false` | Enable the pre-classification pipeline. | | `mode` | String | `parallel` | `parallel` — classifier and main LLM run simultaneously; `sequential` — classifier runs first, main LLM only if no short-circuit. | | `shortCircuit.enabled` | Boolean | `true` | Allow direct routing when classifier confidence is high. | | `shortCircuit.confidenceThreshold` | Number | `0.85` | Minimum confidence to skip the reasoning loop. | | `toolFilter.enabled` | Boolean | `true` | Filter tools to only relevant ones before reasoning. | | `toolFilter.maxTools` | Number | `6` | Maximum tools passed to the reasoning loop. | | `keywordVeto.enabled` | Boolean | `true` | Prevent short-circuit when the user mentions local tool keywords. | | `keywordVeto.keywords` | String | `[]` | Additional keywords that veto short-circuit routing. | | `intentBridge.enabled` | Boolean | `true` | Bridge classifier intent into routing decisions. | | `intentBridge.programmaticThreshold` | Number | `0.85` | Confidence at/above which intent routing is applied programmatically. | | `intentBridge.guidedThreshold` | Number | `0.5` | Confidence at/above which intent is offered to the model as guidance. | | `intentBridge.outOfScopeDecline` | Boolean | `true` | Decline out-of-scope requests detected by the classifier. | | `intentBridge.multiIntentSignal` | Boolean | `true` | Signal when the message contains multiple distinct intents. | For pure routing supervisors, `sequential` mode with `shortCircuit.enabled: true` gives the best cost savings. In `parallel` mode the classifier adds latency protection but no cost savings, since both calls run regardless. *** ## HANDOFF HANDOFF transfers conversational control from the current agent to another agent, passing context and optionally expecting a return. When a handoff enters a target agent whose entry point is a REASONING zone (or a scripted flow step), the target begins executing **immediately on entry** — it does not wait for a new user message. This lets a receiving agent act on the forwarded context (see `history`, below) as soon as control transfers. ### Syntax ```yaml expandable=true theme={null} HANDOFF: - TO: Compliance_Officer WHEN: sanctions_clear == false PRIORITY: 0 CONTEXT: pass: [customer_id, beneficiary_name, amount, sanctions_match_score] summary: "Wire flagged during sanctions screening (score: {{sanctions_match_score}})." history: full memory_grants: - path: user.compliance_notes access: readwrite EXPECT_RETURN: true ON_FAILURE: ESCALATE ON_RETURN: action: continue map: compliance_decision: sanctions_clear review_notes: compliance_review_notes ``` ### Properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `TO` | `string` | Yes | -- | Target agent name. | | `WHEN` | `string` | Yes | -- | Condition that triggers the handoff. | | `ENABLED_WHEN` | `string` | No | -- | Condition deciding whether this route exists for the session at all, evaluated once before any turn. See [Availability (ENABLED\_WHEN)](#availability-enabled_when). | | `PRIORITY` | `number` | No | -- | Evaluation priority. Lower values are evaluated first when multiple handoff rules match. | | `CONTEXT` | `object` | Yes | -- | Context to pass to the target agent. See [Context](#handoff-context). | | `EXPECT_RETURN` | `boolean` | Yes | `false` | Whether control should return to this agent after the target completes. `RETURN` is accepted as a backward-compatible alias. | | `ON_FAILURE` | `string` / `object` | No | -- | Parent-side fallback for setup or dispatch failures **before** the target accepts the handoff. See [Handoff failure strategies](#handoff-failure-strategies). | | `ON_RETURN` | `string` / `object` | No | -- | How to handle the target returning control. See [Return expectations](#return-expectations). | | `EXPERIENCE_MODE` | `string` | No | -- | End-user experience when the transfer happens. See [Experience modes](#experience-modes). | | `REMOTE` | `object` | No | -- | Remote agent configuration. Can also be expressed as top-level `LOCATION`/`ENDPOINT`/`PROTOCOL` keys. See [Remote agent support](#remote-agent-support). | | `ASYNC` | `boolean` | No | `false` | Use async dispatch with push notifications for remote agents. | | `ASYNC_TIMEOUT` | `number` | No | -- | Timeout in seconds for async handoff. (Also accepted as `TIMEOUT`.) | The primary keyword is **`EXPECT_RETURN`**. The older `RETURN` keyword is still parsed for backward compatibility, but new agents should use `EXPECT_RETURN`. The legacy top-level `MAP` block is superseded by `ON_RETURN.map` (see [Return expectations](#return-expectations)). ### Handoff context The `CONTEXT` block defines what information the target agent receives. | Property | Type | Required | Default | Description | | - | - | - | - | - | | `pass` | `string[]` | Yes | -- | Session variable names to include in the handoff context. | | `summary` | `string` | Yes | -- | Human-readable summary of why the handoff is occurring. Supports `{{}}` interpolation. | | `history` | `string` / `object` | No | `full` | Conversation history strategy. See [History strategies](#history-strategies). | | `set` | `Record` | No | -- | Route-scoped variable assignments applied **before** the target agent is invoked (inject computed values without a separate step). | | `memory_grants` | `object[]` | No | -- | Persistent memory paths to grant the target agent access to. See [Memory grants](#memory-grants). | The legacy `grant_memory: [path, ...]` form is **no longer supported** and produces a parse error. Use `memory_grants` with explicit `path`/`access` entries instead (see [Memory grants](#memory-grants)). `CONTEXT` may also be expressed as a shorthand where `pass`, `summary`, and `history` appear as direct siblings of the handoff entry rather than nested under `CONTEXT:`. ### History strategies The `history` property controls how much conversation history the target agent receives. | Value | Behavior | | - | - | | `full` | **Platform default** — applied when `history` is omitted. The complete parent conversation history is passed. | | `auto` | Uses the handoff summary when available; otherwise falls back to a bounded recent transcript (the last 5 messages). | | `none` | No conversation history is passed. The target agent starts fresh. | | `summary_only` | Only the `summary` text is passed, no raw messages. | | `{ mode: last_n, count: }` | The last N messages are passed. | ```yaml theme={null} CONTEXT: pass: [customer_id, amount] summary: "Customer needs fraud review." history: mode: last_n count: 10 ``` The legacy shorthand `history: last_10` is still accepted during the compatibility window, but new agents should use the typed `mode` + `count` block. ### Return expectations When `EXPECT_RETURN: true`, the calling agent pauses and waits for the target agent to complete. When the target explicitly returns control (via its built-in return capability), the parent resumes. When `EXPECT_RETURN: false`, the handoff is a one-way transfer and the calling agent's turn ends. `ON_RETURN` controls what happens when the target returns. It accepts either a **string** (the name of a named return handler, or a built-in action) or a **structured block**: ```yaml expandable=true theme={null} HANDOFF: - TO: Authentication_Agent WHEN: user.is_authenticated == false CONTEXT: pass: [session_context] summary: "User needs authentication" EXPECT_RETURN: true ON_RETURN: action: continue # continue | resume_intent handler: route_to_booking # optional: named RETURN_HANDLERS entry resume_with: latest_session_user_utterance map: # child result field -> parent variable user_id: auth_result.user_id auth_token: auth_result.token set: # parent assignments after mapping auth_summary: "Authenticated {{auth_result.user_id}}" ``` #### `ON_RETURN` properties | Property | Type | Description | | - | - | - | | `action` | `string` | Built-in resume behavior: `continue` (resume the parent's current flow) or `resume_intent` (re-run routing against the original user intent with the returned context). | | `handler` | `string` | Name of a [named return handler](#return-handlers) declared in the top-level `RETURN_HANDLERS:` block. | | `resume_with` | `string` | Which utterance to replay when resuming. One of `original_handoff_utterance`, `latest_child_user_utterance`, `latest_session_user_utterance`, `forwarded_return_message`. | | `map` | `Record` | Copies child return fields into parent variables (child key → parent variable). | | `set` | `Record` | Parent assignments applied after `map`, using the same templated assignment shape as flow `ON_RESULT` branches. | ### Return handlers A top-level `RETURN_HANDLERS:` block declares reusable named handlers that run on the parent after a `EXPECT_RETURN: true` child returns. Reference one from `ON_RETURN.handler`. ```yaml theme={null} RETURN_HANDLERS: route_to_booking: RESPOND: "You're verified — let's finish your booking." CLEAR: [pending_auth_reason] CONTINUE: true # leave the parent waiting for the next user turn # RESUME_INTENT: true # or replay the parent's last user message ``` | Property | Type | Description | | - | - | - | | `RESPOND` | `string` | Message to send when the handler runs. | | `CLEAR` | `string[]` | Session variables to reset after the child returns. | | `CONTINUE` | `boolean` | Leave the parent waiting for the next user turn. | | `RESUME_INTENT` | `boolean` | Replay the parent's last user message through routing. | ### Handoff failure strategies `ON_FAILURE` defines a parent-side fallback for failures that occur **before** the target accepts the handoff — for example target lookup, pre-transfer validation, or dispatch failures. It does not replace the timeout path after an accepted returnable handoff, and it does not fire when a downstream child later reports its own failure. ```yaml theme={null} HANDOFF: - TO: Specialist_Agent WHEN: needs_specialist == true CONTEXT: pass: [user_id, query] summary: "User needs specialist help" ON_FAILURE: ESCALATE # CONTINUE | ESCALATE | RESPOND "message" ``` | Value | Behavior | | - | - | | `CONTINUE` | Silently continue in the current agent. | | `ESCALATE` | Trigger human escalation. | | `RESPOND "message"` | Send a message and continue in the current agent. | ### Experience modes `EXPERIENCE_MODE` shapes how the transfer is presented to the end user. It is valid on both `HANDOFF` and `DELEGATE`. | Value | Behavior | | - | - | | `shared_voice_handoff` | Voice channels — hand off while keeping a single shared voice experience. | | `visible_handoff` | The user is told the conversation is being transferred. | | `silent_delegate` | The sub-agent runs without a user-visible transfer. | | `human_escalation` | Present the transfer as an escalation to a human. | ### Async dispatch For remote agents, set `ASYNC: true` to dispatch the handoff asynchronously. The calling agent receives a notification when the remote agent completes rather than blocking. ```yaml expandable=true theme={null} HANDOFF: - TO: External_Review WHEN: requires_external_review == true CONTEXT: pass: [case_id, documents] summary: "Documents require external review." EXPECT_RETURN: true REMOTE: location: remote endpoint: "https://review.partner.com/api/v1/submit" protocol: a2a ASYNC: true ASYNC_TIMEOUT: 3600 ``` ### Memory grants Use `memory_grants` to give the target agent scoped access to specific persistent memory paths. Without this, the target agent cannot read or write the parent's persistent variables. Each grant declares a `path` and an `access` level. ```yaml expandable=true theme={null} CONTEXT: pass: [user_id, booking_reference] summary: "User needs to authenticate to manage booking." memory_grants: - path: user.last_verified_at access: read - path: workflow.auth_token access: readwrite ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `path` | `string` | Yes | -- | Persistent memory path being granted. | | `access` | `string` | No | `read` | Access level: `read` or `readwrite`. | *** ## DELEGATE DELEGATE invokes a sub-agent synchronously, waits for it to complete, and maps the result back into the calling agent's context. The sub-agent runs in its own scope and does not have direct access to the parent's session variables. **Breaking changes in this release - Artemis 1.4.0:** * `DELEGATE` may only be declared by an [`orchestrator`](#execution-roles). A `router` or `assistant` that uses `DELEGATE` fails compilation. * `DELEGATE` is a one-shot internal call — it cannot wait for multi-turn user input. If the target agent has **required** `GATHER` fields, you must supply them in `INPUT:` (or make them optional/defaulted). An unmapped required field fails compilation (`DELEGATE_CHILD_GATHER_INPUT_UNMAPPED`); model a user-interactive child as an `EXPECT_RETURN: true` `HANDOFF` instead. ### Syntax ```yaml expandable=true theme={null} DELEGATE: - AGENT: Sanctions_Screening WHEN: beneficiary_name IS SET AND beneficiary_country IS SET PURPOSE: "Screen beneficiary against OFAC SDN and EU sanctions lists" INPUT: name: beneficiary_name account: beneficiary_account country: beneficiary_country RETURNS: cleared: sanctions_clear match_score: sanctions_match_score USE_RESULT: "Block if match_score > 85. Proceed only if cleared." TIMEOUT: "15s" ON_FAIL: escalate ``` ### Properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `AGENT` | `string` | Yes | -- | Name of the sub-agent to invoke. `TO` is accepted as an alias. | | `WHEN` | `string` | Yes | -- | Condition that must be true for delegation to occur. | | `ENABLED_WHEN` | `string` | No | -- | Condition deciding whether this route exists for the session at all, evaluated once before any turn. See [Availability (ENABLED\_WHEN)](#availability-enabled_when). | | `PURPOSE` | `string` | Yes | -- | Description of why this delegation exists. Included in trace events. `SUMMARY` is accepted as an alias. | | `INPUT` | `Record` | Yes | -- | Input mapping. Keys are sub-agent parameter names; values are expressions from the parent's context. `PASS` is accepted as an alias. | | `RETURNS` | `Record` | Yes | -- | Output mapping. Keys are sub-agent result fields; values are parent context variable names to write to. | | `USE_RESULT` | `string` | Yes | -- | Instructions for interpreting the sub-agent's result. | | `MEMORY_GRANTS` | `object[]` | No | -- | Persistent memory paths to grant the sub-agent, each with `path` and `access` (`read` / `readwrite`). Same shape as [handoff memory grants](#memory-grants). | | `TIMEOUT` | `string` | No | -- | Maximum time to wait for the sub-agent. Format: `"Ns"` (seconds). | | `ON_FAIL` | `string`, `object` | No | -- | Action when the sub-agent fails or times out. See [Failure strategies](#delegate-failure-strategies). | | `EXPERIENCE_MODE` | `string` | No | -- | End-user experience for the delegation. See [Experience modes](#experience-modes). | | `REMOTE` | `object` | No | -- | Remote agent configuration. Can also be given as top-level `LOCATION`/`ENDPOINT`/`PROTOCOL` keys. See [Remote agent support](#remote-agent-support). | | `FAILURE_MESSAGE` | `string` | No | -- | Message to display when delegation fails. | ### Input/output mapping The `INPUT` block maps values from the parent agent's session context into the sub-agent's input parameters: ```yaml theme={null} INPUT: name: beneficiary_name # parent's beneficiary_name -> sub-agent's "name" parameter account: beneficiary_account amount: amount ``` The `RETURNS` block maps the sub-agent's result fields back into the parent's session variables: ```yaml theme={null} RETURNS: cleared: sanctions_clear # sub-agent's "cleared" field -> parent's sanctions_clear match_score: sanctions_match_score ``` ### Delegate failure strategies | Value | Behavior | | - | - | | `respond` | Send a message and continue. Requires `FAILURE_MESSAGE`. | | `continue` | Silently continue without the sub-agent's result. | | `escalate` | Trigger human escalation. | | `retry` | Retry the delegation. Accepts a `count` for max retries. | Structured failure example: ```yaml theme={null} ON_FAIL: type: retry count: 2 ``` ### Remote agent support DELEGATE supports invoking agents running on remote services. Add a `REMOTE` block to configure the connection. ```yaml expandable=true theme={null} DELEGATE: - AGENT: External_Compliance WHEN: amount > 10000 PURPOSE: "External compliance check for high-value transfers" INPUT: transaction_id: wire_reference amount: amount RETURNS: approved: compliance_approved USE_RESULT: "Proceed only if approved." REMOTE: location: remote endpoint: "https://compliance.partner.com/api/v1/check" protocol: a2a auth: type: bearer timeout: "30s" ``` #### Remote properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `location` | `local`, `remote` | No | `local` | Whether the agent is co-located or remote. | | `endpoint` | `string` | No | -- | URL for remote agent invocation. | | `protocol` | `a2a` , `rest` | No | -- | Communication protocol. `a2a` for Agent-to-Agent, `rest` for REST API. | | `auth` | `object` | No | -- | Authentication configuration. `type` is `bearer` or `api_key`; an optional `header` names the header to carry the credential. Credential values are resolved from runtime config / auth-profile resolution — never inlined in ABL. | | `timeout` | `string` | No | -- | Override timeout for the remote call. | `LOCATION: remote` is explicit and recommended, but the compiler also treats an `ENDPOINT` without `LOCATION` as remote. The remote block can be authored either nested under `REMOTE:` or as top-level `LOCATION`/`ENDPOINT`/`PROTOCOL` keys on the entry. ### Availability (`ENABLED_WHEN`) `ENABLED_WHEN` turns a `HANDOFF` or `DELEGATE` route on or off for the whole session, decided once before any turn is understood. `WHEN` still decides which available route a given turn takes — `ENABLED_WHEN` decides which routes exist at all: ```yaml theme={null} INTENTS: vehicles: "The customer is looking for automotive options." products: "The customer is looking for product options." HANDOFF: - TO: Automotive_Search WHEN: intent.category == "vehicles" ENABLED_WHEN: _metadata.country != "SGP" ON_FAILURE: RESPOND "Vehicle search is not offered in your region." - TO: Product_Search WHEN: intent.category == "products" ``` **Writing the condition.** Put `ENABLED_WHEN:` on the entry, after `WHEN:` (YAML key: `enabled_when`). Read only session data known before the turn: `_metadata.*`, `env.*`, `caller.*`, `session.*`, `user.*`, and memory fields. Don't read what the current turn means (`intent`, `routing.*`, `input`) or write natural language — both are compile errors (`ENABLED_WHEN_SEMANTIC_REFERENCE`). A long condition can continue on deeper-indented lines, after an inline value, an empty value, or a block indicator (`|`, `>`, `>-`); a line that looks like a key or starts a new entry ends it, and `#` lines are comments. The compiler warns (`ROUTE_WHEN_MULTILINE_JOINED`) when it joins lines written after an inline or empty value; a condition opened with a block indicator is joined without the warning. **How availability is decided.** Each target is judged by its own entries: a `HANDOFF` to X by the `HANDOFF` entries for X, a `DELEGATE` to X by the `DELEGATE` entries for X. The target is available when *any* of its entries is enabled — an entry without `ENABLED_WHEN` is always enabled, so one unconditional entry keeps the target permanently available (the compiler warns, `ENABLED_WHEN_SHADOWED_BY_UNCONDITIONAL_ENTRY`). The decision **fails closed**: a condition that can't be evaluated disables the entry, and so does one that reads a value the session doesn't have (unset, `null`, or an empty string) — even inside `OR` — *unless* the condition explicitly allows that value to be missing. A value compared with `== null`, `!= true`, or `IS NOT SET` anywhere in the condition counts as known, and an unset value then reads as `null`: ```yaml theme={null} ENABLED_WHEN: _metadata.restricted != true # enabled when the caller sends nothing ENABLED_WHEN: _metadata.country != "SGP" # disabled when country is missing ENABLED_WHEN: _metadata.region IS NOT SET OR _metadata.region != "EU" # guard explicitly ``` `_metadata` changes made during the session apply to every agent in the conversation, including agents waiting for a child to return. **When a target isn't available.** The route is withheld from the model entirely — left out of prompt route lists in both text and voice. A model that calls it anyway is refused and told which routes are available. A route the *author* wrote (a FLOW step `HANDOFF`, `ON_ACTION`, a digression, a tool hook, `ON_ERROR`, an `ON_START` handoff) is refused and runs its own failure policy: the entry's `ON_FAILURE` for a handoff, the delegate's `ON_FAILURE` for a delegate. `RESPOND` shows its message, `ESCALATE` escalates the same turn, `CONTINUE` skips the route and runs the rest of the step. A handoff with no `ON_FAILURE` tells the user the option isn't available and the agent stays on its current step; a delegate with no `ON_FAILURE` is simply skipped — a refused route never leaves a turn without an answer. A constraint's `ON_FAIL`/`ON_EXHAUSTED` handoff is a safety route that must always be reachable, so its target can't be fully conditional: the compiler rejects one whose every `HANDOFF` entry has `ENABLED_WHEN` (`ENABLED_WHEN_SAFETY_ROUTE_CONDITIONAL`). When every route of an agent is unavailable, the agent answers the user directly. **Diagnostics** | Code | Severity | Meaning | | - | - | - | | `ENABLED_WHEN_EMPTY` | error | `ENABLED_WHEN` has no condition. | | `ENABLED_WHEN_LINE_COMMENT` | error | A YAML `enabled_when` read as one line carries a `//` comment (use `#`, or a literal block). | | `ROUTE_WHEN_MULTILINE_JOINED` | warning | A condition without a block indicator was read from several lines. | | `ENABLED_WHEN_SEMANTIC_REFERENCE` | error | The condition reads turn meaning, or is natural language. | | `INVALID_CEL_SYNTAX` | error | The condition isn't a valid expression. | | `ENABLED_WHEN_ALL_DISABLED_RISK` | warning | Every route of a routing agent is conditional. | | `ENABLED_WHEN_SHADOWED_BY_UNCONDITIONAL_ENTRY` | warning | A target has entries both with and without `ENABLED_WHEN`. | | `ENABLED_WHEN_AUTHORED_ROUTE_NO_FALLBACK` | warning | An authored route targets a conditional handoff with no `ON_FAILURE`. | | `ENABLED_WHEN_SAFETY_ROUTE_CONDITIONAL` | error | A constraint `ON_FAIL`/`ON_EXHAUSTED` handoff targets a fully conditional handoff. | | `ENABLED_WHEN_ON_START_UNSTABLE_DATA` | warning | An `ON_START` delegate's condition reads data that may not exist yet at session start. | `_metadata` is supplied by the caller. Use `ENABLED_WHEN` to shape the experience, not as an authorization boundary. Availability is always enforced — there's no runtime setting that turns it off, and an entry without `ENABLED_WHEN` behaves exactly as before this feature existed. The trace records which routes were offered and withheld on each model turn, and every refusal, with the condition text — never the session values it evaluated. *** ## Parallel delegation (fan-out) There is no `FAN_OUT:` section in ABL. Fan-out is a runtime capability, not an authored construct — agents do not declare it directly. When a single user message contains **multiple distinct requests** that need different specialists, a supervisor's runtime can dispatch them in parallel and then synthesize the branch results into one unified response. This is exposed to the reasoning model as a built-in `__fan_out__` system tool (you never write this tool yourself) and is bounded to **2–5 sub-tasks** per message. Separately, when multiple `DELEGATE` entries have their `WHEN` conditions satisfied on the same turn, the runtime can run those delegations concurrently. Each delegation runs in its own scope and its `RETURNS` are mapped back into the parent context as it completes. ```yaml expandable=true theme={null} DELEGATE: - AGENT: Sanctions_Screening WHEN: beneficiary_name IS SET PURPOSE: "Screen beneficiary" INPUT: name: beneficiary_name RETURNS: cleared: sanctions_clear - AGENT: Fraud_Detection WHEN: amount IS SET PURPOSE: "Score fraud risk" INPUT: amount: amount account: source_account RETURNS: score: fraud_score ``` *** ## ESCALATE ESCALATE transfers the conversation to a human operator. It is designed for situations where the agent cannot or should not continue autonomously. ### Syntax ```yaml expandable=true theme={null} ESCALATE: triggers: - WHEN: sanctions_screening_unavailable == true AND retry_count >= 2 REASON: "Sanctions screening service down. Compliance check cannot be bypassed." PRIORITY: critical TAGS: [compliance, service_outage] - WHEN: user.wants_human_agent == true REASON: "Customer requesting human specialist." PRIORITY: medium TAGS: [human_request] context_for_human: - customer_id - customer_name - amount - conversation_history routing: connection: livechat queue: "wire_operations_l2" skills: [wire_transfer, compliance] priority: 1 voice: # voice-channel transfer settings (see below) transfer_method: refer call_transfer_type: sip sip_headers: X-Escalation-Reason: "sanctions_review" on_human_complete: - IF human.resolved == true: COMPLETE - IF human.needs_agent == true: HANDOFF to specified_agent CONNECTOR_ACTION: create_servicenow_incident ``` ### Trigger properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `WHEN` | `string` | Yes | -- | Condition that triggers escalation. | | `REASON` | `string` | Yes | -- | Human-readable reason for the escalation. | | `PRIORITY` | `string` / `number` | No | `medium` | Priority level: `low`, `medium`, `high`, `critical`, or a non-negative integer. | | `TAGS` | `string[]` | No | -- | Tags for routing and categorization in the human agent queue. | | `SET` | `Record` | No | -- | Session variable assignments applied when this trigger fires (`key = value`). | | `GENESYS_SIP_HEADERS` | `object` | No | -- | Per-trigger override of the Genesys SIP transfer headers attached when this escalation transfers a voice call. See [Genesys SIP transfer headers](#genesys-sip-transfer-headers). | `PRIORITY` is optional and defaults to `medium`. Note that `ESCALATE` inside an `ON_ERROR` handler is **not** parsed — to escalate from an error handler use `THEN: ESCALATE with REASON: "..."` instead of an inline `ESCALATE:` block. ### Connector action `CONNECTOR_ACTION` names a connector action to invoke for ITSM integration when the escalation fires (for example, opening an incident in an external ticketing system). It is declared at the top level of the `ESCALATE` block, alongside `triggers`, `context_for_human`, `routing`, and `on_human_complete`. ### Priority levels | Level | Use case | | - | - | | `low` | Non-urgent requests (for example. general feedback). | | `medium` | Standard requests (for example. customer asks to speak with a human). | | `high` | Urgent issues (for example. service outages, repeated failures). | | `critical` | Immediate attention required (for example. compliance violations, fraud). | ### Context for human The `context_for_human` block lists session variable names to include in the escalation package. The human agent sees these values in their interface. ```yaml theme={null} context_for_human: - customer_id - customer_name - source_account - amount - fraud_score - conversation_history ``` You can also use structured context items with templates: ```yaml theme={null} context_for_human: - NAME: case_summary TEMPLATE: "Customer {{customer_name}} requesting wire of {{amount}} {{currency}}" INCLUDE: [fraud_score, sanctions_match_score] ``` ### Routing configuration The `routing` block controls how the escalation is routed in the human agent system. | Property | Type | Required | Default | Description | | - | - | - | - | - | | `connection` | `string` | No | -- | Human-agent connection/channel to route through. | | `queue` | `string` | No | -- | Target queue name in the human agent system. | | `skills` | `string[]` | No | -- | Required skills for the human agent. | | `priority` | `number` | No | -- | Numeric routing priority for the queue entry. | | `post_agent` | `string` | No | -- | What happens after the human agent finishes: `return` or `end`. | | `voice` | `object` | No | -- | Voice-channel transfer settings — how the live call leg is handed off at the SIP/transport layer. See [Voice transfer settings](#voice-transfer-settings). | Earlier drafts used `skill_tags` and `priority_boost`. The current runtime contract uses `skills` and `priority` — the older keys are silently ignored. The `routing` block also accepts several [advanced routing keys](#advanced-routing-keys) for richer human-agent integrations. #### Conditional routing entries `routing:` accepts two shapes, distinguished purely by what follows the colon: * A **mapping** (`routing: { connection: ..., queue: ..., ... }`, as above) — a single, unconditional destination. This is the legacy, still-supported form. * A **YAML sequence** (a list of `-`-prefixed entries) — an **ordered list of conditional routing entries**, each with the same fields as the single form plus `id` and `when`. When authored as a list, `routing` itself is left unset and the entries compile into `routing_entries` instead. ```yaml theme={null} ESCALATE: triggers: - WHEN: intent.category == "human_escalation" REASON: "Caller requested a live agent" PRIORITY: high routing: - id: whatsapp WHEN: system.channel == "whatsapp" connection: "Genesys Agent Transfer" - id: voice_es WHEN: system.channel == "voice" && system.locale == "es" voice: call_transfer_type: sip transfer_method: refer sip_transfer_id: "sip:es_billing_queue@sbc.example.com" - id: fallback WHEN: always connection: "Default Queue" ``` | Field | Type | Description | | - | - | - | | `id` | `string` | Optional label for authoring/observability. Defaults to `entry_` if omitted. | | `when` | `string` | Condition selecting this entry, in the same deterministic expression grammar as trigger `WHEN` clauses (comparisons, `AND`/`OR`/`NOT`, `IS [NOT] SET`, `has(...)`, `abl.(...)`) — **not** free text. `WHEN` (uppercase) is also accepted. Omit, or use `always` / `*`, for the unconditional default entry. | Every other field (`connection`, `queue`, `skills`, `priority`, `post_agent`, `voice`, and all the [advanced routing keys](#advanced-routing-keys)) means exactly the same thing per entry as it does in the single-object form. A routing entry's `when` must be **deterministic** — it's evaluated in code against session data and the reserved `system.*` values (`system.channel`, `system.locale`, and similar), never deferred to the model. This is stricter than a trigger's `WHEN`, which may be prompt-only/semantic: a condition that's valid on a `triggers.WHEN` but isn't deterministic is a **compile-time error** on a routing entry (`ESCALATE_ROUTING_WHEN_PROMPT_ONLY`) — a live transfer destination can never depend on model judgment. **Evaluation is first-match-wins, in authored order.** The compiler enforces the entry list's shape: | Rule | Enforcement | | - | - | | At most one unconditional (default) entry | More than one entry with no `when` (or `always`/`*`) is a compile error. | | A default entry, if present, must be last | A default entry followed by other entries is a compile error — those entries would be unreachable. | | No entry is required to match | A list with no default entry is valid; this isn't a compile-time exhaustiveness check. If none of the entries' conditions match at runtime, routing **fails closed**: no connector or voice transfer is attempted (the escalation's `HumanTask`/HITL fallback still stands). List-form routing never falls back to the project's `agentTransfer.defaultRouting` connection — that backstop applies only to the legacy single-object `routing:` form. To reach the project default from a list, author an explicit trailing default entry (omit `when`, or use `when: always`) with your own `connection`/`voice`. | An entry whose `WHEN` guards `system.channel == "voice"` without a `voice:` block, or guards a non-voice channel without a `connection`, gets an advisory (non-blocking) warning — worth checking, since a matched entry with neither is unlikely to route anywhere useful. ##### `sip_transfer_id` A voice routing entry (or the legacy single `routing.voice` block) may declare an explicit SIP transfer destination directly in the DSL with `sip_transfer_id` — a literal or `{{ }}`-templated SIP URI: ```yaml theme={null} voice: call_transfer_type: sip sip_transfer_id: "sip:{{escalation_skill}}@sbc.example.com" ``` This requires `call_transfer_type: sip` — declaring `sip_transfer_id` under `pstn` or `custom` is a compile error (PSTN transfers always use the project's PSTN transfer target, not a SIP transfer id). The compiler **always** stamps the resulting destination's provenance as `config` itself — an authored `sip_transfer_id_source` value is discarded and never trusted verbatim, the same trust posture as the `call_transfer` tool's `default_source: config` gate. Omit `sip_transfer_id` to keep today's behavior: the transfer target comes from the project-configured Agent Transfer settings. #### Advanced routing keys Beyond `connection` / `queue` / `skills` / `priority`, the `routing` block accepts additional keys for richer human-agent (contact center) integrations. Some are structured; others (`flow_policy`, `provider_config`, `metadata`) are **opaque pass-throughs** whose shape is defined by the receiving human-agent integration rather than validated by the ABL compiler. Every key accepts `snake_case` or `camelCase`. ```yaml expandable=true theme={null} ESCALATE: routing: connection: livechat queue: "wire_operations_l2" skills: [wire_transfer, compliance] priority: 1 # Route to specific named human agents, optionally waiting for one to be free named_agents: ["jane.doe", "john.smith"] named_agent_options: wait_for_agent: true wait_duration_seconds: 120 # Constrain which agents may pick up the conversation agent_matching_conditions: skills: [wire_transfer] skill_groups: [compliance_l2] agent_groups: [emea_desk] # Provider-defined routing sub-type and adapter configuration sub_type: "priority_callback" provider_config: campaign_id: "Q3-collections" disposition_required: true # Wait / no-agent behavior, declared in a TRANSFER_FLOW_POLICIES block flow_policy_ref: standard_wait # Opaque key-value pairs forwarded on the transfer payload's metadata metadata: case_id: "{{case_id}}" segment: "gold" triggers: - WHEN: user.wants_human_agent == true REASON: "User requested human support" PRIORITY: high ``` | Key | Type | Description | | - | - | - | | `sub_type` | `string` | Provider-defined routing sub-type / channel hint for the human-agent system. | | `named_agents` | `string[]` | Request specific human agents by name or id. | | `named_agent_options` | `object` | Options that apply when `named_agents` is set: `wait_for_agent` (`boolean`) and `wait_duration_seconds` (`number`). | | `agent_matching_conditions` | `object` | Constrain who may receive the transfer: `skills`, `skill_groups`, and `agent_groups` (each `string[]`). | | `flow_policy_ref` / `transfer_flow_policy_ref` | `string` | Name of a policy declared in a `TRANSFER_FLOW_POLICIES:` block that governs wait / no-agent behavior. See [Transfer flow policies](#transfer-flow-policies). | | `flow_policy` / `transfer_flow_policy` | `object` | The same policy authored **inline** instead of by reference. Free-form; shape defined by the integration. | | `provider_config` | `object` | Opaque provider-specific adapter configuration. Shape defined by the human-agent integration, not the ABL parser. | | `metadata` | `object` | Opaque key-value pairs delivered with the escalation on the transfer payload's metadata. Supports `{{ }}` interpolation. Reserved keys (`reason`, `priority`, flow policy) are always set by the runtime and cannot be overridden here. | These keys are hints for the receiving human-agent system — the valid `sub_type` values, `provider_config` shape, and inline `flow_policy` structure are defined by that integration, not the ABL compiler. Voice-channel transfer settings live under `routing.voice` (see [Voice transfer settings](#voice-transfer-settings)). #### Voice transfer settings When an escalation routes over a **voice** channel, the optional `routing.voice` block controls how the live call leg is handed off at the SIP/transport layer. It is ignored on non-voice channels. ```yaml theme={null} ESCALATE: routing: connection: livechat queue: "wire_operations_l2" voice: transfer_method: refer # invite | refer | bye call_transfer_type: sip # sip | pstn | custom (omit sip/pstn to infer from project config) sip_headers: # forwarded to the receiving SBC / contact center X-Escalation-Reason: "sanctions_review" X-Priority: "1" ``` | Field | Type | Description | | - | - | - | | `transfer_method` | `"invite"` \| `"refer"` \| `"bye"` | SIP signaling method used to transfer the active call leg. | | `call_transfer_type` | `"sip"` \| `"pstn"` \| `"custom"` | Whether to move the call over SIP or PSTN, or run a **custom** tool sequence (see below). Omit `sip`/`pstn` to infer from project configuration — SIP when a `sipTransferTargetUri` is configured, PSTN when only a `pstnTransferTargetNumber` is. | | `sip_headers` | `Record` | Custom SIP headers forwarded on the transfer to the receiving SBC or contact center. | | `sip_transfer_id` | `string` | Explicit SIP transfer destination URI (literal or `{{ }}`-templated). Requires `call_transfer_type: sip`. See [Conditional routing entries](#sip_transfer_id) above. | | `custom_tools` | `string[]` | Required when `call_transfer_type: custom` — an ordered list of existing project tool names run as the transfer sequence. See [Custom voice transfer](#custom-voice-transfer). | Without `sip_transfer_id`, `call_transfer_type` selects a **transport, not a destination** — the transfer target is project-owned (Agent Transfer settings), and authored DSL cannot name a phone number or SIP URI, the same boundary the built-in `call_transfer` tool enforces by default. `sip_transfer_id` is the one explicit exception to that rule (see above). For Genesys Audio Connector transfers, use the dedicated `genesys_sip_headers` block below rather than `sip_headers`. #### Custom voice transfer `call_transfer_type: custom` (ABLP-3552, CXone/NICE integration) replaces the SIP/PSTN leg transfer with an ordered sequence of **project tools**. Instead of moving the call, the runtime keeps the Artemis audio leg live and runs the named tools serially as a notification sequence — for example, obtaining a token from one HTTP tool and signaling an external contact-center API with the next. ```yaml theme={null} ESCALATE: triggers: - WHEN: caller.requests_human == true REASON: "Caller requested an agent" PRIORITY: high routing: voice: call_transfer_type: custom custom_tools: - nice_token # first tool may capture a SecretRef result - signal_api # next tool may consume that reference ``` * `custom_tools` is an ordered list of **1–8** exact, existing project **HTTP** tool names, invoked in the authored order. The first tool may capture a [SecretRef](/agent-platform/abl/reference/tools#reuse-secrets-across-tools-secretref) that a later tool consumes. * `custom` routing is **mutually exclusive** with a `connection` — a custom escalation has no SIP/PSTN connection. * The compiler validates shape (blocking errors): the transfer type must be `sip`/`pstn`/`custom`; `custom` requires 1–8 `custom_tools`; each name must be a non-empty ASCII identifier (`^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$`); names must be unique. Tool **existence and HTTP kind** are enforced at runtime (a non-HTTP or unknown tool is rejected then). Custom voice transfer is **notification-only** in this version — it signals an external system while the caller stays on the Artemis leg; it does not hand the live call off to a human queue. Use `sip`/`pstn` (or `ESCALATE` routing to a human connection) when the call itself must be transferred. #### Genesys SIP transfer headers When an escalation transfers a **voice** call to Genesys (through the Genesys Audio Connector), `genesys_sip_headers` attaches custom SIP headers to the transfer — carrying the target queue, a reason code, and optional metadata. Declare a default under `routing:` and/or an override on individual triggers with the `GENESYS_SIP_HEADERS:` block. A trigger's block takes precedence over the routing default. ```yaml expandable=true theme={null} ESCALATE: triggers: - WHEN: intent.category == "billing_dispute" REASON: "Billing dispute requires a specialist." PRIORITY: high GENESYS_SIP_HEADERS: # per-trigger override queue_header_key: "X-Genesys-Queue" queue_name: "billing_disputes" reason_header_key: "X-Genesys-Reason" reason: "billing_dispute" metadata_header_key: "X-Genesys-Data" metadata: tier: "gold" region: "us-east" routing: connection: livechat queue: "billing_l2" genesys_sip_headers: # routing-level default for all triggers queue_header_key: "X-Genesys-Queue" queue_name: "billing_default" ``` Each field is optional. The `*_header_key` fields name a SIP header; the paired field supplies its value. | Field | Type | Description | | - | - | - | | `queue_header_key` | `string` | SIP header **name** that carries the queue. | | `queue_name` | `string` | Value placed in the queue header. | | `reason_header_key` | `string` | SIP header **name** that carries the reason. | | `reason` | `string` | Value placed in the reason header. | | `metadata_header_key` | `string` | SIP header **name** that carries the metadata. | | `metadata` | `string` or `object` | A string is sent verbatim; a map is serialized to a compact JSON string. | The compiler validates the block against the same limits the runtime transfer enforces: * Header-name fields (`*_header_key`) must be RFC 3261 tokens, at most 64 characters. * Header values (`queue_name`, `reason`, and the serialized `metadata`) must contain no CR/LF/NUL characters and stay within 1024 UTF-8 bytes. * `metadata` must be a string or a non-empty map — arrays and scalar values are rejected; an empty `{}` is ignored. * Two header-name fields must not resolve to the same SIP header key (compared case-insensitively). A collision (`GENESYS_SIP_HEADER_KEY_COLLISION`) is a compile error, because the transfer rejects colliding header keys at runtime. * Unrecognized fields are ignored with a warning. Keys may be written in `snake_case` or `camelCase` (for example `queue_header_key` or `queueHeaderKey`). #### Transfer flow policies `routing.flow_policy_ref` (or `transfer_flow_policy_ref`) names a policy declared in a top-level `TRANSFER_FLOW_POLICIES:` block. A transfer flow policy defines what happens while a caller waits in queue or when no agent is available — for example, playing a message or triggering a follow-up workflow. The policy body is free-form (its shape is defined by the human-agent integration, not the ABL parser). ```yaml expandable=true theme={null} TRANSFER_FLOW_POLICIES: standard_wait: on_queued: type: message text: "Hello {{user.first_name}}, you're in the queue — a specialist will join shortly." on_no_agents: type: workflow workflow_id: no_agents_followup ESCALATE: routing: connection: livechat queue: support flow_policy_ref: standard_wait triggers: - WHEN: user.wants_human_agent == true REASON: "User requested human support" PRIORITY: high ``` ### Post-completion actions The `on_human_complete` block defines what happens after the human agent finishes. ```yaml theme={null} on_human_complete: - IF human.resolved == true: COMPLETE - IF human.needs_agent == true: HANDOFF to specified_agent - IF human.needs_followup == true: CONTINUE ``` Each entry has a `condition` and an `action`. The action can be `COMPLETE` (end the conversation), `HANDOFF` (transfer to another agent), or `CONTINUE` (resume the current agent). When the human agent closes the conversation, a `FEEDBACK` survey card can be shown — authored either on the agent itself or inside a `FLOW:` step. See [Post-transfer surveys](/agent-platform/abl-reference/rich-content#post-transfer-surveys-escalate-to-a-human-agent) for exactly which block is picked and when. *** ## COMPLETE COMPLETE defines the conditions under which the agent considers its task finished. Each completion condition specifies a `WHEN` expression and an optional response. ### Syntax ```yaml expandable=true theme={null} COMPLETE: - WHEN: confirmation_number IS SET AND transfer_status == "released" RESPOND: | Your wire transfer has been executed successfully. **Confirmation:** {{confirmation_number}} **Amount:** {{amount}} {{currency}} **To:** {{beneficiary_name}} **Estimated Arrival:** {{estimated_arrival}} STORE: "wire_transfers" - WHEN: transfer_status == "queued" RESPOND: "Your wire has been queued for the next processing window." ``` ### Properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `WHEN` | `string` | Yes | -- | Boolean condition over session state. The agent completes when this evaluates to true. | | `RESPOND` | `string` | No | -- | Final message to the user. Supports `{{}}` template interpolation. | | `SET` | `Record` | No | -- | Session assignments applied when the completion fires (for example, clear the sticky flag that triggered it). | | `TERMINAL` | `boolean` | No | `true` | When `false`, the response renders (including interactive widgets like a closing feedback collector) **without** ending the session — control returns to the agent for the next turn. | | `STORE` | `string` | No | -- | Collection or path name to store the completion result for analytics. | | `MODEL_CONTEXT` | `string` | No | -- | Text retained for the model instead of the delivered `RESPOND` in future model calls. See [MODEL\_CONTEXT on a response](/agent-platform/abl-reference/flow#model_context-on-a-response). | `COMPLETE`'s `WHEN` is **deterministic** — it must be a boolean expression over session state. Plain-English / intent-style text is rejected at compile time (`PLAIN_ENGLISH_COMPLETE_WHEN`). Use `NLU`/routing for intent matching, not `COMPLETE WHEN`. A fully-quoted `WHEN` (`WHEN: "status == \"done\""`) is unwrapped and evaluated the same as an unquoted one when it contains an operator — see [Quoting a condition](/agent-platform/abl-reference/expressions-and-functions#quoting-a-condition). A `COMPLETE` condition that matches does **not** complete the agent if the agent's own `CONSTRAINTS` fail on the state it would complete with — the failing constraint's `ON_FAIL` runs instead, so a completion can never bypass a constraint that would otherwise block the same state. Example of a non-terminal completion that collects feedback and then keeps the session open: ```yaml theme={null} COMPLETE: - WHEN: feedback_requested == true RESPOND: "Thanks! How would you rate this conversation?" SET: feedback_requested: "false" TERMINAL: false ``` Always clear the condition that triggered a `COMPLETE` block when using `TERMINAL: false` (for example, by using `SET`). Otherwise, the same completion condition may be evaluated again on the next user message, creating a completion loop. When such a loop is detected, the platform automatically closes the session and logs a warning. ### Rich content in completion COMPLETE responses support voice configuration and rich content, the same as any RESPOND: ```yaml expandable=true theme={null} COMPLETE: - WHEN: booking_confirmed == true RESPOND: "Your booking is confirmed!" VOICE: ssml: "Your booking is confirmed!" RICH_CONTENT: MARKDOWN: | ## Booking Confirmed | Detail | Value | |--------|-------| | Reference | {{booking_ref}} | | Date | {{travel_date}} | ACTIONS: - id: view_itinerary type: button label: "View Itinerary" ``` `FORMATS`/`RICH_CONTENT`, `VOICE`, and `ACTIONS` are recognized regardless of where they appear in the condition — immediately after `RESPOND`, after `STORE`/`SET`/`TERMINAL`, or on a condition with **no** `RESPOND` at all (for example, a completion that renders only a `FEEDBACK` card): ```yaml theme={null} COMPLETE: - WHEN: transfer_status == "released" STORE: "wire_transfers" RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: Post_Chat_NPS ``` ### Completion evaluation Completion conditions are evaluated after every turn, in declaration order. The first matching condition triggers completion. If no condition matches, the agent continues the conversation. *** ## Context passing ### Evaluation order across constructs When multiple multi-agent constructs apply on the same turn, the runtime evaluates them in this order: 1. **ESCALATE triggers** -- checked first; critical safety and compliance. 2. **HANDOFF rules** -- evaluated by priority (lower first). 3. **DELEGATE conditions** -- evaluated in declaration order. 4. **COMPLETE conditions** -- checked last. *** ## Complete supervisor example ```yaml expandable=true theme={null} SUPERVISOR: Customer_Service_Hub VERSION: "2.0" GOAL: "Route customers to the right specialist" PERSONA: | Professional customer service coordinator. Routes requests efficiently and preserves context across agent transfers. HANDOFF: - TO: Live_Agent WHEN: intent.category == "escalation" CONTEXT: pass: [user_id, conversation_summary] summary: "User requests human assistance" EXPECT_RETURN: false - TO: Sales_Agent WHEN: intent.category == "new_booking" CONTEXT: pass: [search_context, preferences] summary: "User looking to book" EXPECT_RETURN: false ESCALATE: triggers: - WHEN: routing_failures >= 3 REASON: "Multiple routing failures" PRIORITY: high ON_ERROR: routing_failure: RESPOND: "Let me connect you with someone who can help." RETRY: 1 THEN: HANDOFF Live_Agent COMPLETE: - WHEN: handoff_successful == true RESPOND: "I've connected you with the right specialist." ``` *** **Related articles:** * [NLU](/agent-platform/abl-reference/nlu) -- intent classification that drives routing decisions * [Memory and Constraints](/agent-platform/abl-reference/memory-and-constraints) -- session and persistent state used in routing conditions * [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions) -- condition syntax for WHEN clauses * [Lifecycle and hooks](/agent-platform/abl-reference/lifecycle-and-hooks) -- ON\_START and hooks that interact with multi-agent flows * [Rich content](/agent-platform/abl-reference/rich-content) -- formatting for RESPOND and COMPLETE messages # NLU (Natural Language Understanding) Source: https://koreai.mintlify.app/agent-platform/abl/reference/nlu The `NLU:` block configures how the agent classifies user intent, extracts entities, resolves synonyms, and matches utterances. NLU definitions are used by the runtime for intent routing, digression matching, and entity extraction during conversation. ## Overview ABL's NLU configuration supports: * **Intent classification** with keyword patterns and example utterances. * **Entity extraction** with typed extractors (enum, pattern, location, date, number, free text). * **Synonyms** for normalizing variant expressions to canonical values. * **Embeddings-based matching** for semantic similarity when keyword patterns are insufficient. * **Multi-language support** with per-language model configuration. * **A glossary** for domain-specific terminology. ```yaml expandable=true theme={null} NLU: intents: - NAME: send_wire PATTERNS: ["wire transfer", "send money", "wire funds"] EXAMPLES: ["I need to wire $50,000 to Germany", "Can I send a domestic wire?"] entities: - NAME: currency_code TYPE: enum VALUES: [USD, EUR, GBP, JPY] SYNONYMS: USD: [dollars, usd, bucks] EUR: [euros, eur] glossary: - "SWIFT/BIC -- Code identifying a bank globally" - "Fedwire -- Federal Reserve real-time settlement system" ``` ## Intent classification Intents represent categories of user messages. Each intent has a name, keyword patterns for quick matching, and optional example utterances for model-based classification. ### Syntax ```yaml theme={null} NLU: intents: - NAME: book_flight PATTERNS: ["book flight", "find flights", "search flights", "fly to"] EXAMPLES: - "I want to fly to Paris next Tuesday" - "Find me a round trip to London" - "Book two seats on the morning flight to NYC" ENTITIES: [destination, travel_date, passenger_count] ``` ### Intent properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `NAME` | `string` | Yes | -- | Unique intent identifier. | | `PATTERNS` | `string[]` | Yes | -- | Keyword patterns for quick substring matching. | | `EXAMPLES` | `string[]` | No | -- | Full example utterances for model-based classification. Improves accuracy. | | `EXAMPLES_FILE` | `string` | No | -- | Path to an external file containing example utterances (one per line). | | `ENTITIES` | `string[]` | No | -- | Entity types expected to co-occur with this intent. | ### Pattern matching Patterns are matched as case-insensitive substrings against the user's message. If any pattern appears in the message, the intent is a candidate match. Pattern matching is the first tier of classification; it is fast but imprecise. ### Example-based classification When `EXAMPLES` are provided, the runtime uses an LLM or embedding model to classify messages based on semantic similarity to the examples. This is more accurate than pattern matching but requires more compute. ### Intent with external examples file For intents with many examples, reference an external file: ```yaml theme={null} NLU: intents: - NAME: product_inquiry PATTERNS: ["tell me about", "what is", "describe"] EXAMPLES_FILE: "./nlu/product_inquiry_examples.txt" ``` ## Categories The `categories:` sub-block declares coarse-grained message categories matched by keyword patterns. Categories are a lighter-weight classification than full intents (no examples or entity bindings). ```yaml theme={null} NLU: categories: - NAME: billing PATTERNS: ["invoice", "charge", "refund", "payment"] - NAME: technical PATTERNS: ["error", "bug", "not working", "broken"] ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `NAME` | `string` | Yes | -- | Unique category identifier. | | `PATTERNS` | `string[]` | Yes | -- | Keyword patterns for matching. | ## Entity extraction Entities are structured values extracted from user messages. ABL supports six entity types. ### Syntax ```yaml expandable=true theme={null} NLU: entities: - NAME: currency_code TYPE: enum VALUES: [USD, EUR, GBP, JPY, CHF, CAD, AUD] SYNONYMS: USD: [dollars, usd, bucks, us dollars] EUR: [euros, eur] GBP: [pounds, sterling, gbp, quid] - NAME: phone_number TYPE: pattern PATTERN: "\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b" VALIDATION: "Must be a valid US phone number" - NAME: destination TYPE: location - NAME: travel_date TYPE: date - NAME: passenger_count TYPE: number - NAME: special_request TYPE: free_text ``` ### Entity types | Type | Description | Additional properties | | - | - | - | | `enum` | Matches against a fixed set of values with optional synonyms. | `VALUES`, `SYNONYMS` | | `pattern` | Matches against a regular expression. | `PATTERN`, `VALIDATION` | | `location` | Extracts geographic locations (cities, countries, addresses). | -- | | `date` | Extracts date and time references, including relative expressions ("next Tuesday"). | -- | | `number` | Extracts numeric values. | -- | | `free_text` | Captures arbitrary text spans. Useful for notes, descriptions, or comments. | -- | ### Entity properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `NAME` | `string` | Yes | -- | Unique entity identifier. | | `TYPE` | `string` | Yes | -- | Entity type. See [Entity types](#entity-types). | | `VALUES` | `string[]` | No | -- | Valid values (for `enum` type). | | `SYNONYMS` | `Record` | No | -- | Synonym mappings. Keys are canonical values. | | `PATTERN` | `string` | No | -- | Regular expression (for `pattern` type). | | `VALIDATION` | `string` | No | -- | Validation description or expression. | | `SENSITIVE` | `boolean` | No | `false` | Marks extracted values as PII for privacy-aware handling. | ### Synonyms Synonyms map variant expressions to canonical values. When a synonym is detected, the runtime normalizes it to the canonical form before storing: ```yaml theme={null} SYNONYMS: USD: [dollars, usd, bucks, us dollars] EUR: [euros, eur] GBP: [pounds, sterling, gbp, quid] ``` If the user says "100 bucks", the entity extraction yields `currency_code: "USD"`. ## Embeddings-based matching For more accurate semantic matching, enable embeddings-based NLU. This uses vector similarity to match user messages against intent examples. ```yaml theme={null} NLU: embeddings: enabled: true provider: "bge-m3" model: "BAAI/bge-m3" threshold: 0.75 cache_ttl: 3600 ``` ### Embeddings properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `enabled` | `boolean` | Yes | -- | Whether embeddings-based matching is active. | | `provider` | `string` | No | -- | Embedding model provider name. | | `model` | `string` | No | -- | Specific model identifier. | | `base_url` | `string` | No | -- | Custom endpoint URL for the embedding service. | | `threshold` | `number` | No | -- | Similarity threshold (0.0--1.0). Below this, the match is rejected. | | `cache_ttl` | `number` | No | -- | Cache duration in seconds for computed embeddings. | ## Multi-language support ABL NLU supports multiple languages with per-language model configuration and optional code-switching detection. ```yaml theme={null} NLU: languages: [en, es, fr] default_language: en allow_code_switching: true language_models: en: "gpt-4o" es: "gpt-4o" fr: "gpt-4o" ``` ### Multi-language properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `languages` | `string[]` | No | -- | Supported language codes. | | `default_language` | `string` | No | -- | Default language when detection is ambiguous. | | `allow_code_switching` | `boolean` | No | `false` | Allow users to switch languages mid-conversation. | | `language_models` | `Record` | No | -- | Per-language model assignments for classification. | ## Model configuration Specify which models to use for NLU classification tasks: ```yaml theme={null} NLU: models: fast: "claude-haiku-4-5-20251001" balanced: "claude-sonnet-4-5-20250929" ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `fast` | `string` | No | -- | Model for fast, low-latency classification. | | `balanced` | `string` | No | -- | Model for higher-accuracy classification. | ## Evaluation configuration Control NLU evaluation behavior for monitoring and testing: ```yaml theme={null} NLU: evaluation: log_predictions: true ab_test: true confidence_threshold: 0.6 ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `log_predictions` | `boolean` | No | -- | Log all NLU predictions for analysis. | | `ab_test` | `boolean` | No | -- | Enable A/B testing between NLU models. | | `confidence_threshold` | `number` | No | -- | Minimum confidence score for accepting a prediction. | ## Glossary The glossary defines domain-specific terms and abbreviations. These definitions are injected into the LLM context to improve understanding of specialized vocabulary. ```yaml theme={null} NLU: glossary: - "SWIFT/BIC -- Society for Worldwide Interbank Financial Telecommunication code" - "IBAN -- International Bank Account Number" - "Fedwire -- Federal Reserve real-time gross settlement system" - "OFAC -- Office of Foreign Assets Control" - "Beneficiary -- The person or entity receiving the wire transfer" ``` Each glossary entry is a plain string in `"term -- definition"` format. ## External NLU configuration For complex NLU setups, reference an external configuration file: ```yaml theme={null} NLU: config_file: "./nlu/config.yaml" ``` The external file is merged with any inline NLU configuration. Inline values take precedence. ## Complete example ```yaml expandable=true theme={null} NLU: models: fast: "claude-haiku-4-5-20251001" balanced: "claude-sonnet-4-5-20250929" languages: [en, es] default_language: en allow_code_switching: true intents: - NAME: send_wire PATTERNS: ["wire transfer", "send money", "wire funds", "remittance"] EXAMPLES: - "I need to wire $50,000 to a vendor in Germany" - "Can I send a domestic wire from my business account?" - NAME: check_status PATTERNS: ["wire status", "where is my wire", "tracking"] EXAMPLES: - "What's the status of confirmation WR-2024-88431?" - NAME: cancel_wire PATTERNS: ["cancel the wire", "stop the transfer", "recall"] entities: - NAME: currency_code TYPE: enum VALUES: [USD, EUR, GBP, JPY, CHF, CAD, AUD] SYNONYMS: USD: [dollars, usd, bucks] EUR: [euros, eur] GBP: [pounds, sterling, quid] - NAME: transfer_type TYPE: enum VALUES: [domestic, international] SYNONYMS: domestic: [within the us, fedwire, local wire] international: [overseas, swift, cross-border] embeddings: enabled: true provider: "bge-m3" threshold: 0.75 cache_ttl: 3600 glossary: - "SWIFT/BIC -- Code identifying a bank globally" - "Fedwire -- Federal Reserve real-time settlement system" - "OFAC -- Office of Foreign Assets Control" ``` ## Named entities (`ENTITIES:` section) Separate from the `NLU:` block, the top-level `ENTITIES:` section defines **reusable** semantic entities that are shared by both NLU (for recognition) and [GATHER](/agent-platform/abl-reference/gather) (via `entity_ref`). Declaring an entity once lets multiple fields and agents reuse the same type, allowed values, synonyms, and validation. ```yaml expandable=true theme={null} ENTITIES: request_type: TYPE: enum VALUES: [REQ_REFUND, REQ_EXCHANGE, REQ_CANCEL] SYNONYMS: REQ_REFUND: [refund, money back, reimburse] REQ_EXCHANGE: [exchange, swap] booking_ref: TYPE: pattern PATTERN: "^[A-Z]{2}[0-9]{4,6}$" VALIDATION: "Two letters followed by 4-6 digits" SENSITIVE: false ``` | Property | Type | Required | Default | Description | | - | - | - | - | - | | `TYPE` | `string` | No | `"string"` | One of the 15 unified types (see below). | | `VALUES` | `string[]` | No | -- | Canonical values (for `enum`). | | `SYNONYMS` | `Record` | No | -- | Canonical value → alternative forms. | | `PATTERN` | `string` | No | -- | Regex (for `pattern`). | | `VALIDATION` | `string` | No | -- | Intrinsic validation expression/description. | | `SENSITIVE` | `boolean` | No | `false` | Marks the value as PII. | | `DESCRIPTION` | `string` | No | -- | Semantic, model-facing description of what the entity value represents. A GATHER field's `entity_ref` inherits it. | | `EXTRACTION_HINTS` | `string[]` | No | -- | Example surface forms that guide extraction. Inherited by GATHER fields that reference the entity. | Top-level entities accept the full unified type set: `string`, `text`, `free_text`, `number`, `integer`, `float`, `currency`, `boolean`, `date`, `datetime`, `email`, `phone`, `enum`, `pattern`, `location`. This is a distinct section from `NLU: entities:`. Top-level `ENTITIES:` defaults `TYPE` to `string` and supports all 15 types, whereas the `NLU: entities:` sub-block defaults to `enum` and supports only 6 types (`enum`, `pattern`, `location`, `date`, `number`, `free_text`). A GATHER field's `entity_ref` points at a top-level `ENTITIES:` entry. ## Intent categories (`INTENTS:` section) The top-level `INTENTS:` section declares supervisor routing categories (distinct from `NLU: intents:`). Each entry is a category name with an optional description, and the block can set a lexical-fallback mode. ```yaml theme={null} INTENTS: LEXICAL_FALLBACK: when_unavailable new_booking: "Customer wants to book new travel" manage_booking: "Change or cancel an existing booking" billing ``` | Property | Type | Description | | - | - | - | | category name | `string` | Intent category identifier; optionally followed by `: "description"`. | | `LEXICAL_FALLBACK` | `string` | When to fall back to lexical matching: `never`, `when_unavailable`, or `always`. | ## Multi-intent handling (`MULTI_INTENT:`) The top-level `MULTI_INTENT:` section configures how the agent handles a single user message that contains **more than one intent** — for example, *"Book me a flight to NYC and also cancel my hotel reservation."* When enabled, the NLU pipeline detects and triages multiple intents per turn, and the `strategy` controls how they are dispatched. ```yaml theme={null} MULTI_INTENT: strategy: primary_queue unknown_relationship_strategy: sequential max_intents: 5 confidence_threshold: 0.7 queue_max_age_ms: 300000 enabled: true ``` ### Properties | Property | Type | Default | Description | | - | - | - | - | | `enabled` | `boolean` | `true` | Whether multi-intent detection and triage is active. | | `strategy` | `string` | `primary_queue` | How detected intents are dispatched. See [Strategies](#multi-intent-strategies). | | `unknown_relationship_strategy` | `string` | `parallel` | Fallback strategy when the relationship between intents is unknown: `sequential`, `parallel`, or `disambiguate`. | | `max_intents` | `number` | `3` | Maximum intents to consider per message (1–10). | | `confidence_threshold` | `number` | `0.6` | Minimum confidence (0.0–1.0) for a secondary intent to be accepted. | | `queue_max_age_ms` | `number` | `600000` | Time-to-live for queued intents in milliseconds (0–3,600,000). After this, queued intents are dropped. | | `advance_on_primary_response` | `boolean` | `true` | With `strategy: primary_queue`, whether the primary specialist's first delivered, non-blocking answer counts as done for queue purposes — advancing to the next queued intent in the same turn. Also applies to a `sequential` task resuming after a nested child returns, and to the option picked from a `disambiguate` menu. Set `false` to require an explicit completion signal (a `COMPLETE` condition, a terminal control, or return-to-parent) before advancing. See the note below for how a prose-only reply is judged. | ### Multi-intent strategies | Strategy | Behavior | | - | - | | `primary_queue` | Handle the highest-confidence intent immediately; queue the rest and surface them one-by-one after it completes. (Default.) | | `sequential` | Execute the detected intents one at a time in order, carrying context forward between them. | | `parallel` | Fan out to sub-agents simultaneously (supervisor agents only). | | `disambiguate` | Present the detected intents to the user and ask which to handle first. | | `auto` | Resolves at runtime from the assessed relationship between the intents: independent and dependent requests resolve to `sequential`, and ambiguous ones resolve to `disambiguate`. It never fans out — author `strategy: parallel` directly for that. | The user-facing prompts for the `disambiguate` and `primary_queue` flows (the "I noticed more than one request…" text) are customizable via the [`MESSAGES:`](/agent-platform/abl-reference/lifecycle-and-hooks#messages) block (keys such as `multi_intent_disambiguate_header` and `multi_intent_queued_notice`). A specialist with a missing required `GATHER` field, a pending input/tool confirmation, an authentication step, an escalation, or a still-waiting child keeps the floor regardless of `advance_on_primary_response`. A request whose tool/model call errored (and the customer was told) counts as finished, so the plan moves on. For a reply given only in prose (no `GATHER`) — the ambiguous case — the Runtime judges the reply itself: one that still waits on the customer (an unresolved follow-up question) keeps the floor, a finished-sounding one advances, and one the Runtime can't judge keeps the floor. Set `advance_on_primary_response: false` to keep the specialist in control regardless of what it replies. In a `sequential` plan, the last request's specialist keeps the customer's next reply when it answers without finishing (no `COMPLETE`, no return) — the same as after a single routed request — and may hand that reply back with `__return_to_parent__` when the supervisor's handoff to it declared `RETURN: true`. *** **Related articles:** * [Multi-Agent and Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor) -- intent-based routing in Supervisors * [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions) -- expression syntax used in intent conditions * [Memory and Constraints](/agent-platform/abl-reference/memory-and-constraints) -- NLU results stored in session variables # Rich content Source: https://koreai.mintlify.app/agent-platform/abl/reference/rich-content This page documents ABL's rich content output formats: voice configuration, format-specific content (Markdown, Adaptive Cards, HTML, Slack, WhatsApp), carousels, interactive actions, feedback surveys, and reusable templates. For the expression language (operators, functions, `{{}}` interpolation syntax), see [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions). *** ## Rich content ABL supports multi-format output for delivering responses across different channels (web, mobile, voice, messaging platforms). The `VOICE:`, `RICH_CONTENT:`, and `ACTIONS:` blocks can be attached to any `RESPOND` statement, `COMPLETE` condition, or lifecycle handler. A pure reasoning agent (no `FLOW:`) may also declare top-level `RICH_CONTENT:` and `ACTIONS:` blocks that attach to its generated responses. ### Overview A single response can include: * **Plain text** -- the default `RESPOND` string. * **Voice configuration** -- SSML markup or natural language voice instructions. * **Rich content** -- Markdown, Adaptive Cards, HTML, Slack Block Kit, WhatsApp, or AG-UI. * **Carousels** -- scrollable card collections with images and buttons. * **Interactive actions** -- buttons, select menus, and input fields. * **Templates** -- reusable named response definitions with interpolation. The runtime selects the appropriate format based on the delivery channel. ### Voice configuration Voice configuration provides channel-specific voice output. The `VOICE:` block can appear alongside any `RESPOND`. #### Syntax ```yaml theme={null} RESPOND: "Your booking is confirmed for December 15th." VOICE: ssml: | Your booking is confirmed for 12/15/2025. instructions: "Speak in a warm, congratulatory tone" plain_text: "Your booking is confirmed for December fifteenth." ``` #### Voice properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `provider` | `string` | No | -- | TTS provider (for example. `elevenlabs`, `google`, `azure`, `openai`). | | `voice_id` | `string` | No | -- | Provider-specific voice identifier. | | `speed` | `number` | No | `1.0` | Speech-rate multiplier (`1.0` = normal). | | `ssml` | `string` | No | -- | W3C SSML markup for TTS engines (Google, Azure, Amazon Polly). | | `instructions` | `string` | No | -- | Natural language voice style instructions (OpenAI Realtime, Gemini Live). | | `plain_text` | `string` | No | -- | Voice-optimized plaintext. Used by ElevenLabs and as a fallback for all engines. | | `pace` | `number` | No | -- | Numeric speaking-rate multiplier (authoring alias for speed/pace controls). | | `temperature` | `number` | No | -- | Provider-specific TTS sampling/variation control. | | `auto_tts_language_switch` | `boolean` | No | -- | Switch the TTS voice/model to match the detected language of the response. | #### SSML example ```yaml theme={null} VOICE: ssml: | Your wire transfer of $50,000 USD has been executed. The confirmation number is WR-2024-88431. ``` #### Natural language instructions For voice platforms that accept style instructions rather than SSML: ```yaml theme={null} VOICE: instructions: "Speak slowly and clearly, emphasizing the confirmation number. Use a professional but warm tone." ``` ### Rich content formats The rich-content block provides format-specific variants of a response. The runtime selects the variant matching the delivery channel. `RICH_CONTENT:` is the canonical block keyword; `FORMATS:` is the original spelling, kept as an accepted alias. The `.agent.yaml` format accepts `rich_content:` or `formats:` the same way. Most places a rich-content block is authored — directly under a `RESPOND:` (in a flow step, `ON_START`, `HOOKS:`, or an `ON_ACTION`/`ON_ERROR` handler), in a tool's `on_result:`/`on_error:` action block, and in a `COMPLETE:` condition (including one with no `RESPOND:` at all) — accept either spelling interchangeably. #### Syntax ```yaml expandable=true theme={null} RESPOND: "Here are your flight options." FORMATS: MARKDOWN: | ## Flight Options | Flight | Departure | Arrival | Price | |--------|-----------|---------|-------| | AA 142 | 8:00 AM | 11:30 AM | $349 | | UA 891 | 10:15 AM | 1:45 PM | $289 | ADAPTIVE_CARD: | { "type": "AdaptiveCard", "body": [ {"type": "TextBlock", "text": "Flight Options", "size": "large"} ] } HTML: |

Flight Options

...
SLACK: | { "blocks": [ {"type": "header", "text": {"type": "plain_text", "text": "Flight Options"}} ] } ``` #### Rich content properties Rich content supports two kinds of properties. Channel-specific string formats carry a payload written for one channel. Structured template formats define content once, and the runtime renders it for each channel. **Channel-specific string formats:** | Property | Type | Required | Default | Description | | - | - | - | - | - | | `MARKDOWN` | `string` | No | -- | Formatted markdown text. | | `ADAPTIVE_CARD` | `string` | No | -- | JSON string conforming to the Microsoft Adaptive Cards schema. | | `HTML` | `string` | No | -- | HTML content for web-based channels. | | `SLACK` | `string` | No | -- | JSON string conforming to the Slack Block Kit format. | | `AG_UI` | `string` | No | -- | JSON string for AG-UI / CopilotKit events. | | `WHATSAPP` | `string` | No | -- | JSON string for WhatsApp interactive messages. | **Structured template formats** (channel-neutral; the runtime renders them per channel): Structured templates describe content as data, such as a carousel, table, or chart. The runtime converts each template to the native format of the target channel. Templates fall into two groups: *basic templates* for simple display, and *data-rich templates* for structured data. *Basic templates* — display-only, no aggregation or layout options beyond their own items: | Property | Type | Description | | - | - | - | | `CAROUSEL` | `object` | Scrollable card collection. See [Carousels](#carousels). | | `QUICK_REPLIES` | `array` | Quick-reply chips: each `{ id, label, iconUrl? }`. See [Quick replies](#quick-replies). | | `LIST` | `object` | `{ title?, items: [{ title, subtitle?, imageUrl?, defaultActionUrl? }] }`. See [Lists](#lists). | | `IMAGE` | `object` | `{ url, alt?, thumbnailUrl?, caption? }`. See [Media](#media-image-video-audio). | | `VIDEO` | `object` | `{ url, alt?, thumbnailUrl?, caption? }`. See [Media](#media-image-video-audio). | | `AUDIO` | `object` | `{ url, alt?, thumbnailUrl?, caption? }`. See [Media](#media-image-video-audio). | | `FILE` | `object` | `{ url, filename, sizeBytes?, mimeType? }`. See [Files](#files). | *Data-rich templates* — designed to present structured/tabular data, with extra options like `adaptive` narrowing: | Property | Type | Description | | - | - | - | | `KPI` | `object` | `{ label, value, unit?, trend?: up/down/flat, iconUrl? }`. See [KPIs](#kpis). | | `TABLE` | `object` | `{ columns: [{ key, header, align?: left/center/right }], rows, maxVisibleRows? }`. See [Tables](#tables). | | `CHART` | `object` | `{ type: bar/line/pie, title?, data: [{ label, value, color? }] }`. See [Charts](#charts). | | `FORM` | `object` | `{ title?, fields: [ActionElement], submitLabel? }`. See [Forms](#forms). | | `PROGRESS` | `object` | `{ label?, value, max?, variant?: bar/circle }`. See [Progress](#progress). | | `FEEDBACK` | `object` | Rating/feedback survey. `{ FEEDBACK_TEMPLATE?, prompt, type: thumbs/star/scale, max?, submit_label?, pending_message?, success_message?, error_message?, comment_threshold?, comment_prompt?, comment_placeholder?, when?, _rules? }`. See [FEEDBACK surveys](#feedback-surveys). | Named templates (in the `TEMPLATES:` block) may also declare a `RENDERABLES:` list — customer-owned structured payloads with `name`, `payload`, optional `targets` (`api`/`sdk_websocket`/`http_async`), `fallbackText`, and `schemaRef`. Several structured templates also support **collection binding** — `from:`/`template:` — to render one entry per array item; see [Collection binding](#collection-binding) for exactly which ones. #### Template modifiers Two cross-cutting modifiers apply to structured templates: * **`when`** — a conditional render guard (expression). On an agent-level rich-content block, the template renders only when the expression is truthy at reasoning finalization. Available on the rich-content block and on each structured template. See [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions) for expression syntax. * **`adaptive`** — on `LIST`, `KPI`, and `TABLE` templates whose items/rows are bound to a tool variable, set `adaptive: true` to let the reasoning agent narrow which items/rows/columns render for the user's request. #### Collection binding Several structured templates have an array-valued property — `CAROUSEL.cards`, `QUICK_REPLIES` itself, `LIST.items`, `TABLE.columns` and `TABLE.rows`, `CHART.data`, and `FORM.fields`. Each of these accepts **three** forms: 1. **A literal array** — write the items directly, as shown in every static example on this page. 2. **A `from`/`template` binding** — resolve the array from a session variable at render time, and render one entry per array item: ```yaml theme={null} data: from: "{{tool_result.output.sectors}}" # or bare, without {{ }}: tool_result.output.sectors template: label: "{{name}}" value: "{{percentage}}" ``` `from` names the session/tool-result path holding the array (the `{{ }}` wrapper is optional — both forms resolve identically). `template` is applied once per array item; inside it, `{{ }}` placeholders resolve against that item's own fields merged over the outer session data — so an item shaped `{name: "Equities", percentage: 45}` is referenced as `{{name}}`/`{{percentage}}`, **not** `{{item.name}}`. If an array element isn't an object (a bare number or string), it's exposed as `{{value}}` instead. 3. **A bare string reference** — when the tool result is *already* shaped exactly like the target items (for example, a tool that already returns `{label, value, color}` objects for a chart), skip `template:` entirely: ```yaml theme={null} data: "{{tool_result.output.pre_shaped_points}}" ``` `template` is optional within a `from` binding — a binding with only `from` behaves like the bare-string form above, applied to the resolved array. `options` on a `select`-type action element (used inside `FORM.fields` and `ACTIONS`) is itself collection-bound the same way, so a select's options list can also be tool-driven. ### Carousels Carousels display a horizontal scrollable collection of cards, each with a title, subtitle, image, and action buttons. #### Syntax ```yaml expandable=true theme={null} FORMATS: CAROUSEL: - title: "Economy Class" subtitle: "$289 - UA 891" imageUrl: "https://cdn.example.com/economy.jpg" defaultActionUrl: "https://booking.example.com/ua891" buttons: - id: select_economy type: button label: "Select" value: "ua891_economy" - title: "Business Class" subtitle: "$1,249 - UA 891" imageUrl: "https://cdn.example.com/business.jpg" buttons: - id: select_business type: button label: "Select" value: "ua891_business" ``` #### Carousel card properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `title` | `string` | Yes | -- | Card title. | | `subtitle` | `string` | No | -- | Card subtitle or description. | | `imageUrl` | `string` | No | -- | URL for the card image. | | `defaultActionUrl` | `string` | No | -- | URL opened when the card itself is tapped. | | `buttons` | `array` | No | -- | Action buttons. See [Interactive actions](#interactive-actions). | `CAROUSEL` also accepts a `when` render guard, and `cards` supports [collection binding](#collection-binding) — resolve the cards from a tool result instead of writing them statically: ```yaml theme={null} FORMATS: CAROUSEL: when: hotel_results.length > 0 from: "{{search_result.output.hotels}}" template: title: "{{name}}" subtitle: "{{price_display}}" imageUrl: "{{photo_url}}" ``` ### Quick replies Quick-reply chips give the user a small set of one-tap responses. #### Syntax ```yaml theme={null} RESPOND: "Would you like to proceed?" FORMATS: QUICK_REPLIES: - id: yes label: "Yes, proceed" - id: no label: "No, cancel" ``` #### Quick reply properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `id` | `string` | Yes | -- | Value posted back when the chip is tapped. | | `label` | `string` | Yes | -- | Chip display text. | | `iconUrl` | `string` | No | -- | Optional icon shown on the chip. | `QUICK_REPLIES` itself supports [collection binding](#collection-binding) — the whole list can come from `from`/`template` — but, unlike the other structured templates, it has **no `when` guard**: the property is the raw collection, with no wrapping object to hang a render guard on. ### Lists A vertical list of items, each with an optional subtitle, image, and tap action. #### Syntax ```yaml theme={null} FORMATS: LIST: title: "Nearby Branches" items: - title: "Downtown Branch" subtitle: "0.4 miles away — open until 6 PM" imageUrl: "https://cdn.example.com/downtown.jpg" defaultActionUrl: "https://maps.example.com/downtown" - title: "Uptown Branch" subtitle: "1.2 miles away — open until 5 PM" ``` #### List properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `adaptive` | `boolean` | No | `false` | Let the reasoning agent narrow which items render. See [Template modifiers](#template-modifiers). | | `title` | `string` | No | -- | List heading. | | `items` | `array` | Yes | -- | List items. Supports [collection binding](#collection-binding). | #### List item properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `title` | `string` | Yes | -- | Item title. | | `subtitle` | `string` | No | -- | Item subtitle or description. | | `imageUrl` | `string` | No | -- | URL for the item's image. | | `defaultActionUrl` | `string` | No | -- | URL opened when the item itself is tapped. | Dynamic example — one list item per branch a tool returns: ```yaml theme={null} FORMATS: LIST: title: "Nearby Branches" items: from: "{{search_result.output.branches}}" template: title: "{{branch_name}}" subtitle: "{{distance_label}}" ``` ### Media (image, video, audio) `IMAGE`, `VIDEO`, and `AUDIO` share an identical shape — a single media item, not a collection. #### Syntax ```yaml theme={null} FORMATS: IMAGE: url: "https://cdn.example.com/receipt-12345.png" alt: "Payment receipt" caption: "Your receipt for confirmation #12345" ``` #### Media properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `url` | `string` | Yes | -- | Media URL. | | `alt` | `string` | No | -- | Accessibility text (also used as a text fallback where media can't render). | | `thumbnailUrl` | `string` | No | -- | Preview thumbnail, mainly for `VIDEO`/`AUDIO`. | | `caption` | `string` | No | -- | Caption shown alongside the media. | `IMAGE`/`VIDEO`/`AUDIO` are each a single object — there's no array, so [collection binding](#collection-binding) doesn't apply here. To show several images, use a `LIST` or `CAROUSEL` instead. ### Files A downloadable file attachment. #### Syntax ```yaml theme={null} FORMATS: FILE: url: "https://cdn.example.com/statements/2026-08.pdf" filename: "August_2026_Statement.pdf" sizeBytes: 184320 mimeType: "application/pdf" ``` #### File properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `url` | `string` | Yes | -- | File URL. | | `filename` | `string` | Yes | -- | Display filename. | | `sizeBytes` | `number` | No | -- | File size in bytes, shown to the user. | | `mimeType` | `string` | No | -- | MIME type, used to pick a file-type icon. | ### KPIs A single metric tile — a label, a value, and an optional trend indicator. #### Syntax ```yaml theme={null} FORMATS: KPI: label: "Account Balance" value: 12450.75 unit: "USD" trend: up ``` #### KPI properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `adaptive` | `boolean` | No | `false` | Let the reasoning agent select this KPI among other `adaptive` templates. See [Template modifiers](#template-modifiers). | | `label` | `string` | Yes | -- | Metric label. | | `value` | `string` \| `number` | Yes | -- | Metric value. | | `unit` | `string` | No | -- | Unit suffix (for example `"USD"`, `"%"`). | | `trend` | `"up"` \| `"down"` \| `"flat"` | No | -- | Trend indicator. | | `iconUrl` | `string` | No | -- | Icon shown on the tile. | `KPI` is a **single value**, not an array — there's no [collection binding](#collection-binding) here (that's what distinguishes it from `TABLE`/`LIST`/`CHART`). To show multiple KPIs, author multiple `KPI` blocks or use a `TABLE`. ### Tables Columns and rows for tabular data. #### Syntax ```yaml expandable=true theme={null} FORMATS: TABLE: columns: - key: date header: "Date" - key: description header: "Description" - key: amount header: "Amount" align: right rows: - date: "2026-09-01" description: "Direct deposit" amount: "$2,450.00" - date: "2026-09-03" description: "Grocery store" amount: "-$86.20" maxVisibleRows: 10 ``` #### Table properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `adaptive` | `boolean` | No | `false` | Let the reasoning agent narrow which rows/columns render. See [Template modifiers](#template-modifiers). | | `columns` | `array` | Yes | -- | Column definitions. Supports [collection binding](#collection-binding). | | `rows` | `array` | Yes | -- | Row data — each row an object keyed by column `key`. Supports [collection binding](#collection-binding), independently of `columns`. | | `maxVisibleRows` | `number` | No | -- | Rows shown before the client truncates/paginates. | #### Table column properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `key` | `string` | Yes | -- | Row property this column reads. | | `header` | `string` | Yes | -- | Column header text. | | `align` | `"left"` \| `"center"` \| `"right"` | No | `left` | Column text alignment. | Dynamic example — columns declared statically, rows bound to a tool result (the two bindings are independent, so it's common to declare `columns` statically and bind only `rows`): ```yaml expandable=true theme={null} FORMATS: TABLE: columns: - key: date header: "Date" - key: description header: "Description" - key: amount header: "Amount" align: right rows: from: "{{transactions_result.output.items}}" template: date: "{{posted_date}}" description: "{{merchant_name}}" amount: "{{formatted_amount}}" ``` ### Charts Pie, bar, or line charts, rendered as SVG on channels that support them. Currently, charts render on the Web SDK only. Color is optional — the SDK provides a default palette for dynamic charts. #### Syntax ```yaml expandable=true theme={null} RESPOND: "Here is the allocation:" FORMATS: CHART: type: pie title: "Portfolio Allocation" data: - label: "Equities" value: 45 color: "#4f46e5" - label: "Bonds" value: 25 color: "#06b6d4" - label: "Cash" value: 10 color: "#10b981" ``` #### Chart properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `type` | `"pie"` \| `"bar"` \| `"line"` | Yes | -- | Chart type. | | `title` | `string` | No | -- | Chart title. | | `data` | `array` | Yes | -- | Data points. Supports [collection binding](#collection-binding). | #### Chart data point properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `label` | `string` | Yes | -- | Data point label (category / x-axis value). | | `value` | `string` \| `number` | Yes | -- | Data point value. | | `color` | `string` | No | -- | Optional hex color for this data point/slice. | Dynamic example — one data point per array item a tool returns: ```yaml theme={null} RESPOND: "Portfolio for {{investor_id}}:" FORMATS: CHART: type: pie title: "Allocation — {{investor_id}}" data: from: "{{tool_result.output.sectors}}" template: label: "{{name}}" value: "{{percentage}}" ``` ### Interactive actions Interactive actions add buttons, select menus, and input fields to a response. Users interact with these elements, and the agent handles the interactions via `ON_ACTION` blocks. #### Syntax ```yaml expandable=true theme={null} RESPOND: "How would you like to proceed?" ACTIONS: - id: confirm_wire type: button label: "Confirm Wire" value: "confirmed" - id: modify_amount type: button label: "Modify Amount" value: "modify" - id: cancel type: button label: "Cancel" value: "cancelled" ``` #### Action element properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `id` | `string` | Yes | -- | Unique action identifier. Referenced in `ON_ACTION` handlers. | | `type` | `string` | Yes | -- | Element type: `button`, `select`, or `input`. | | `label` | `string` | Yes | -- | Display label. | | `value` | `string` | No | -- | Hidden value sent when the user interacts with this element. | | `url` | `string` | No | -- | For `button` type only: clicking opens this link instead of posting the value back to the agent. (Aliases: `link`, `web_url`.) | | `description` | `string` | No | -- | Subtitle or help text (for list items). | | `options` | `array` | No | -- | Options for `select` type. Each has `id`, `label`, `description`. | | `inputType` | `string` | No | -- | Input type for `input` elements: `text`, `number`, `date`, `time`, `email`. | | `placeholder` | `string` | No | -- | Placeholder text for `input` elements. | | `required` | `boolean` | No | -- | Whether the input is required before submission. | #### Select element example ```yaml theme={null} ACTIONS: - id: select_account type: select label: "Choose Account" options: - id: checking label: "Checking ****4521" description: "Available: $12,340.50" - id: savings label: "Savings ****8872" description: "Available: $45,200.00" ``` #### Input element example ```yaml expandable=true theme={null} ACTIONS: - id: amount_input type: input label: "Transfer Amount" inputType: number placeholder: "Enter amount" required: true - id: reference_input type: input label: "Reference (optional)" inputType: text placeholder: "Invoice number, memo, etc." required: false submitLabel: "Submit" submitId: "submit_transfer" ``` #### Form submission When the `ACTIONS` block contains `input` elements, you can specify a `submitLabel` and `submitId` for the form submission button: | Property | Type | Required | Default | Description | | - | - | - | - | - | | `submitLabel` | `string` | No | -- | Label for the form submit button. | | `submitId` | `string` | No | -- | Action ID emitted when the form is submitted. | #### ON\_ACTION handlers Handle user interactions with action elements in flow steps: ```yaml expandable=true theme={null} FLOW: review_wire: RESPOND: "Ready to proceed?" ACTIONS: - id: confirm type: button label: "Confirm" - id: cancel type: button label: "Cancel" ON_ACTION: confirm: DO: - SET: user_confirmed = true - RESPOND: "Executing your wire transfer." FORMATS: MARKDOWN: "**Executing your wire transfer.**" - GOTO: execute_wire_step cancel: RESPOND: "Wire transfer cancelled." GOTO: cleanup ``` The `ON_ACTION` handler properties are described below. | Property | Type | Required | Default | Description | | - | - | - | - | - | | `ACTION_ID` | `string` | Yes | -- | Which action element triggered this handler. | | `CONDITION` | `string` | No | -- | Optional condition based on the action value. | | `DO` | ordered action list | No | -- | Preferred form when order or nested rich `FORMATS:` matters. | | `RESPOND` | `string` | No | -- | Response message; supports nested `FORMATS:` inside a `DO` action. | | `SET` | `Record` | No | -- | Variable assignments. | | `CLEAR` | `string[]` | No | -- | Session variables to clear. | | `CALL` | `string` | No | -- | Tool call; use `AS result_key` to store the result. | | `GOTO` / `TRANSITION` / `THEN` | `string` | No | -- | Flow step to transition to. | | `HANDOFF` | `string` | No | -- | Declared handoff target agent. | | `DELEGATE` | `string` | No | -- | Declared delegate target agent; supports nested `RETURN` and `ON_RETURN`. | | `COMPLETE` | `boolean` | No | `true` | Complete the current flow. | If a handler `RESPOND` includes rich `FORMATS:` before terminal routing, that payload is forwarded as the fallback final channel payload. The terminal target's own rich payload takes precedence. #### Reusable handlers (`ACTION_HANDLERS:`) `ON_ACTION` inside a flow step handles actions for that step only. To define **reusable** action handlers available across the whole agent, declare a top-level `ACTION_HANDLERS:` section. Each entry is keyed by the action element `id` and uses the same grammar as `ON_ACTION` (an optional `CONDITION`, shorthand `RESPOND`/`SET`/`TRANSITION`, or a canonical `DO:` block of ordered actions — `SET`, `CLEAR`, `LOG`, `RESPOND`, `CALL`, `GOTO`/`TRANSITION`/`THEN`, `HANDOFF`, `DELEGATE`, `COMPLETE`). ```yaml expandable=true theme={null} ACTION_HANDLERS: open_summary: DO: - SET: selected_action = "open_summary" - RESPOND: TEMPLATE(account_summary) - TRANSITION: display_summary contact_support: CONDITION: user.tier == "premium" DO: - RESPOND: "Connecting you to premium support." - HANDOFF: Support_Agent ``` ### Forms A form groups several [action elements](#action-element-properties) — typically `input` fields, plus a submit control — into one submission. #### Syntax ```yaml expandable=true theme={null} RESPOND: "Let's set up your transfer." FORMATS: FORM: title: "Transfer Details" fields: - id: amount_input type: input label: "Amount" inputType: number required: true - id: memo_input type: input label: "Memo (optional)" inputType: text required: false submitLabel: "Continue" ``` #### Form properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `title` | `string` | No | -- | Form heading. | | `fields` | `array` | Yes | -- | Form fields — the same shape as [action elements](#action-element-properties) (`id`, `type`, `label`, and the rest). Supports [collection binding](#collection-binding). | | `submitLabel` | `string` | No | -- | Label for the form's submit button. | `FORM` doesn't have its own `submitId` — submission is handled the same way as any other `ACTIONS` form submission; see [Form submission](#form-submission) and [ON\_ACTION handlers](#on_action-handlers). ### Progress A progress bar or circle for a single value against a maximum. #### Syntax ```yaml theme={null} FORMATS: PROGRESS: label: "Document verification" value: 2 max: 3 variant: bar ``` #### Progress properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `when` | `string` | No | -- | Conditional render guard. See [Template modifiers](#template-modifiers). | | `label` | `string` | No | -- | Label shown with the progress indicator. | | `value` | `number` | Yes | -- | Current progress value. | | `max` | `number` | No | -- | Maximum value the progress is measured against. | | `variant` | `"bar"` \| `"circle"` | No | `bar` | Visual style. | `PROGRESS` is a single value, not an array — there's no [collection binding](#collection-binding) here. ### FEEDBACK surveys A `FEEDBACK` block renders a rating/feedback card. Author it inside a `RICH_CONTENT:` (or `FORMATS:`) block. **Inline feedback is deprecated** (`DEPRECATED_INLINE_FEEDBACK`). A `FEEDBACK` block without a `FEEDBACK_TEMPLATE` still compiles, but the compiler warns: *"Inline feedback is deprecated and will be removed in a future version. Define a survey in the project Surveys area and reference it with `FEEDBACK_TEMPLATE`."* Prefer referencing a project survey template. #### Referencing a survey template Set `FEEDBACK_TEMPLATE` to a project survey name. The `prompt` and `type` (the survey's **measure**) then come from the template, so they become optional here; any copy fields you do set act as **per-string overrides** (useful for translation): ```yaml theme={null} RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: Post_Chat_NPS prompt: "How likely are you to recommend us?" # optional copy override ``` When a template is named you may **not** override the measure — `type`, `max`, `scoring`, `range`, and `comment_threshold` are owned by the template, and setting them alongside a template is a compile error. Naming a template that doesn't exist in the project is an error (`UNKNOWN_SURVEY_TEMPLATE`). `expires_after` is the one exception to that rule — a numeric answer-window TTL (in seconds) that **is** overridable per-agent even when a template is referenced: ```yaml theme={null} RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: Post_Chat_NPS prompt: "How likely are you to recommend us?" expires_after: 600 # widen the answer window to 10 minutes for this agent ``` The effective window is the agent's `expires_after` if set, else the template's own TTL, else a 300-second platform default — clamped to 30–86400 seconds either way. A survey presented to the customer can only be answered within this window; a submission after it expires is rejected. #### Inline FEEDBACK (deprecated) Without a template, `prompt` and `type` are required: | Field | Type | Description | | - | - | - | | `prompt` | `string` | The question (aliases: `question`, `label`). | | `type` | `"thumbs"` \| `"star"` \| `"scale"` | Rating measure. `stars` is an accepted alias for `star`. | | `max` | `number` | Maximum rating value (for `star`/`scale`). | | `submit_label` | `string` | Submit-button label. | | `pending_message` / `success_message` / `error_message` | `string` | Status copy. | | `comment_prompt` / `comment_placeholder` | `string` | Free-text comment field copy. | | `comment_threshold` | `number` | Rating at/below which the comment field is requested. | | `when` | `string` | Conditional render guard (expression). | #### Condition-based wording (`_rules`) To vary wording by condition — the second way to translate a survey, kept next to the source copy — add a `_rules` list. Each rule has a `when` expression and the field's replacement text (the field key, or the alias `value`). Rules are ordered, first match wins: ```yaml theme={null} FEEDBACK: FEEDBACK_TEMPLATE: Post_Chat_NPS prompt: "How likely are you to recommend us?" prompt_rules: - when: "language == 'es'" prompt: "¿Nos recomendaría?" # or: value: "¿Nos recomendaría?" comment_placeholder_rules: - when: "language == 'es'" value: "Cuéntenos más..." ``` Only **wording** fields accept rules: `prompt`, `submit_label`, `pending_message`, `success_message`, `error_message`, `comment_prompt`, `comment_placeholder`. There is no `type_rules`/`max_rules` — the measure is never rule-selectable. Copy rules are evaluated **before the customer answers**, so a `when` expression may reference `max`, `language`, `channel`, and `session` (as `session.`) but **not** `rating`. An invalid condition is a compile error (`INVALID_SURVEY_CONDITION`). #### Post-transfer surveys (ESCALATE to a human agent) When a human agent closes a conversation the bot [escalated](/agent-platform/abl-reference/multi-agent-and-supervisor#escalate), the survey card shown on close is resolved from a `FEEDBACK` block — but that card renders with no bot turn running, so it can't come from "whichever step the conversation happened to be on." The platform instead picks **one** `FEEDBACK` block to stand for the whole agent: 1. **The agent-level block**, if the agent declares one directly under its top-level `RICH_CONTENT:` (outside any `FLOW:` step) — this always wins when present. 2. **Otherwise, a FLOW step's own `FEEDBACK` block.** Both placements compile to the same thing: `FEEDBACK` nested under a step's own `RICH_CONTENT:`, or under that step's `RESPOND:`. The platform scans steps in `FLOW:` order: * **Exactly one step** has a `FEEDBACK` block → that one is used. * **Several steps** have a `FEEDBACK` block, naming the **same** `FEEDBACK_TEMPLATE` → treated as the same survey; the first step's copy/wording wins. * **Several steps** have `FEEDBACK` blocks naming **different** templates, or any of them is an inline (template-less) block → ambiguous. No post-transfer survey is shown at all, rather than guessing which one you meant. ```yaml theme={null} FLOW: collect_card: REASONING: false RESPOND: "Let me get that taken care of for you." RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: Post_Transfer_CSAT THEN: transfer_to_agent ``` A `FEEDBACK` block's `when` guard is **not** evaluated for this purpose — closing the conversation isn't a turn, and `when` conditions over turn-scoped values (like `input`) would never hold outside one. A block nested deeper than the step itself — inside an `ON_ACTION` handler, a `GATHER` field, a digression, or a reasoning zone — is never a candidate; only the step's own `RICH_CONTENT:`/`RESPOND:`-level block counts. The transfer record stays pending after the human agent finishes and only closes once the survey is answered or its answer window ([`expires_after`](#referencing-a-survey-template)) runs out — whichever happens first. The resulting rating is stored with the survey template, the escalating agent's name, and its version, so it appears correctly scoped in that survey's Feedback Insights. ### Templates Templates are named, reusable response definitions declared in the `TEMPLATES:` block. They support `{{}}` interpolation and multi-format variants. #### Syntax ```yaml expandable=true theme={null} TEMPLATES: wire_confirmation: DEFAULT: | **Wire Confirmation -- {{confirmation_number}}** From: ****{{source_last4}} | To: {{beneficiary_name}} Amount: {{amount}} {{currency}} | Fees: {{fees}} | Total: {{total_debit}} Status: {{status}} | ETA: {{estimated_arrival}} MARKDOWN: | ## Wire Transfer Confirmation | Field | Value | |-------|-------| | Confirmation | {{confirmation_number}} | | Amount | {{amount}} {{currency}} | | Status | {{status}} | fee_breakdown: DEFAULT: | **Fee Breakdown for {{transfer_type}} wire** Wire fee: {{wire_fee}} {{currency}} {{#if intermediary_fee}}Intermediary fee: {{intermediary_fee}} {{currency}}{{/if}} **Total fees: {{total_fees}} {{currency}}** ``` #### Template properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `name` | `string` | Yes | -- | Template name (the YAML key). Referenced via `TEMPLATE(name)`. | | `DEFAULT` | `string` | Yes | -- | Default template body with `{{}}` interpolation. | | `MARKDOWN` | `string` | No | -- | Markdown variant. | | `ADAPTIVE_CARD` | `string` | No | -- | Adaptive Card JSON variant. | | `ACTIONS` | `object` | No | -- | Interactive actions attached to this template. | | `VOICE` | `object` | No | -- | Voice (TTS) overrides for this template. Same shape as [Voice configuration](#voice-configuration). | | `RENDERABLES` | `object[]` | No | -- | Customer-owned structured payload contracts (`name`, `payload`, `targets`, `fallbackText`, `schemaRef`). | #### Referencing templates Use `TEMPLATE(name)` in any RESPOND value: ```yaml theme={null} RESPOND: TEMPLATE(wire_confirmation) ``` #### Template interpolation Template bodies use the same `{{variable_name}}` interpolation and `{{#if variable}}...{{/if}}` conditional-section syntax as any other ABL string — see [Template strings](/agent-platform/abl-reference/expressions-and-functions#template-strings) in Expressions and functions for the full syntax, including calling built-in functions inside `{{}}`. ```yaml theme={null} DEFAULT: | Hello, {{customer_name}}. {{#if has_loyalty}}Your loyalty tier: {{loyalty_tier}}.{{/if}} How can I help you today? ``` ### Channel selection The runtime selects the response format based on the delivery channel. | Channel | Priority format | Fallback | | - | - | - | | Web/SDK | `RICH_CONTENT` (Markdown, HTML, AG-UI) | Plain text | | Mobile | `RICH_CONTENT` (Adaptive Card, Carousel) | Plain text | | Voice | `VOICE` (SSML, instructions, plain\_text) | RESPOND text | | Slack | `RICH_CONTENT` (SLACK) | Markdown | | WhatsApp | `RICH_CONTENT` (WHATSAPP) | Plain text | If the preferred format isn't available, the runtime falls back through the priority chain until it finds a defined format. Plain text (`RESPOND`) is always the final fallback. *** **Related articles:** * [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions) -- operators, built-in functions, `{{}}` template-string syntax, and type coercion rules * [Lifecycle and hooks](/agent-platform/abl-reference/lifecycle-and-hooks) -- ON\_START and hooks that support rich content * [Multi-Agent and supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor) -- COMPLETE conditions with rich responses * [Data Types and utilities](/agent-platform/abl-reference/data-types-and-utilities) -- types used in rich content and template fields # ABL programming and scripting constructs Source: https://koreai.mintlify.app/agent-platform/abl/reference/scripting-constructs ABL supports two complementary execution styles: * **Reasoning mode**: The agent has no `FLOW:` section, and the runtime decides how to pursue the goal. * **Scripted flow mode**: The agent has a `FLOW:` section and follows authored steps, actions, guards, and transitions. Use scripted flows when ordering, auditability, predictable cost, or strict business rules matter. Typical examples include payments, identity verification, regulated disclosures, onboarding forms, and support procedures with fixed escalation rules. This guide treats ABL as a programming and logic language: variables, expressions, validation, branching, state reset, collection processing, tool calls, retries, and terminal paths. Agent coordination is an integration boundary rather than the focus of this page — see [Multi-Agent and Supervisor](/agent-platform/abl-reference/multi-agent-and-supervisor) for `DELEGATE`, `HANDOFF`, and `ESCALATE`. Every flow step must declare `REASONING: true` or `REASONING: false`. *** ## Minimal scripted flow example ```yaml theme={null} AGENT: Account_Support GOAL: "Help the customer resolve an account request." FLOW: start: REASONING: false RESPOND: "I can help with your account." THEN: collect_account collect_account: REASONING: false GATHER: - account_id: required THEN: lookup_account lookup_account: REASONING: false CALL: get_account WITH: account_id: account_id AS: accountResult THEN: show_account show_account: REASONING: false RESPOND: "Your account is {{accountResult.status}}." THEN: COMPLETE ``` *** ## Flow structure and execution ### FLOW `FLOW:` defines the scripted state machine. Steps are named blocks. A step can transition to any other step, which allows linear flows, branches, retries, loops, and terminal paths. Use it for: * Fixed process ordering. * Deterministic tool execution. * Compliance or audit requirements. * High-volume workflows where unnecessary LLM turns should be avoided. ```yaml theme={null} FLOW: start -> collect -> validate -> complete ``` The arrow notation communicates a sequence. The named step blocks contain the actual behavior. Learn more: [Flow structure](/agent-platform/abl-reference/flow#flow-structure). ### REASONING `REASONING` controls whether a step is deterministic or LLM-assisted: ```yaml theme={null} FLOW: deterministic_check: REASONING: false CHECK: amount > 0 THEN: process interpret_request: REASONING: true GOAL: "Determine which supported request the customer is making." EXIT_WHEN: request_type != null THEN: route_request ``` Use `false` for explicit actions and boolean conditions. Use `true` when the step needs flexible interpretation, tool selection, or reasoning that can't be represented as deterministic expressions. Reasoning-step controls include `GOAL`, `AVAILABLE_TOOLS`, `EXIT_WHEN`, `MAX_TURNS`, and `STEP_CONSTRAINTS`. Learn more: [Per-step REASONING toggle](/agent-platform/abl-reference/flow#per-step-reasoning-toggle). ### WHEN `WHEN` is an entry guard. The step runs only when its condition is true. ```yaml theme={null} international_details: REASONING: false WHEN: transfer_type == "international" GATHER: - swift_code: required THEN: validate_transfer ``` Learn more: [Entry guards](/agent-platform/abl-reference/flow#entry-guards). ### MAX\_ATTEMPTS and ON\_EXHAUSTED `MAX_ATTEMPTS` limits retries for a step. `ON_EXHAUSTED` routes to a recovery step once the limit is reached. ```yaml theme={null} verify_pin: REASONING: false MAX_ATTEMPTS: 3 ON_EXHAUSTED: lock_account GATHER: - pin: required THEN: check_pin ``` Learn more: [Attempt limiting](/agent-platform/abl-reference/flow#attempt-limiting). ### THEN `THEN` is the normal next transition. ```yaml theme={null} complete_profile: REASONING: false SET: profile_complete = true THEN: confirmation ``` `THEN` can point to a named step or a terminal action such as `COMPLETE`. Learn more: [THEN / ON\_FAIL](/agent-platform/abl-reference/flow#then-%2F-on_fail). *** ## User interaction constructs ### RESPOND `RESPOND` sends text to the user. Responses support `{{variable}}` interpolation and multiline pipe blocks. The FLOW reference sometimes labels this concept "SAY / RESPOND" — `RESPOND:` is the canonical flow action used in ABL examples. ```yaml theme={null} summary: REASONING: false RESPOND: | Please confirm: Amount: {{amount}} Destination: {{beneficiary_name}} THEN: confirm ``` Use it for instructions, confirmations, validation messages, receipts, and error recovery. Learn more: [SAY / RESPOND](/agent-platform/abl-reference/flow#say-%2F-respond). ### PRESENT `PRESENT` shows context before a `GATHER` operation. ```yaml theme={null} review: REASONING: false PRESENT: | Name: {{beneficiary_name}} Account: {{MASK(beneficiary_account, "last4")}} Amount: {{FORMAT_CURRENCY(amount, "USD")}} GATHER: - confirmation: required THEN: confirm ``` Learn more: [PRESENT](/agent-platform/abl-reference/flow#present). ### GATHER `GATHER` collects one or more fields from the user. It can specify type, required status, prompt, defaults, validation, extraction strategy, and correction behavior. ```yaml theme={null} collect_booking: REASONING: false GATHER: FIELDS: - destination: required - checkin: TYPE: date REQUIRED: true PROMPT: "When would you like to check in?" - guests: TYPE: number DEFAULT: 2 STRATEGY: hybrid CORRECTIONS: true COMPLETE_WHEN: destination AND checkin THEN: search ``` Use field validation for intrinsic values such as date format or account-number shape. Use `CHECK` or `CONSTRAINTS` for cross-field rules. Learn more: [GATHER](/agent-platform/abl-reference/gather) for the full field-type and validation reference, or [GATHER in flow steps](/agent-platform/abl-reference/flow#gather-in-flow-steps) for flow-specific syntax. *** ## State and data constructs ### SET `SET` assigns values to session variables. The right-hand side can be a literal, variable, dotted result path, or built-in expression. ```yaml theme={null} initialize: REASONING: false SET: status = "pending" retry_count = 0 request_id = UNIQUE_ID(12) created_at = NOW() THEN: collect_details ``` Inline assignment is useful inside branches: ```yaml theme={null} ON_INPUT: - IF: input == "express" SET: shipping_method = "express" THEN: calculate_shipping ``` Use `SET` for normalization, derived values, flags, counters, tool-result extraction, and workflow IDs. Learn more: [SET](/agent-platform/abl-reference/flow#set). ### CLEAR `CLEAR` removes variables from session state. This is useful when the user changes an earlier choice and dependent values must be recollected. ```yaml theme={null} change_destination: REASONING: false CLEAR: [destination, hotel_id, room_id, booking_total] RESPOND: "Let's start with the new destination." THEN: collect_destination ``` Clear all dependent values, not just the field the user changed. Learn more: [CLEAR](/agent-platform/abl-reference/flow#clear). ### TRANSFORM `TRANSFORM` applies a declarative array pipeline. The stages are `FILTER`, `MAP`, `SORT_BY`, and `LIMIT`. ```yaml theme={null} prepare_results: REASONING: false TRANSFORM: searchResult.hotels AS hotel INTO visible_hotels FILTER: hotel.rating >= 4 MAP: name: hotel.name price: FORMAT_CURRENCY(hotel.price_per_night, "USD") rating: hotel.rating SORT_BY: rating DESC LIMIT: 5 THEN: show_results ``` Use `TRANSFORM` for search results, transaction lists, catalog filtering, reporting data, and safe presentation shaping. It avoids embedding collection-processing logic in prompts. Learn more: [TRANSFORM](/agent-platform/abl-reference/flow#transform). *** ## Tool execution constructs ### CALL `CALL` invokes a registered tool. Keep one tool call per step when sequencing matters, and chain calls through `THEN`. ```yaml theme={null} CALL: get_balance WITH: account_id: account_id AS: balanceResult ``` `WITH` maps tool parameters to literals, variables, dotted paths, or expressions. `AS` stores the result for later use. Learn more: [Tools](/agent-platform/abl-reference/tools) for tool declaration and binding types. ### ON\_SUCCESS, ON\_FAILURE, and ON\_FAIL `ON_SUCCESS` and `ON_FAILURE` handle the two basic outcomes of a tool call. ```yaml theme={null} submit_request: REASONING: false CALL: create_ticket WITH: subject: subject description: description AS: ticketResult ON_SUCCESS: RESPOND: "Ticket {{ticketResult.id}} was created." THEN: complete ON_FAIL: RESPOND: "I could not create the ticket." THEN: retry_or_escalate ``` Use `ON_SUCCESS` and `ON_FAILURE` for structured success/failure handling around a `CALL`. Use `ON_RESULT` when the tool returns several business outcomes that require different paths. Use `ON_FAIL` for `CHECK` failure routing and other documented failure fallbacks. `ON_FAILURE` is also used by handoff configuration, so its meaning depends on where it appears. Learn more: [ON\_SUCCESS / ON\_FAIL](/agent-platform/abl-reference/flow#on_success-%2F-on_fail). ### ON\_RESULT `ON_RESULT` branches on fields in a tool result or deterministic flow context. Branches are evaluated in author order; the first matching branch wins. ```yaml theme={null} validate_recipient: REASONING: false CALL: verify_recipient WITH: routing_number: routing_number account_number: account_number AS: recipientResult ON_RESULT: - IF: recipientResult.status == "valid" SET: recipient_name = recipientResult.account_holder THEN: collect_amount - IF: recipientResult.status == "account_closed" RESPOND: "That account is closed. Please provide another account." THEN: collect_recipient - ELSE: RESPOND: "The recipient details could not be verified." THEN: collect_recipient ``` Learn more: [ON\_RESULT](/agent-platform/abl-reference/flow#on_result). ### CHECK `CHECK` evaluates a deterministic boolean guard after state has been gathered or a tool has returned. ```yaml theme={null} check_limits: REASONING: false CHECK: amount <= balanceResult.available AND amount <= daily_limit ON_FAIL: over_limit THEN: confirm_transfer ``` Use `CHECK` for local step prerequisites. If `CHECK` is false and no `ON_FAIL` target is provided, the step halts rather than automatically choosing a recovery path. Use `CONSTRAINTS` for reusable business rules that apply across multiple steps or boundaries. Learn more: [CHECK](/agent-platform/abl-reference/flow#check). ### CONSTRAINTS `CONSTRAINTS` declares reusable business invariants outside an individual step. Constraints are flattened and evaluated in declaration order on each turn; labels organize them but don't control execution order. A constraint can `REQUIRE`, `WARN`, `LIMIT`, or `RESTRICT` a condition, optionally scoped with `WHEN`, and define `ON_FAIL` behavior. ```yaml theme={null} CONSTRAINTS: transfer_rules: - REQUIRE account_id != null ON_FAIL: "I need to verify your account before continuing." - LIMIT amount <= daily_limit WHEN: amount != null ON_FAIL: ESCALATE ``` Use constraints for rules that must remain true across several paths or at a tool/result boundary. Use `CHECK` when the rule is local to one flow step. `LIMIT` and `RESTRICT` remain distinct constraint kinds, but initially share the runtime handling path. If a failed constraint must clear state before retrying, add a `CLEAR:` list to the `ON_FAIL` block (or route to a dedicated step that performs `CLEAR`) — a plain string or `ESCALATE` `ON_FAIL` does not clear variables on its own. See [Memory and Constraints](/agent-platform/abl-reference/memory-and-constraints#constraints) for the full constraint syntax, severity levels, and evaluation order. *** ## Branching and navigation ### ON\_INPUT `ON_INPUT` routes based on the current user input using deterministic boolean expressions. ```yaml theme={null} confirm: REASONING: false RESPOND: "Would you like me to submit this request?" ON_INPUT: - IF: input == "yes" OR input == "confirm" THEN: submit - IF: input == "no" OR input == "cancel" THEN: cancelled - ELSE: RESPOND: "Please answer yes or no." THEN: confirm ``` Use `ON_INPUT` for explicit, reproducible choices. Don't use it to make an LLM semantic judgment — use `DIGRESSIONS` or a reasoning step for that. Learn more: [ON\_INPUT](/agent-platform/abl-reference/flow#on_input). ### IF and ELSE `IF` defines a branch condition. An omitted condition represents `ELSE`. Both appear in `ON_INPUT` and `ON_RESULT` branches. ### GOTO `GOTO` jumps directly to a named step. It's useful for cancellation, correction, and recovery paths. ```yaml theme={null} cancelled: REASONING: false CLEAR: [draft_request, confirmation] RESPOND: "Your request has been cancelled." THEN: COMPLETE ``` ### DIGRESSIONS `DIGRESSIONS` handle intent-based escapes from the current step. Unlike `ON_INPUT`, digressions are for semantic intents such as help, cancellation, or an urgent request. ```yaml theme={null} collect_details: REASONING: true GATHER: - account_id: required DIGRESSIONS: - INTENT: "help" DO: - RESPOND: "I need your account number to continue." - RESUME - INTENT: "cancel" DO: - RESPOND: "I cancelled the request." - GOTO: cancelled ``` `global_digressions` can be declared under `FLOW` for behavior available in every step. A digression can `RESPOND`, `SET`, `CLEAR`, `CALL`, `DELEGATE`, `HANDOFF`, `GOTO`, or `RESUME`. Learn more: [Digressions](/agent-platform/abl-reference/flow#digressions) and [Global digressions](/agent-platform/abl-reference/flow#global-digressions). ### SUB\_INTENTS `SUB_INTENTS` handle step-local intents without leaving the step. ```yaml theme={null} collect_address: REASONING: false GATHER: - address: required SUB_INTENTS: - INTENT: "change address" CLEAR: [address, delivery_quote] RESPOND: "Please provide the new address." - INTENT: "why do you need this" RESPOND: "We use it to calculate delivery eligibility." ``` Learn more: [Sub-intents](/agent-platform/abl-reference/flow#sub-intents). ### COMPLETE\_WHEN `COMPLETE_WHEN` defines when a collection step is complete. This is especially useful when several fields may be extracted from one user message. ```yaml theme={null} collect_profile: REASONING: false GATHER: FIELDS: - name: required - email: required - phone: optional COMPLETE_WHEN: name AND email THEN: validate_profile ``` `COMPLETE_WHEN` controls collection completion. It isn't a replacement for a final business-rule `CHECK` or `CONSTRAINT`. Learn more: [COMPLETE\_WHEN](/agent-platform/abl-reference/flow#complete_when). *** ## Programming and logic patterns ### Re-entry as looping ABL doesn't use a general-purpose `WHILE` or `FOR` statement in an authored flow. A loop is expressed by transitioning back to a named step with `THEN` or `GOTO`. State variables provide the loop condition; `MAX_ATTEMPTS` bounds execution of a step, but it isn't a general-purpose loop predicate. ```yaml theme={null} collect_code: REASONING: false MAX_ATTEMPTS: 3 ON_EXHAUSTED: lock_account GATHER: - verification_code: required THEN: validate_code validate_code: REASONING: false CALL: verify_code WITH: code: verification_code AS: codeResult ON_RESULT: - IF: codeResult.valid == true THEN: continue_flow - ELSE: RESPOND: "That code is not valid. Please try again." THEN: collect_code ``` ### Guarded state transitions Use `WHEN`, `CHECK`, `ON_INPUT`, and `ON_RESULT` as the equivalent of guarded statements. Keep predicates side-effect-free and put mutations in `SET` or `CLEAR` actions. ```yaml theme={null} approve: REASONING: false WHEN: request_status == "ready" CHECK: reviewer_id != null AND amount <= approval_limit ON_FAIL: approval_required THEN: submit ``` ### Counters and bounded retries Use `SET` to maintain counters and `MAX_ATTEMPTS` to enforce a hard bound. ```yaml theme={null} retryable_call: REASONING: false SET: attempt_count = ADD(COALESCE(attempt_count, 0), 1) CALL: submit_request AS: result ON_FAILURE: THEN: retry_or_escalate THEN: complete retry_or_escalate: REASONING: false CHECK: attempt_count < 3 ON_FAIL: escalate THEN: retryable_call ``` ### Terminal paths `COMPLETE` ends the flow. `ESCALATE` ends the scripted path by routing to a human. Both should be explicit for success, cancellation, validation failure, and unrecoverable error paths. ```yaml theme={null} complete: REASONING: false RESPOND: "Your request is complete." THEN: COMPLETE ``` Learn more: [COMPLETE](/agent-platform/abl-reference/multi-agent-and-supervisor#complete) and [ESCALATE](/agent-platform/abl-reference/multi-agent-and-supervisor#escalate). ### ON\_ERROR `ON_ERROR` handles named runtime or tool errors at the agent level or, with a `TYPE` entry, at an individual flow step. Use it for centralized recovery, retry, response, or escalation policy. Use `ON_FAILURE` for structured call failure handling and `ON_FAIL` for guard/fallback failure routing. ```yaml theme={null} ON_ERROR: tool_error: RESPOND: "The service is taking too long. I will try once more." SUBTYPE: tool_timeout RETRY: 1 THEN: RETRY_STEP validation_error: RESPOND: "The service could not complete the request." RETRY: 0 THEN: ESCALATE with REASON: "Tool failure after scripted recovery" ``` For escalation from an error handler, use `THEN: ESCALATE with REASON: "..."`. Don't use an unsupported nested escalation-priority form inside `ON_ERROR`. `THEN` in an `ON_ERROR` handler takes only one of a fixed set of actions (`CONTINUE`, `COMPLETE`, `ESCALATE`, `RETRY_STEP`, `HANDOFF`, `BACKTRACK`) — not a step name. The handler's own key is likewise matched against a fixed canonical set of error types (`default`, `llm_error`, `tool_error`, `routing_error`, `validation_error`, `timeout_error`, `unknown_error`); older names such as `tool_timeout`, `invalid_input`, and `api_error` are not valid types and never match — see [Error types](/agent-platform/abl-reference/lifecycle-and-hooks#error-types). Step-level handlers can additionally narrow an error with `SUBTYPE`, add retry delay/backoff, or route validation failures back to a collection step: ```yaml theme={null} search: REASONING: false CALL: search_catalog ON_ERROR: - TYPE: tool_error SUBTYPE: timeout RETRY: 2 RETRY_DELAY: 3000 RETRY_BACKOFF: exponential THEN: ESCALATE with REASON: "Catalog search timed out" ``` The runtime's flow order is significant: response, gather, call, check, result/failure handling, input handling, then the default transition. Don't assume a later action runs after a branch has already transitioned. Top-level `COMPLETE` rules can also define conditional completion responses. Interactive `ACTIONS` and `ON_ACTION` can be used as a typed input alternative to free-form `ON_INPUT` branching when the valid choices are known. Learn more: [ON\_ERROR handlers](/agent-platform/abl-reference/lifecycle-and-hooks#on_error-handlers) for agent-level errors, or [step-level error handling](/agent-platform/abl-reference/flow#step-level-error-handling) for the `TYPE`/`SUBTYPE` form shown above. *** ## Built-in expression functions Expressions can be used in `SET`, `CHECK`, `WHEN`, `ON_INPUT`, `ON_RESULT`, `FILTER`, `MAP`, and templates. Boolean expressions support comparisons and `AND`/`OR`/`NOT`. Keep them side-effect-free. | Category | Functions | | - | - | | Math | `ADD`, `SUB`, `MUL`, `DIV`, `ROUND`, `ABS`, `MIN`, `MAX` | | String | `UPPER`, `LOWER`, `TRIM`, `SUBSTRING`, `REPLACE`, `SPLIT`, `JOIN`, `PAD_START`, `PAD_END`, `REPEAT` | | Formatting | `MASK`, `FORMAT_CURRENCY`, `FORMAT_DATE`, `ORDINAL` | | Type | `IS_ARRAY`, `IS_NUMBER`, `IS_STRING`, `TO_NUMBER`, `TO_STRING` | | Array | `LENGTH`, `ARRAY_FIND`, `ARRAY_FIND_INDEX` | | Object | `OBJECT_KEYS`, `OBJECT_VALUES`, `OBJECT_MERGE` | | Utility | `COALESCE`, `NOW`, `UNIQUE_ID` | Example normalization: ```yaml theme={null} SET: normalized_phone = REPLACE(TRIM(phone), "-", "") display_total = FORMAT_CURRENCY(total, "USD") safe_account = MASK(account_number, "last4") ``` Guard nullable results before arithmetic or field access. `TO_NUMBER` can return `null`, and `DIV` returns `null` on division by zero — use `COALESCE`, `CHECK`, or a result branch before continuing. See [Expressions and functions](/agent-platform/abl-reference/expressions-and-functions) for the complete function reference, operator precedence, and type coercion rules. *** ## End-to-end example: funds transfer The following example combines collection, normalization, tool calls, checks, result branching, correction, cancellation, and completion. ```yaml expandable=true theme={null} AGENT: Funds_Transfer GOAL: "Safely collect, validate, and submit a customer funds transfer." TOOLS: verify_recipient(routing_number: string, account_number: string) -> {status: string, account_holder: string} get_balance(account_id: string) -> {available: number} create_transfer(account_id: string, recipient_account: string, amount: number) -> {status: string, transfer_id: string, error: string} FLOW: global_digressions: - INTENT: "cancel" DO: - RESPOND: "The transfer has been cancelled." - CLEAR: [amount, recipient_account, transferResult] - GOTO: cancelled start: REASONING: false SET: status = "collecting" request_id = UNIQUE_ID(12) THEN: collect_recipient collect_recipient: REASONING: false GATHER: FIELDS: - routing_number: required - account_number: required CORRECTIONS: true THEN: verify_recipient verify_recipient: REASONING: false CALL: verify_recipient WITH: routing_number: routing_number account_number: account_number AS: recipientResult ON_RESULT: - IF: recipientResult.status == "valid" SET: recipient_name = recipientResult.account_holder recipient_account = account_number THEN: collect_amount - IF: recipientResult.status == "account_closed" RESPOND: "That account is closed. Please provide another account." CLEAR: [account_number, recipient_account] THEN: collect_recipient - ELSE: RESPOND: "I could not verify those details. Please check them." THEN: collect_recipient collect_amount: REASONING: false PRESENT: "Transferring to {{recipient_name}}." GATHER: - raw_amount: TYPE: string REQUIRED: true SET: amount = TO_NUMBER(REPLACE(TRIM(raw_amount), "$", "")) CHECK: amount > 0 ON_FAIL: invalid_amount THEN: load_balance load_balance: REASONING: false CALL: get_balance WITH: account_id: account_id AS: balanceResult CHECK: amount <= balanceResult.available ON_FAIL: insufficient_funds THEN: confirm confirm: REASONING: false RESPOND: | Transfer {{FORMAT_CURRENCY(amount, "USD")}} to {{recipient_name}}? Reply yes to submit or no to cancel. ON_INPUT: - IF: input == "yes" OR input == "confirm" THEN: submit - IF: input == "no" OR input == "cancel" THEN: cancelled - IF: input == "change" CLEAR: [amount, raw_amount] THEN: collect_amount - ELSE: RESPOND: "Please reply yes, no, or change." THEN: confirm submit: REASONING: false CALL: create_transfer WITH: account_id: account_id recipient_account: recipient_account amount: amount AS: transferResult ON_RESULT: - IF: transferResult.status == "submitted" RESPOND: "Transfer submitted: {{transferResult.transfer_id}}." THEN: complete - IF: transferResult.status == "duplicate" RESPOND: "This transfer appears to have already been submitted." THEN: complete - ELSE: RESPOND: "The transfer could not be submitted." THEN: escalate invalid_amount: REASONING: false RESPOND: "Please enter a positive transfer amount." THEN: collect_amount insufficient_funds: REASONING: false RESPOND: "The requested amount exceeds your available balance." THEN: collect_amount cancelled: REASONING: false RESPOND: "The transfer was cancelled." THEN: COMPLETE escalate: REASONING: false RESPOND: "I am connecting you with a specialist." THEN: ESCALATE with REASON: "Transfer submission failed" complete: REASONING: false SET: status = "complete" completed_at = NOW() RESPOND: "Your transfer request is complete." THEN: COMPLETE ``` *** ## Choose the right construct | Programming need | Preferred construct | | - | - | | Store or derive a value | `SET` | | Reset stale or dependent state | `CLEAR` | | Collect user data | `GATHER` | | Show a review before collecting confirmation | `PRESENT` | | Invoke a tool | `CALL` | | Validate a local prerequisite | `CHECK` | | Branch on explicit user input | `ON_INPUT` with `IF` / `ELSE` | | Branch on tool business outcomes | `ON_RESULT` | | Handle tool success versus failure | `ON_SUCCESS` / `ON_FAILURE` | | Route a guard or documented fallback failure | `ON_FAIL` | | Filter or shape a list | `TRANSFORM` | | Jump to a recovery or cancellation step | `GOTO` or `THEN` | | Handle semantic interruptions | `DIGRESSIONS` | | Handle a step-local request | `SUB_INTENTS` | | Bound a retry loop | `SET`, `CHECK`, `MAX_ATTEMPTS`, and re-entry via `THEN` | | End processing | `COMPLETE` | | Involve a human after scripted recovery is exhausted | `ESCALATE` | | Offer known choices as typed input | `ACTIONS` and `ON_ACTION` | *** ## Design guidelines 1. Keep deterministic work in `REASONING: false` steps and reserve reasoning steps for interpretation or open-ended decisions. 2. Use one `CALL` per step when tool ordering and failure routing matter. 3. Prefer `ON_RESULT` for business statuses such as `valid`, `duplicate`, or `account_closed`. Use `ON_FAILURE` for tool execution failures and `ON_FAIL` for guard/fallback failures where documented. 4. Clear dependent state whenever an earlier user choice changes. 5. Use `ON_INPUT` for explicit boolean conditions and `DIGRESSIONS` for semantic intent handling. 6. Use `CHECK` for local prerequisites and `CONSTRAINTS` for reusable cross-step or boundary rules. 7. Shape arrays with `TRANSFORM` before presenting them to the user. 8. Make every normal, failure, cancellation, retry, and escalation path explicit. 9. Treat re-entry to a named step as a loop and always give it a bounded exit condition. 10. Keep calculations and data shaping in expressions and `TRANSFORM`. Keep user-facing wording in `RESPOND`. 11. Use `DELEGATE` and `HANDOFF` only when the problem genuinely crosses an agent ownership boundary. # Tools Source: https://koreai.mintlify.app/agent-platform/abl/reference/tools The `TOOLS:` section defines the external capabilities available to an agent. Each tool declares a typed signature (parameters and return type), an optional execution binding, and metadata that guides the LLM's tool selection. ## Tool binding types The `type:` property selects how a tool is executed. The supported values are: | Type | Purpose | | :- | :- | | `http` | Call a REST (or SOAP) API endpoint. See [HTTP tools](#http-tools). | | `mcp` | Bind a tool exposed by an MCP server. See [MCP tools](#mcp-tools). | | `sandbox` | Run inline JavaScript/Python in an isolated sandbox. See [Code tools](#code-tools-sandbox). | | `lambda` | Invoke a cloud serverless function. See [Lambda tools](#lambda-tools). | | `async_webhook` | Fire-and-callback async HTTP. See [Async webhook tools](#async-webhook-tools). | | `workflow` | Invoke a durable workflow on the workflow engine. See [Workflow tools](#workflow-tools). | | `searchai` | Query a SearchAI knowledge base. See [SearchAI tools](#searchai-tools). | | `table` | Structured CRUD against an Agent Table. See [Table tools](#table-tools). | ## Tool declaration syntax You declare a tool with a function-style signature followed by indented properties. ```yaml theme={null} TOOLS: tool_name(param1: type1, param2: type2 = default) -> ReturnType description: "What this tool does" type: http endpoint: "/api/path" method: POST ``` ### Signature format ```yaml theme={null} name(parameters) -> return_type ``` * **name** -- a lowercase identifier using `snake_case` * **parameters** -- comma-separated list of typed parameters * **return\_type** -- the type of data the tool returns ### Parameter syntax Each parameter follows the format `name: type`, optionally marked optional with `?` and/or given a default value: ```yaml theme={null} param_name: type param_name?: type # optional (no default) param_name: type = default_value ``` | Component | Description | Examples | | - | - | - | | `name` | Parameter identifier (snake\_case) | `account_id`, `query`, `limit` | | `?` | Optional marker after the name — makes the parameter optional without giving it a default | `note?: string` | | `type` | Data type | `string`, `number`, `boolean`, `date` | | `= default` | Default value (also makes the parameter optional) | `= 10`, `= "USD"`, `= true` | A parameter is required only when it has **neither** a `?` marker **nor** a default value. Both `name?: type` and `name: type = default` make it optional. ### Parameter types | Type | Description | Examples | | - | - | - | | `string` | Text value | `"hello"` | | `number` | Numeric value (integer or float) | `42`, `3.14` | | `boolean` | True or false | `true`, `false` | | `date` | Date value | `"2026-03-01"` | | `object` | Nested object (specify fields with nested `parameters:`) | -- | | `type[]` | Array of the given type | `string[]`, `number[]` | #### Object parameters When a parameter has type `object`, you can define nested fields using a `parameters:` block under the tool: ```yaml theme={null} TOOLS: create_order(customer: object, items: object[]) -> {order_id: string} description: "Create a new order" parameters: customer: name: string email: string items: product_id: string quantity: number ``` #### Array parameters Array types use the `[]` suffix: ```yaml theme={null} TOOLS: bulk_lookup(ids: string[]) -> {results: object[]} description: "Look up multiple records by ID" ``` #### Parameter enrichment and sources The `parameters:` block does more than describe nested object fields — it can enrich any signature-declared parameter with a description, validation, visibility, and a **value source**. This lets you inject values (from system state, config, auth, an expression, or a lookup table) without the LLM having to supply them, and optionally hide them from the model. ```yaml theme={null} TOOLS: charge_card(amount: number, customer_id: string) -> {receipt_id: string} description: "Charge the customer's card on file" type: http endpoint: "/api/charges" method: POST parameters: customer_id: source: system # model | system | runtime | config | auth | expr | lookup value: "session.customer_id" hidden: true # do not expose to the model amount: description: "Charge amount in the account currency" required: true ``` | Enrichment key | Type | Description | | - | - | - | | `description` | `string` | Parameter description shown to the model. | | `required` | `boolean` | Whether the parameter is required. | | `default` | any | Default value. | | `source` | `string` | Value source: `model`, `system`, `runtime`, `config`, `auth`, `expr`, `lookup`. | | `value` | `string` | The expression or reference used with `expr`/`lookup`/`system`/`config` sources. | | `key` / `table` | `string` | Lookup key and table (with `source: lookup`). | | `hidden` | `boolean` | Hide the parameter from the model (still sent to the tool). | | `visibility` | `object` | Fine-grained `{ model, trace }` visibility flags. | | `properties` | `object` | Nested object field schema (for `object` params). | | `items` | `object` | Item schema (for array params). | `default_source` is accepted as an alias for `source` (both set the parameter's value source). Some compile-time diagnostics — for example the [trusted SIP transfer destination](#trusted-sip-transfer-destinations) check — refer to the key by its `default_source` spelling. #### Trusted SIP transfer destinations The built-in `call_transfer` voice tool takes a `sipTransferId` parameter — a `sip:`/`sips:` endpoint URI. To pin a **fixed, trusted** transfer target for the agent (rather than letting the model choose one, or relying on project settings), declare `call_transfer` with its signature in `TOOLS:` and enrich the `sipTransferId` parameter with a literal `default` and `default_source: config`: ```yaml theme={null} TOOLS: # Pin the agent's SIP transfer destination on the built-in call_transfer tool. call_transfer(callTransferType: string, sipTransferId: string) -> object params: # `parameters:` is an accepted alias sipTransferId: default: "sip:premium-support@sbc.example.com" default_source: config ``` The `params:` block enriches the signature-parsed parameter by name — it does not redeclare the built-in's implementation. Marking the default `default_source: config` does two things: the runtime reads the destination **server-side from the compiled agent** (never from the tool-call arguments, which carry model- and caller-influenced values), and it **hides the parameter from the model's tool schema** entirely, so the LLM never sees a destination field to fill. At SIP transfer time the runtime resolves the destination in order: (1) the authored `sipTransferId` default, if present and valid; (2) the project-configured `voice.sipTransferTargetUri` (Agent Transfer settings); (3) otherwise the call fails with a missing-target error. `default_source: system` is treated as trusted the same way (for platform-injected declarations). The compiler enforces that an authored destination is safe to trust: | Diagnostic | Severity | Fires when | | - | - | - | | `INVALID_SIP_TRANSFER_DESTINATION` | Error | A trusted-source (`config`/`system`) `sipTransferId` default is empty, is not a valid `sip:`/`sips:` URI, uses a dynamic source (`expr`, `lookup`, `runtime`, `auth`), or contains a `{{ }}` template other than an admitted inbound-header placeholder (see [Inbound SIP header placeholders](#inbound-sip-header-placeholders)). The destination's user/host must be a static literal — a target derived from conversation data could be influenced by the caller. | | `UNTRUSTED_SIP_TRANSFER_DESTINATION` | Warning | A `sipTransferId` default is declared with `default_source` **absent or `model`**. The value is treated as model-supplied and is **ignored at transfer time** (behavior is unchanged from before) — add `default_source: config` to make it the agent's trusted destination. | Omit the `sipTransferId` default entirely to rely solely on the project-configured Agent Transfer target. ##### Inbound SIP header placeholders The one template form allowed in a `sipTransferId` default is `{{sip.
}}`. At transfer time the runtime replaces it with the value of an inbound SIP header from the **current call's trusted SIP metadata** (the headers the call arrived with) — never from conversation data or model output. Use it to forward an inbound header onto the transfer leg, for example a `User-to-User` correlation token or a tracing header: ```yaml theme={null} TOOLS: call_transfer(callTransferType: string, sipTransferId: string) -> object params: sipTransferId: default: "sip:premium-support@sbc.example.com?User-to-User={{sip.user-to-user}}&X-Trace-Id={{sip.x-trace-id}}" default_source: config ``` **Syntax.** `{{sip.
}}`, where `
` is the inbound SIP header name, matched case-insensitively (for example `{{sip.user-to-user}}` reads the inbound `User-to-User` header). Placeholders are constrained so inbound data can *decorate* the transfer but never *redirect* it. The compiler rejects the default with `INVALID_SIP_TRANSFER_DESTINATION` unless every placeholder satisfies all of: | Rule | Requirement | | - | - | | **Position** | A placeholder may appear only in the URI's header section (the `?Name=value&…` part after `?`). A placeholder anywhere in the user/host — the part that determines where the call is dialed — is rejected (`placeholder_outside_headers`). | | **Whole value only** | A placeholder must be the *entire* value of a URI header (`?X-Trace-Id={{sip.x-trace-id}}`), not embedded in a larger value and not partial. | | **Value, not name** | A placeholder may never appear in a URI-header *name* (`placeholder_in_header_name`) — an inbound value must not become a header key. | | **Admitted destination header** | Only `User-to-User` and any `x-*` header may *carry* a placeholder value (`inadmissible_header_name` otherwise). | | **Admitted source header** | The inbound header *read from* — the `` in `{{sip.}}` — must **also** be admissible (`User-to-User` or `x-*`), so an admitted destination header cannot launder an arbitrary inbound header's value (`inadmissible_source_header` otherwise). | Any other use of `{{ }}` — a general expression/variable template, a placeholder in the user/host, or a malformed placeholder — remains an error; only `{{sip.
}}` in an admitted header value is interpolated. The URI's user and host are always static. ##### Dynamic destinations For a destination chosen at runtime, replace the static `default` with a `template` and an `approved_authorities` allow-list. The template is a **two-zone** SIP URI: a static shape whose authority (user/host/port) is filled from `{{route.*}}` placeholders, plus the same inbound `{{sip.
}}` header decoration described above. The `{{route.*}}` values are produced by a [`RESOLVE_SIP_DESTINATION`](/agent-platform/abl/reference/flow#resolve_sip_destination) flow step, which runs before the transfer and commits the chosen authority to a trusted server-side routing channel. ```yaml theme={null} TOOLS: call_transfer(callTransferType: string, sipTransferId: string) -> object params: sipTransferId: default_source: config template: "sip:{{route.vdn}}@{{route.ip}}:{{route.port}};user=phone?User-to-User={{sip.user-to-user}}" approved_authorities: - "sip:521001@172.0.41.7:5060" - "sip:521002@172.0.41.7:5060" - "sip:5220[0-9][0-9]@172.0.41.7:5060" ``` * **Zone 1 — authority** (before `?`): `{{route.vdn}}`, `{{route.ip}}`, `{{route.port}}` are filled from the `RESOLVE_SIP_DESTINATION` feeder's result — its return shape must use exactly those keys. * **Zone 2 — headers** (after `?`): `{{sip.
}}` placeholders, resolved server-side from trusted inbound SIP metadata, under the same rules as static defaults above. * **`approved_authorities`** is a mandatory allow-list — the resolved destination must match one entry or the transfer is refused. Each entry must be a **literal** SIP authority with an explicit scheme, host, and port (no default-port inference, no wildcard host). The VDN (user part) may be an exact digit string or a constrained digit-class pattern using single-digit ranges only — for example, `5220[0-9][0-9]`. Regex metacharacters, alternation, anchors, groups, and any query/header/param section are rejected. The compiler validates both surfaces against the same predicates the runtime matcher uses (so a compile-accepted entry is never runtime-rejected): a malformed template raises `INVALID_SIP_TRANSFER_DESTINATION`; a bad allow-list entry raises `INVALID_APPROVED_SIP_AUTHORITY` (not a literal authority with a literal VDN, host, and explicit port) or `UNSAFE_SIP_AUTHORITY_PATTERN` (unsafe VDN pattern). ### Return type The return type appears after the `->` arrow. It can be: | Form | Syntax | Example | | - | - | - | | Simple type | `-> type` | `-> string` | | Object type | `-> {field: type, ...}` | `-> {id: string, name: string}` | | Array type | `-> type[]` | `-> Hotel[]` | | Nested object | `-> {field: {nested: type}}` | `-> {user: {name: string, age: number}}` | | Optional fields | `-> {field?: type}` | `-> {error?: string}` | | Array of objects | `-> {field: type}[]` | `-> {id: string, title: string}[]` | ```yaml theme={null} TOOLS: search_hotels(destination: string, checkin: date, checkout: date) -> {hotels: {id: string, name: string, price: number, rating: number}[], total: number} description: "Search available hotels" ``` ### Description The `description:` property provides a natural-language explanation of what the tool does. The LLM uses this description to decide when and how to call the tool. ```yaml theme={null} TOOLS: verify_account(account_id: string) -> {name: string, status: string} description: "Retrieve account details and verify the account is active" ``` Write meaningful descriptions that help the LLM understand: * What the tool does * When to call it * What data it returns ### Hints The `hints:` block provides execution metadata that the runtime uses for optimization: ```yaml theme={null} TOOLS: get_balance(account_id: string) -> {balance: number} description: "Get account balance" hints: cacheable: true latency: fast timeout: 5000 ``` | Hint | Type | Default | Description | | - | - | - | - | | `cacheable` | `boolean` | *none* | Whether results can be cached across calls with the same parameters | | `latency` | `"fast"` , `"medium"` , `"slow"` | *none* | Expected latency category for runtime scheduling | | `side_effects` | `boolean` | *none* | Whether the tool modifies external state | | `requires_auth` | `boolean` | *none* | Whether the tool requires authenticated context | | `timeout` | `number` | *none* | Tool-specific timeout in milliseconds (overrides `EXECUTION.tool_timeout`) | *** ## HTTP tools HTTP tools call REST API endpoints. They're the most common tool type. ```yaml theme={null} TOOLS: search_flights(origin: string, destination: string, date: date) -> {flights: {id: string, price: number}[]} description: "Search available flights" type: http endpoint: "https://api.flights.com/v1/search" method: POST auth: bearer timeout: 10000 retry: 3 retry_delay: 1000 ``` ### HTTP binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"http"` | Yes | -- | Declares this as an HTTP tool | | `endpoint` | `string` | Yes | -- | URL or path for the API call. Supports path parameters: `"/hotels/{hotel_id}"` | | `method` | `"GET"` , `"HEAD"` , `"OPTIONS"` , `"POST"` , `"PUT"` , `"PATCH"` , `"DELETE"` | Yes | -- | HTTP method | | `idempotency_header` | `string` | No | *none* | Header name the runtime sends a durable dedupe key under (for example `Idempotency-Key`). Must name a real HTTP header, be ≤128 characters, and cannot be a credential/cookie/transport/signing header (`Authorization`, `Cookie`, `Host`, `Content-Type`, `X-Amz-*`, `SOAPAction`, etc.) — an invalid value is a compile warning (`HTTP_TOOL_INVALID_IDEMPOTENCY_HEADER`) and the runtime sends no idempotency key. Omit to keep the key internal. | | `auth` | `string` | No | `"none"` | Authentication type (see [Auth](#auth)) | | `timeout` | `number` | No | `EXECUTION.tool_timeout` | Request timeout in milliseconds | | `retry` | `number` | No | *none* | Number of retry attempts on failure | | `retry_delay` | `number` | No | *none* | Delay in milliseconds between retries | | `retry_policy` | `object` | No | *none* | Structured backoff policy (richer alternative to `retry`/`retry_delay`). See [Retry policy](#retry-policy). | | `headers` | `Record` | No | *none* | Custom HTTP headers | | `query_params` | `Record` / `object[]` | No | *none* | Query string parameters. A `key: value` block keeps insertion order; an array of `{key, value}` also allows duplicate keys. | | `body_type` | `"json"` , `"form"` , `"xml"` , `"text"` | No | *none* | Request body encoding. | | `body_mode` | `"auto"` , `"none"` | No | `"auto"` | For non-GET methods: `auto` serializes remaining params as the body; `none` sends no body. | | `body_template` | `string` | No | *none* | Custom body template with interpolation. `body` is accepted as an alias. | | `body_schema` | `string` | No | *none* | JSON Schema for the request body (used with `use_body_schema: true`). | | `use_body_schema` | `boolean` | No | `false` | Author/validate the request body from `body_schema`. | | `request_variants` | `object[]` | No | *none* | Conditional request variants selected by a `when` expression. See [Request variants](#request-variants). | | `rate_limit` | `number` | No | *none* | Maximum requests per second | | `circuit_breaker` | `{threshold, resetMs}` | No | *none* | Circuit breaker configuration | | `protocol` | `"rest"` , `"soap"` | No | `"rest"` | Wire protocol. Set `soap` for SOAP endpoints. See [SOAP support](#soap-support). | | `soap_version` | `"1.1"` , `"1.2"` | No | `"1.1"` | SOAP version (when `protocol: soap`). | | `soap_action` | `string` | No | *none* | `SOAPAction` header (1.1) or action media-type parameter (1.2). | | `on_soap_fault` | `"error"` , `"data"` | No | *none* | How to handle `` — `error` throws, `data` returns the fault payload. | | `wsdl_*` | see [WSDL operation binding](#wsdl-operation-binding) | No | *none* | Bind a SOAP tool to a stored WSDL operation instead of authoring the envelope. | ### Path parameters Use curly braces in the endpoint to reference tool parameters: ```yaml theme={null} TOOLS: get_hotel(hotel_id: string) -> Hotel type: http endpoint: "/hotels/{hotel_id}" method: GET ``` ### Headers Custom headers are specified as key-value pairs: ```yaml theme={null} TOOLS: get_data(query: string) -> {results: object[]} type: http endpoint: "/api/data" method: POST headers: X-Api-Version: "2024-01" Accept: "application/json" ``` On a recognized auth header — `Authorization`, `Proxy-Authorization`, `X-Api-Key`, `Api-Key`, or `X-Auth-Token` (case-insensitive) — a literal value that looks like a real credential (a JWT, a known provider token prefix, a `Bearer `/`Basic `/`Token ` scheme, or another long opaque token) is a compile warning (`PLAINTEXT_HTTP_HEADER_SECRET`). The authored request still compiles and runs unchanged, but that value can cross export, logging, or runtime boundaries with no secret-management protection. Use `{{config.API_TOKEN}}` or an auth profile instead of a literal; a `{{ }}` reference is never flagged. ### Auth The `auth:` property specifies the authentication mechanism. The runtime resolves credentials from the project's credential store -- the ABL document never contains actual secrets. | Auth type | Description | | - | - | | `none` | No authentication | | `bearer` | Bearer token in `Authorization` header | | `api_key` | API key (header name configurable via `auth_config`) | | `oauth2_client` | OAuth 2.0 client credentials flow | | `oauth2_user` | OAuth 2.0 authorization code flow (user-scoped tokens) | | `saml` | SAML assertion-based authentication | | `custom` | Custom authentication with configurable headers | `saml` is accepted by the parser but is **not currently resolved at runtime** — it is dropped during compilation. Prefer an [auth profile](#auth-profiles-jit-consent-and-connections) for enterprise SSO-backed integrations. #### Auth configuration For auth types that require additional configuration, use the `auth_config:` block: ```yaml theme={null} TOOLS: get_user_data(user_id: string) -> {name: string, email: string} type: http endpoint: "/api/users/{user_id}" method: GET auth: oauth2_client auth_config: token_url: "https://auth.example.com/oauth/token" client_id: "my-client-id" client_secret: "{{config.OAUTH_CLIENT_SECRET}}" scopes: "read:users" ``` ```yaml theme={null} TOOLS: call_partner_api(payload: string) -> {status: string} type: http endpoint: "https://partner.example.com/api" method: POST auth: api_key auth_config: header_name: "X-API-Key" ``` | Auth config property | Type | Used with | Description | | - | - | - | - | | `token_url` | `string` | `oauth2_client`, `oauth2_user` | OAuth token endpoint URL | | `client_id` | `string` | `oauth2_client`, `oauth2_user` | OAuth client identifier | | `client_secret` | `string` | `oauth2_client`, `oauth2_user` | OAuth client secret (use `{{config.X}}` placeholders) | | `scopes` | `string` | `oauth2_client`, `oauth2_user` | Space-separated OAuth scopes | | `header_name` | `string` | `api_key` | Custom header name for the API key | | `provider` | `string` | `oauth2_user`, `saml` | Auth provider identifier | | `custom_headers` | `Record` | `custom` | Custom authentication headers | #### Credential placeholders Credentials use `{{config.NAME}}` placeholder syntax (with `isSecret: true` on the config var). The runtime resolves these from the project's config vars at execution time. For OAuth integrations, binding an auth profile is the preferred approach. Never embed actual credentials in ABL files. ```yaml theme={null} auth_config: client_secret: "{{config.PARTNER_OAUTH_SECRET}}" ``` #### Auth profiles, JIT, consent, and connections For OAuth and enterprise integrations, the preferred approach is to bind a pre-configured **auth profile** by name rather than declaring `auth_config` inline. These tool-level properties control credential resolution, transport security, just-in-time authorization, consent, and per-user vs. shared connections: ```yaml theme={null} TOOLS: create_ticket(subject: string, body: string) -> {ticket_id: string} description: "Create a support ticket" type: http endpoint: "https://itsm.example.com/api/tickets" method: POST auth_profile: servicenow_oauth # named profile resolved at runtime tls_profile: partner_mtls # mTLS transport cert profile auth_jit: true # acquire credentials just-in-time consent: preflight # preflight | inline connection: per_user # per_user | shared ``` | Property | Type | Description | | - | - | - | | `auth_profile` | `string` | Name of a pre-configured auth profile; resolved at runtime (no secrets in the ABL file). | | `tls_profile` | `string` | Name of an mTLS transport-certificate profile (independent of `auth_profile`). | | `auth_jit` | `boolean` | Acquire credentials just-in-time at call time rather than up front. | | `consent` | `"preflight"` , `"inline"` | When user consent is obtained — before the run (`preflight`) or during the conversation (`inline`). | | `connection` | `"per_user"` , `"shared"` | Whether the integration uses per-end-user connections or a single shared connection. | ### Result transformation Tool results are available in the session context after execution. You can map specific result fields to session variables using `on_result:` and handle errors with `on_error:`: ```yaml theme={null} TOOLS: get_balance(account_id: string) -> {balance: number, currency: string} description: "Get account balance" type: http endpoint: "/api/balance" method: GET on_result: set: current_balance: result.balance account_currency: result.currency on_error: set: balance_error: error.message ``` Beyond `set:`, both `on_result:` and `on_error:` accept an ordered `do:` action block that can run `SET`, `CLEAR`, `LOG`, `RESPOND`, `CALL`, `GOTO`/`THEN`, `HANDOFF`, `DELEGATE`, `RETURN`, `FORMATS` (or its canonical alias `RICH_CONTENT`), and `COMPLETE` actions, with conditional `IF:`/`WHEN:`/`ELSE:` branches. A rich-content payload rendered this way — for example a `FEEDBACK` survey attached to a tool's result — works with either spelling. A `do:` action's `RESPOND` can carry its own `MODEL_CONTEXT` — a text-scalar sibling, the same idea as [MODEL\_CONTEXT on a response](/agent-platform/abl-reference/flow#model_context-on-a-response), applied here to a tool lifecycle response instead of a FLOW step's: ```yaml theme={null} TOOLS: list_products() -> object MODEL_CONTEXT: FIELDS: [products[].id, products[].name, total] on_result: do: - RESPOND: "{{result.widget}}" MODEL_CONTEXT: "Displayed {{result.total}} products in order: {{result.product_summary}}." COMPLETE: on_error: do: - RESPOND: "Product search is unavailable." MODEL_CONTEXT: "Product search failed; no products were displayed." COMPLETE: ``` The full, protected `RESPOND` is still what's delivered and retained in the customer transcript — `MODEL_CONTEXT` only replaces what a *later* model call sees of this specific assistant turn. `result`/`error`, the tool's inputs, and any earlier `SET` in the same action are available to both templates. `MODEL_CONTEXT` without a `RESPOND` in the same action is invalid (`MODEL_CONTEXT_INVALID`). Omit it to keep the historical full-response behavior; an explicitly empty value omits that response from later model calls entirely. This is independent of the tool-level `MODEL_CONTEXT` block above — one shapes the raw tool result the model sees, the other shapes what the model retains of this particular lifecycle response. ### SSRF protection The platform enforces SSRF (Server-Side Request Forgery) protection on all HTTP tool endpoints. Private IP ranges, localhost, and internal network addresses are blocked at the runtime level. Only allowlisted domains and public endpoints are permitted for HTTP tool calls. ### Circuit breaker The circuit breaker prevents cascading failures by halting requests to a failing endpoint after a threshold of consecutive errors: ```yaml theme={null} TOOLS: unreliable_api(query: string) -> {data: string} type: http endpoint: "https://api.example.com/data" method: GET circuit_breaker: threshold: 5 resetMs: 30000 ``` | Property | Type | Description | | - | - | - | | `threshold` | `number` | Number of consecutive failures before the circuit opens | | `resetMs` | `number` | Milliseconds to wait before trying the endpoint again | ### Retry policy `retry_policy` is a structured backoff configuration — a richer alternative to the scalar `retry`/`retry_delay` fields (both forms are still accepted). ```yaml theme={null} TOOLS: flaky_api(id: string) -> {data: string} type: http endpoint: "https://api.example.com/{id}" method: GET retry_policy: strategy: exponential # fixed | exponential initial_delay_ms: 200 multiplier: 2 max_delay_ms: 5000 max_retries: 4 jitter: full # none | full ``` | Property | Type | Default | Description | | - | - | - | - | | `strategy` | `"fixed"` , `"exponential"` | -- | Backoff strategy (required). | | `initial_delay_ms` | `number` | -- | Delay before the first retry. | | `multiplier` | `number` | -- | Growth factor per attempt (for `exponential`). | | `max_delay_ms` | `number` | -- | Ceiling on the delay between attempts. | | `max_retries` | `number` | -- | Maximum retry attempts. | | `jitter` | `"none"` , `"full"` | -- | Randomization applied to the delay. | ### SOAP support HTTP tools can call SOAP endpoints by setting `protocol: soap`. ```yaml theme={null} TOOLS: lookup_policy(policy_number: string) -> {status: string, holder: string} description: "Look up an insurance policy" type: http endpoint: "https://legacy.example.com/PolicyService" method: POST protocol: soap soap_version: "1.1" soap_action: "urn:PolicyService#lookupPolicy" on_soap_fault: error # error | data soap_headers: '{"Security": {"UsernameToken": {"Username": "svc"}}}' ``` | Property | Type | Default | Description | | - | - | - | - | | `protocol` | `"rest"` , `"soap"` | `"rest"` | Wire protocol. | | `soap_version` | `"1.1"` , `"1.2"` | `"1.1"` | SOAP envelope version. | | `soap_action` | `string` | *none* | `SOAPAction` header (1.1) or action media-type parameter (1.2). | | `on_soap_fault` | `"error"` , `"data"` | *none* | `error` raises on ``; `data` returns the fault as the result. | | `soap_headers` | JSON object (as a string) | *none* | Custom SOAP header elements, authored as a JSON string. Malformed JSON is dropped rather than failing the tool compile. | #### WSDL operation binding Instead of hand-writing the SOAP envelope, a SOAP tool can bind to an operation from a WSDL asset stored in the project [WSDL Repository](/agent-platform/tools/wsdl-repo). When bound, the runtime resolves the operation's namespace, `SOAPAction`, and SOAP version from the stored WSDL and **generates the request body from the tool's JSON input** — no manual `body` template is needed. The request `endpoint` still comes from the tool: bulk-created tools are populated with the WSDL service address (or an endpoint override), and hand-authored tools must set `endpoint` explicitly. Reference the WSDL by name; the platform resolves it to a stable asset/version at save time: ```yaml theme={null} TOOLS: lookup_policy(policy_number: string) -> {status: string, holder: string} description: "Look up an insurance policy" type: http endpoint: "https://legacy.example.com/PolicyService" method: POST protocol: soap soap_version: "1.1" wsdl_asset: "PolicyService" # asset name in the WSDL Repository wsdl_version: latest # version number or "latest" (default) wsdl_operation: "lookupPolicy" ``` | Property | Type | Required | Description | | - | - | - | - | | `wsdl_asset` | `string` | No | WSDL asset **name** to bind. Resolved to `wsdl_asset_id` at save time. | | `wsdl_version` | `string` | No | Version number (for example `2`) or `latest`. Defaults to the asset's latest version. | | `wsdl_operation` | `string` | Yes\* | Name of the WSDL operation to invoke. | | `wsdl_asset_id` | `string` | Yes\* | Stable asset ID. Written automatically when `wsdl_asset` is resolved; may be authored directly. | | `wsdl_version_id` | `string` | Yes\* | Stable immutable version ID. Written automatically when `wsdl_version` is resolved. | | `wsdl_service` | `string` | No | WSDL `service` name (when an operation is ambiguous across services). | | `wsdl_port` | `string` | No | WSDL `port` name. | | `wsdl_binding` | `string` | No | WSDL `binding` name. | | `wsdl_target_namespace` | `string` | No | Target namespace override for body generation. | | `wsdl_element` | `string` | No | Root request element name, when it differs from the operation name. | | `wsdl_response_arrays` | comma-separated `string` | No | Response paths (for example `Envelope.Body.Response.Items,Envelope.Body.Response.Errors`) that should be parsed as arrays. | A WSDL binding requires `wsdl_operation` plus a resolved asset/version. Author either the by-name pair (`wsdl_asset` + optional `wsdl_version`) — which the platform rewrites to `wsdl_asset_id` / `wsdl_version_id` on save — or the stable IDs directly. Versions are immutable: a bound tool keeps calling its pinned version until you repoint it, so a WSDL update never silently changes behavior. If a WSDL construct is unsupported, author a manual SOAP tool with an explicit `body` template instead — the binding fields and a manual body are mutually exclusive per tool. ### Request variants For endpoints that vary by condition, `request_variants` selects a request shape at call time based on a `when` expression. Each variant can override the endpoint, method, headers, query params, and body. ```yaml theme={null} TOOLS: submit(order: object, region: string) -> {ref: string} type: http endpoint: "https://api.example.com/us/submit" method: POST request_variants: - name: eu when: region == "EU" endpoint: "https://api.eu.example.com/submit" body_type: json - name: default default: true ``` *** ## MCP tools MCP (Model Context Protocol) tools connect to external MCP servers that expose tools dynamically. The runtime handles protocol negotiation, transport, and tool discovery. ```yaml theme={null} TOOLS: web_search(query: string) -> {results: {title: string, url: string, snippet: string}[]} description: "Search the web" type: mcp server: "brave-search" ``` ### MCP binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"mcp"` | Yes | -- | Declares this as an MCP tool | | `server` | `string` | Yes | -- | MCP server name (resolved from runtime configuration) | | `server_tool` | `string` | No | Tool name | Tool name on the MCP server (if different from the ABL tool name) | | `output_schema` | JSON object (string) | No | -- | JSON Schema describing the tool's result, as a single JSON object literal — for example `{"type": "object", "properties": {...}}`. Invalid JSON is dropped (`TOOL_OUTPUT_SCHEMA_INVALID`). | | `headers` | `Record` | No | *none* | Per-call headers, supporting `{{config.X}}` / `{{session.X}}` templates. | ### Server configuration The `server` value is a logical name that maps to an MCP server configuration in the project's runtime settings. Server configuration (transport type, connection URL, authentication) is managed at the project level, not in the ABL file. The platform supports these MCP transport types. | Transport | Description | | - | - | | `stdio` | Standard input/output (for local MCP server processes) | | `http` | HTTP-based transport (Streamable HTTP) | | `websocket` | WebSocket-based transport | ### Dynamic tool discovery MCP servers can expose multiple tools. When you declare an MCP tool in ABL, you bind a specific tool from the server. If the tool name on the MCP server differs from the tool name in your ABL file, use `server_tool:` to specify the server-side name. ```yaml theme={null} TOOLS: search(query: string) -> {results: object[]} description: "Search using Brave" type: mcp server: "brave-search" server_tool: "brave_web_search" ``` *** ## Code tools (sandbox) Code tools execute user-provided JavaScript or Python code in an isolated sandbox environment with resource limits and no network access. ```yaml theme={null} TOOLS: calculate_tax(income: number, state: string) -> {tax: number, rate: number} description: "Calculate state income tax" type: sandbox runtime: javascript timeout: 5000 memory_mb: 128 code: | const rates = { CA: 0.133, NY: 0.109, TX: 0 }; const rate = rates[state] || 0.05; return { tax: income * rate, rate }; ``` ### Sandbox binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"sandbox"` | Yes | -- | Declares this as a sandbox tool | | `runtime` | `"javascript"` , `"python"` | Yes | -- | Execution runtime | | `code` | `string` | No | *none* | Inline source code (pipe block syntax for multi-line) | | `timeout` | `number` | No | Platform default | Maximum execution time in milliseconds | | `memory_mb` | `number` | No | Platform default | Memory limit in megabytes | ### Isolation Sandbox tools execute in a gVisor-isolated environment with the following restrictions: * No network access * No filesystem access beyond the sandbox working directory * Resource limits enforced (CPU, memory, execution time) * Each execution runs in a new environment ### JavaScript runtime JavaScript code tools have access to standard JavaScript built-ins. The code should return a value matching the declared return type. ```yaml theme={null} TOOLS: format_currency(amount: number, currency: string) -> {formatted: string} type: sandbox runtime: javascript code: | const formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: currency }); return { formatted: formatter.format(amount) }; ``` ### Python runtime Python code tools use a sandboxed Python interpreter. The code should return a dictionary matching the declared return type. ```yaml theme={null} TOOLS: analyze_text(text: string) -> {word_count: number, sentences: number} type: sandbox runtime: python code: | import re words = len(text.split()) sentences = len(re.split(r'[.!?]+', text)) return {"word_count": words, "sentences": sentences} ``` *** ## Lambda tools Lambda tools invoke cloud serverless functions. The function name is a logical identifier resolved to an actual endpoint (ARN, URL) at runtime through the project's function registry. ```yaml theme={null} TOOLS: process_document(document_url: string, format: string) -> {text: string, pages: number} description: "Extract text from a document" type: lambda function: "document-processor" runtime: "nodejs20" timeout: 30000 ``` ### Lambda binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"lambda"` | Yes | -- | Declares this as a lambda tool | | `function` | `string` | Yes | -- | Logical function name (resolved at runtime) | | `runtime` | `string` | No | *none* | Runtime hint (for example. `"nodejs20"`, `"python3.12"`) | | `timeout` | `number` | No | Platform default | Override timeout for this function in milliseconds | *** ## Async webhook tools Async webhook tools send an HTTP request with a callback URL injected into the payload. The external system processes the request asynchronously and posts the result back to the callback URL when complete. ```yaml theme={null} TOOLS: generate_report(report_type: string, date_range: string) -> {report_url: string, status: string} description: "Generate a financial report (processed asynchronously)" type: async_webhook endpoint: "https://reports.internal/api/generate" method: POST callback_url_field: "callbackUrl" timeout_seconds: 3600 ``` ### Async webhook binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"async_webhook"` | Yes | -- | Declares this as an async webhook tool | | `endpoint` | `string` | Yes | -- | URL to send the initial request | | `method` | `"POST"` , `"PUT"` , `"PATCH"` | Yes | -- | HTTP method | | `headers` | `Record` | No | *none* | Custom HTTP headers | | `callback_url_field` | `string` | No | `"callbackUrl"` | Dot-path in the request body where the callback URL is injected | | `timeout_seconds` | `number` | No | `3600` | Timeout in seconds for the async callback response | *** ## Workflow tools Workflow tools invoke a durable workflow on the workflow engine. Use these for long-running, stateful orchestrations (waits, polling, human approval, multi-hour processes) that shouldn't run inside the stateless agent runtime. ```yaml theme={null} TOOLS: start_onboarding(customer_id: string, plan: string) -> {run_id: string, status: string} description: "Kick off the customer onboarding workflow" type: workflow workflow_id: "customer-onboarding" mode: async timeout_ms: 60000 param_mapping: customerId: customer_id selectedPlan: plan ``` ### Workflow binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"workflow"` | Yes | -- | Declares this as a workflow tool. | | `workflow_id` | `string` | Yes | -- | Logical workflow identifier. Version routing comes from the deployment manifest. | | `mode` | `"sync"` , `"async"` | No | `"sync"` | `sync` waits for completion; `async` starts the run and returns a handle. | | `timeout_ms` | `number` | No | *none* | Maximum time to wait. Supports `{{config.X}}` interpolation. | | `param_mapping` | `Record` | No | *none* | Maps workflow input fields to tool parameters (workflow key → tool parameter). | The workflow must exist in the same tenant/project; this is validated at compile time. Legacy `workflow_version` / `trigger_id` fields are no longer used and are silently ignored. *** ## SearchAI tools SearchAI tools query a SearchAI knowledge base (KB) index, letting an agent ground its answers in the indexed content. ```yaml theme={null} TOOLS: search_policies(query: string) -> {results: object[]} description: "Search the HR policy knowledge base" type: searchai index_id: "hr-policies-v2" kb_name: "HR Policies" search_instructions: "Prefer the most recent policy version when duplicates exist." ``` ### SearchAI binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"searchai"` | Yes | -- | Declares this as a SearchAI tool. | | `index_id` | `string` | Yes | -- | SearchAI KB index to query. | | `kb_name` | `string` | No | -- | Display name for the knowledge base. | | `search_instructions` | `string` | No | -- | Extra instructions injected into the search/classification prompt. | | `retrieval_profile` | `string` | No | -- | Set to `object-aware` to use object-aware retrieval against the KB index. | SearchAI tools are tenant-scoped; the tenant binding is applied by the platform. Do not hardcode a `tenant_id` in shared/exported definitions. *** ## Table tools Table tools perform structured CRUD against an **Agent Table** — a project-, user-, or session-scoped datastore. ```yaml theme={null} TOOLS: save_note(title: string, body: string) -> {id: string} description: "Store a customer note" type: table table: customer_notes scope: end_user operations: [get, query, insert, update] ``` ### Table binding properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | `type` | `"table"` | Yes | -- | Declares this as a table tool. | | `table` | `string` | Yes | -- | Primary table slug (`^[a-z_][a-z0-9_]*$`). `table_name` is an alias. | | `scope` | `string` | No | -- | `project`, `end_user`, or `session`. The scope key is derived from trusted session context, never authored in the binding. | | `operations` | `string[]` | No | read-only (`get`/`query`/`count`) | Allowed operations: `get`, `query`, `count`, `insert`, `update`, `delete`, `upsert`, `bulkInsert`, `bulkUpsert`. | | `tables` | `string[]` | No | -- | Additional tables to JOIN (max 8). | | `query_template` | `string` | No | -- | Optional query template. | *** ## Tool file imports You can organize the tool definitions into reusable `.tools.abl` files and import it into agent documents. This lets you share tool definitions across multiple agents. ### Tool file format A `.tools.abl` file starts with a `TOOLS:` section that can include shared defaults: ```yaml theme={null} TOOLS: base_url: "https://api.hotels.com/v1" auth: bearer timeout: 5000 retry: 3 search_hotels(destination: string, checkin: date, checkout: date) -> Hotel[] type: http endpoint: "/search" method: POST description: "Search available hotels" get_hotel(hotel_id: string) -> Hotel type: http endpoint: "/hotels/{hotel_id}" method: GET description: "Get hotel details by ID" ``` ### Shared defaults The following defaults can be set at the top of a `.tools.abl` file and apply to all tools in the file. | Default | Type | Description | | - | - | - | | `base_url` | `string` | Base URL prepended to all relative endpoint paths | | `auth` | `string` | Default auth type for all tools | | `timeout` | `number` | Default timeout in milliseconds | | `retry` | `number` | Default retry count | | `retry_delay` | `number` | Default retry delay in milliseconds | | `rate_limit` | `number` | Default rate limit (requests per second) | | `headers` | `Record` | Default headers applied to all tools | ### Import syntax Import tools from a `.tools.abl` file into an agent using the `file:` directive within the `TOOLS:` section. ```yaml theme={null} TOOLS: file: "./tools/hotels-api.tools.abl" [search_hotels, get_hotel] ``` The import specifies: * The path to the `.tools.abl` file (relative to the agent file) * An optional list of specific tool names to import (in brackets). If omitted, all tools from the file are imported. Project bundle import can also synthesize tool stubs from inline agent `TOOLS:` signatures during apply. That keeps imported agents compileable even when the bundle didn't include companion `.tools.abl` files, and the next export writes those synthesized stubs back out as `tools/.tools.abl`. ### Example Given the file `tools/hotels-api.tools.abl`. ```yaml expandable=true theme={null} TOOLS: base_url: "https://api.hotels.com/v1" auth: bearer search_hotels(destination: string, checkin: date, checkout: date) -> Hotel[] type: http endpoint: "/search" method: POST description: "Search available hotels" get_hotel(hotel_id: string) -> Hotel type: http endpoint: "/hotels/{hotel_id}" method: GET description: "Get hotel details by ID" get_hotel_reviews(hotel_id: string, limit: number = 10) -> Review[] type: http endpoint: "/hotels/{hotel_id}/reviews" method: GET description: "Get reviews for a hotel" ``` An agent file can import selected tools: ```yaml theme={null} AGENT: Hotel_Search TOOLS: file: "./tools/hotels-api.tools.abl" [search_hotels, get_hotel] # Additional agent-specific tools check_availability(hotel_id: string, dates: string) -> {available: boolean} description: "Check real-time availability" type: http endpoint: "/api/availability" method: POST ``` *** ## Tool features ### Confirmation The `confirmation:` block configures whether the agent should ask the user for confirmation before executing a tool. This is particularly important for tools with side effects (for example. executing a payment, deleting a record). ```yaml theme={null} TOOLS: execute_payment(amount: number, recipient: string) -> {confirmation_id: string} description: "Execute a payment" type: http endpoint: "/api/payments" method: POST confirmation: require: always immutable_params: [recipient] ``` | Property | Type | Default | Description | | - | - | - | - | | `require` | `"always"` , `"never"` , `"when_side_effects"` | *none* | When to require user confirmation | | `immutable_params` | `string[]` | *none* | Parameters that can't be changed after confirmation | | `consent_required_in` | `"conversation"` , `"explicit_prompt"` | *none* | Where consent must be obtained. | | `consent_scope` | `string[]` | *none* | Consent scopes required before executing. | Without a `consent_action`, a required confirmation's question is built from the first sentence of the tool's `description` ("Shall I ``…?"). A `description` that opens with a noun phrase or a third-person verb reads as a broken question (compile warning `TOOL_CONFIRMATION_PHRASE_NOT_ACTION`) — write the description starting with an imperative verb ("Transfer the amount to the beneficiary"), or set `consent_action` explicitly to control the confirmation wording. \| `consent_action` | `string` | *none* | Named consent action to trigger. | \| `consent_fallback` | `"explicit_prompt"` , `"block"` | *none* | What to do if consent isn't granted. | #### What the customer is asked When a call needs approval, the customer sees a plain-language question in chat and in cascaded voice (speech-to-text, reasoning, text-to-speech). The runtime builds it from the `tool_confirmation_request` message, your `consent_action` (if you set one), and a readable label for each parameter value. The default wording is: ```yaml theme={null} Please confirm before I go ahead{{action_suffix}}.{{details}} Shall I proceed? ``` For the `create_order` tool with `consent_action: "create order"`, the customer sees: ```yaml theme={null} Please confirm before I go ahead (create order). - Account id: 4411 - Amount: 25 Shall I proceed? ``` * `consent_action` appears in parentheses after "go ahead". Set it to a short, customer-friendly phrase such as "create order". If you leave it out, the parenthetical is omitted. * Each parameter appears on its own line with a humanized label: `account_id`, `accountId`, and `account-id` all display as "Account id". Empty values are skipped, and values of sensitive gather fields are masked. * The tool name, parameter lock markers (`immutable_params`), and the instructions the runtime gives the model are never shown to the customer. * If the tool already ran earlier in the conversation and the agent is about to repeat it, the customer is asked "I already completed this earlier in our conversation (create order). Would you like me to do it again?" instead. Override it with the `tool_confirmation_duplicate_effect` key. To change the wording, override `tool_confirmation_request` in the agent's [`MESSAGES:`](/agent-platform/abl/reference/lifecycle-and-hooks#messages) block. The template supports `{{action_suffix}}` and `{{details}}`: ```yaml theme={null} MESSAGES: tool_confirmation_request: "Before I continue{{action_suffix}}, please check these details:{{details}}\n\nIs that correct?" ``` Realtime speech-to-speech sessions leave approval to the model provider, so this wording does not apply there. ### State authority and provided fields The `effect` property declares a tool's authority over session state, and `provides` lists the fields the tool is authoritative to supply. These help the runtime reason about which values a tool is allowed to write. ```yaml theme={null} TOOLS: set_shipping_address(address: object) -> {saved: boolean} description: "Persist the customer's shipping address" type: http endpoint: "/api/shipping-address" method: PUT effect: state_write # read_only | state_write | side_effect provides: [shipping_address, shipping_zone] ``` | Property | Type | Values | Description | | - | - | - | - | | `effect` | `string` | `read_only`, `state_write`, `side_effect` | The tool's authority over external/session state. | | `provides` | `string[]` | -- | Fields this tool is authoritative to provide. | `effect` is optional by default, but a project can opt into a **strict realtime effect metadata mode** at compile time. Under that mode, every non-system tool must declare an explicit `effect` — an undeclared one is a compile error (`REALTIME_TOOL_EFFECT_REQUIRED`): *"Tool must declare an explicit effect (read\_only, state\_write, or side\_effect) for strict realtime effect metadata mode."* This lets the compiler safely select the guarded scheduler lane a tool runs on. Strict mode is off by default for backward compatibility; declare `effect` on every tool to stay compatible with it regardless. When neither `effect` nor `hints.side_effects` is declared, the runtime infers one: for an HTTP tool, `GET`, `HEAD`, and `OPTIONS` default to read-only and every other method defaults to side-effecting; SearchAI and read-only table bindings default to read-only; any other non-HTTP tool defaults conservatively to side-effecting. An explicit `effect`/`side_effects` always wins over the inferred default. ### Result compaction The per-tool `compaction:` block controls how this tool's results are compacted in conversation history (independent of the agent-wide `EXECUTION.compaction` policy). ```yaml theme={null} TOOLS: search_catalog(query: string) -> {items: object[]} type: http endpoint: "/api/catalog/search" method: GET compaction: essential_fields: [items.id, items.name, items.price] max_description_length: 500 summary_timeout_ms: 3000 ``` | Property | Type | Description | | - | - | - | | `essential_fields` | `string[]` | Fields to preserve verbatim when compacting this tool's result. | | `max_description_length` | `number` | Maximum characters kept for descriptive fields. | | `summary_timeout_ms` | `number` | Timeout for LLM summarization of this tool's result. | | `max_search_tokens` | `number` | Token budget for SearchAI knowledge-base result context sent to the LLM. | ### MODEL\_CONTEXT on a tool result An optional `MODEL_CONTEXT` block on a tool chooses the view of its result supplied to **later model calls** — independent of [result compaction](#result-compaction) above, which is an automatic LLM-assisted summarization fallback for oversized results. `MODEL_CONTEXT` is an author-declared, deterministic replacement that always applies, regardless of result size. ```yaml theme={null} TOOLS: list_products(category: string) -> object type: http endpoint: "/api/products" method: GET model_context: fields: [products[].id, products[].name, total] ``` | Property | Type | Description | | - | - | - | | `fields` | `string[]` | Result paths to keep, 1-64 entries. Paths may traverse arrays with `[]` (for example `products[].id`); each segment is an identifier, at most 16 segments deep. No path may be a prefix of (or overlap) another. | | `template` | `string` | A text template (`{{}}` interpolation against `result`) rendered instead of the raw result — for example `"{{result.total}} products found"`. | `fields` and `template` are mutually exclusive — choose one. The full, unprojected tool result is still available everywhere else: `CALL AS`, `ON_RESULT`, assignments, response templates, and client delivery. Omit `MODEL_CONTEXT` entirely to keep the historical full-result behavior. An invalid or empty policy (both or neither of `fields`/`template`, an empty/overlapping `fields` list, or an empty `template`) fails validation (`MODEL_CONTEXT_INVALID`); if a *valid* policy can't be projected at runtime (a path doesn't resolve, say), the model receives a bounded "unavailable" marker instead of the full result — it never silently falls back to exposing everything. A rendered, non-empty view is **preserved exactly** through later history compaction and summarization, evaluated once and reused on later turns rather than re-rendered — see [MODEL\_CONTEXT on a response](/agent-platform/abl-reference/flow#model_context-on-a-response) for the full preservation contract and the `AUTHORED_MODEL_CONTEXT_CAPACITY_EXCEEDED` capacity limit, which applies to both forms identically. Legacy `.agent.abl` format: `MODEL_CONTEXT:` with nested `FIELDS:`/`TEMPLATE:` keys, directly under the tool signature. `MODEL_CONTEXT` can also sit beside a FLOW step's `RESPOND` or a `COMPLETE` condition's `RESPOND` — see [MODEL\_CONTEXT on a response](/agent-platform/abl-reference/flow#model_context-on-a-response) for that (separate) use. ### Input/output transforms (`process`) The `process:` block defines pure input (`before`) and output (`after`) transform stages that run around the tool call — remapping parameters, enforcing preconditions, and reshaping results. ```yaml theme={null} TOOLS: quote_fx(amount: number, from: string, to: string) -> {converted: number} type: http endpoint: "/api/fx/quote" method: POST process: before: params: amount_cents: "amount * 100" require: [amount > 0] on_fail: reask("Please provide a positive amount.") after: result: converted: "result.amount_cents / 100" on_fail: warn("Could not normalize the quote.") ``` `before` supports `params` (computed parameter map), `require` (precondition expressions), `on_fail` (one of `block("msg")`, `reask("msg")`, `warn("msg")`, `use_original`, `fallback("msg")`), and `use` (a named transform). `after` supports `result` (output remapping) and `on_fail`. ### Caching hints Use the `cacheable` hint to indicate that a tool's results can be cached. ```yaml theme={null} TOOLS: get_exchange_rate(from: string, to: string) -> {rate: number} description: "Get current exchange rate" type: http endpoint: "/api/fx/rate" method: GET hints: cacheable: true latency: fast ``` When `cacheable: true`, the runtime may cache results for identical parameter combinations. When `cacheable: false`, results are never cached (use this for tools that return time-sensitive data). ### PII access The `pii_access:` property declares what level of personally identifiable information (PII) a tool can access. ```yaml theme={null} TOOLS: verify_identity(ssn_last4: string, dob: string) -> {verified: boolean} description: "Verify customer identity" type: http endpoint: "/api/identity/verify" method: POST pii_access: user ``` | Value | Description | | - | - | | `original` | Tool can access the original, unredacted PII values (broadest access). | | `tools` | Tool can access PII data from other tool results | | `user` | Tool can access user-provided PII | | `logs` | PII from this tool may appear in logs | | `llm` | PII from this tool is sent to the LLM | ### Secret scrubbing An always-on scrubber masks credentials (API keys, bearer tokens, private keys, passwords) in a tool's output. Set `scrub_secrets: false` to opt a specific tool out (for example, when the tool's job is to return a token your flow needs). This is independent of `pii_access`, which governs PII rather than credentials. ```yaml theme={null} TOOLS: mint_token(scope: string) -> {token: string} type: http endpoint: "/api/token" method: POST scrub_secrets: false ``` ### Context access The `context_access:` block declares which session context variables a tool reads from and writes to. This enables the runtime to automatically inject context into HTTP requests and apply result values back to session state. ```yaml theme={null} TOOLS: update_profile(name: string) -> {updated: boolean} description: "Update user profile" type: http endpoint: "/api/profile" method: PUT context_access: read: [user.id, user.email] write: [user.name, user.updated_at] ``` `context_access` is validated against your `MEMORY:` declarations. A tool that declares a `read` of a **write-only** memory path (or a `write` of a **read-only** path) now fails compilation, and at runtime a tool is blocked from executing if its declared memory is unavailable. Declare the matching `MEMORY:` access mode for every path a tool reads or writes. A `read` key that is only ever populated by this **same tool's own** `on_result`/`on_error` writes can never be satisfied on the tool's first call in a session — that key doesn't exist yet, so the runtime fails the call closed every time. The compiler warns `TOOL_CONTEXT_ACCESS_READ_BEFORE_WRITE` for this. The key needs a producer other than the tool itself: another tool, `ON_START`, a FLOW `SET`, or a `GATHER` field. ### Store result The `store_result:` property controls whether the raw tool result blob is stored in the session context. Defaults to `true`. ```yaml theme={null} TOOLS: log_event(event: string) -> {logged: boolean} description: "Log an audit event" type: http endpoint: "/api/audit" method: POST store_result: false ``` ### Reuse secrets across tools (SecretRef) Some flows need a credential produced by one tool (a one-time token, session key, or OTP) to be sent to a later tool — without ever exposing the raw value to the LLM. SecretRef captures such values into an encrypted, session-scoped **secret vault** and restores them into downstream tool calls. * **Producer** — the tool that returns the secret declares a `RESULT_SECRETS:` block. Each entry captures a result field into the vault under a key, in the form ` -> (options)`. Capture works on **every tool type** — HTTP, code sandbox, and `workflow` tools can all produce vaulted secrets. * **Consumer** — a downstream tool declares a `SECRET_ACCESS:` block listing the vault keys it may read, then references them with the `{{_secret.}}` placeholder in its `headers` or `body_template`. Only **HTTP tools** can consume secrets — `{{_secret.}}` resolves in the HTTP request path alone. Declaring `SECRET_ACCESS` on a non-HTTP tool (a `workflow` or code-sandbox tool) is a **compile error** (`SECRET_REF_UNSUPPORTED_CONSUMER_TYPE`): the executor cannot resolve `{{_secret.}}`, so the single-use vault entry would be consumed without delivering the value, and the next authorized consumer would fail with "already used". If you reached for a workflow tool to assemble a variable-length batch payload from a secret, build it directly in an HTTP tool with `{{#each input.}}` in the `body_template` instead. (`RESULT_SECRETS` capture is unaffected and still works on workflow tools.) ```yaml expandable=true theme={null} TOOLS: # Producer: capture the OTP token into the vault, never exposing it to the model request_otp(account_id: string) -> {otp_token: string, expires_in: number} type: http endpoint: "/api/otp/request" method: POST RESULT_SECRETS: otp_token -> txn_otp (consumers: [convert_transaction_to_emi], ttl: 300, single_use: true) # Consumer: restore the vaulted secret into a header — the model never sees it convert_transaction_to_emi(txn_id: string) -> {status: string} type: http endpoint: "/api/txn/convert" method: POST SECRET_ACCESS: READ: [txn_otp] headers: X-OTP-Token: "{{_secret.txn_otp}}" ``` #### `RESULT_SECRETS` entry options | Option | Type | Description | | - | - | - | | `` | `string` | Result field to capture (left of `->`). | | `` | `string` | Vault key to store it under (right of `->`). | | `consumers` | `string[]` | Tool names allowed to restore this secret. Omit to allow any consumer that declares `SECRET_ACCESS`. | | `ttl` | `number` | Time-to-live in seconds before the vaulted secret expires. | | `single_use` | `boolean` | When `true`, the secret is consumed (invalidated) after its first use. | SecretRef is **fail-closed**: the vault is invisible to the LLM, `{{_secret.X}}` resolves only in `headers` and `body_template` (never forwarded as a query/body param on its own), and an unresolved reference substitutes an empty string — the literal `{{_secret.X}}` placeholder is never sent upstream as a credential. ## Inbuilt tools Inbuilt tools are tools that the Artemis runtime provides. They cover voice transfers, IVR and DTMF, human-agent handoff, and attachment handling. These tools do not require an HTTP endpoint or a Tool Library entry for them. They are available to all agents by default. To use these tools, declare them in the `TOOLS:` section of your agent, with its input fields and return type. Then use them as required in the agent. For the full list of built-in tools and how to use them, see [Inbuilt tools](/agent-platform/inbuilt-tools/index). *** **Related articles:** * [Language overview](/agent-platform/abl-reference/language-overview) -- file structure and syntax. * [Agent declaration](/agent-platform/abl-reference/agent-declaration) -- `tool_timeout` and model settings in EXECUTION. * [GATHER](/agent-platform/abl-reference/gather) -- information collection (often used alongside tools). * [FLOW](/agent-platform/abl-reference/flow) -- `CALL` action for invoking tools within flow steps. * [Inbuilt tools](/agent-platform/inbuilt-tools/index) -- Inbuilt tools. # Advanced settings Source: https://koreai.mintlify.app/agent-platform/administration/advanced-settings The **Advanced Settings** page configures advanced execution parameters for a project. Use it to change the project slug, control LLM reasoning behavior, customize the system prompts that drive data collection, edit escalation messages, snapshot settings versions, and delete the project. **Navigation**: **Project** -> **Settings** -> **Advanced** ## Slug The slug is the URL-safe identifier for the project. The platform uses it in project URLs and in any endpoint or integration that references the project by name. | Field | Description | | - | - | | **Slug** | The project identifier. Edit the value and click **Save** to apply the change. | Editing the slug breaks existing URLs registered in third-party systems. Update every webhook, channel configuration, and external integration that references the old slug before you save. *** ## LLM thinking Thinking controls whether the LLM produces explicit reasoning before it calls a tool. Reasoning improves traceability in session traces, but it consumes additional tokens on every tool call. | Setting | Description | Default | | :- | - | - | | **Enable Thinking** | When enabled, the LLM provides detailed reasoning (thought) for each tool call. This improves traceability but increases token usage. | Disabled | | **Thinking Budget** | Maximum number of tokens the LLM can spend on reasoning. Accepts a value from 128 to 32,768. | 1,024 | | **Thought Description** | Instruction that the platform appends to tool descriptions to guide the LLM's reasoning output. Use it to shape what the model explains, such as why it chose a particular action. | Empty | **Thinking Budget** and **Thought Description** apply only when you enable **Enable Thinking**. A higher budget produces longer reasoning traces and raises per-call token cost. *** ## LLM task prompts The platform uses a set of system prompts to handle entity extraction, correction detection, validation, and inference during data collection. Each prompt ships with a default. Expand a prompt row to read the default text, then edit it to change how the model behaves for that task in this project. | Prompt | Description | | - | - | | **Entity Extraction Prompt** | System prompt for extracting field values from user messages during data collection. | | **Correction Detection Prompt** | Detects when a user corrects a previously provided value. The prompt receives the currently collected values as context. | | **Field Validation Prompt** | Validates extracted values against field rules and constraints before the platform accepts them. The prompt returns a JSON object that reports whether the value is valid. | | **Field Inference Prompt** | Infers the most likely values for missing fields from the collected context. The prompt returns a value only when confidence is high. | The platform marks **Entity Extraction Prompt** as deprecated with inline gather. Projects that use inline gather don't rely on this prompt. Engineering needs to confirm the replacement mechanism and whether the prompt still applies to projects built before inline gather. Edit these prompts only when the default behavior doesn't fit your data collection flow. Changes apply to every agent in the project. *** ## Escalation templates Customize the escalation confirmation messages that users see when a conversation hands off to a human agent. Each channel type has its own template: | Channel | Description | | - | - | | **Digital Channel** | Appears in web chat and messaging apps. Supports markdown formatting and emoji. | | **Voice Channel** | TTS reads it aloud on voice or phone channels. Keep it short and natural. | | **Plain Text** | Fallback for SMS and channels without rich formatting support. | Each template supports `{{reason}}` and `{{priority}}` variables that the system substitutes at runtime with the actual escalation reason and priority level. *** ## Danger zone This archives the project immediately and permanently deletes it after a 30-day grace period. You can restore it from **Archived Projects** before then. *** ## Settings versions After you change a settings and save it, you can create a versioned snapshot of all project settings. Click **Create Version** to save the current settings state. These versions enable deployment pinning, allowing you to associate a specific settings version with a deployment environment. For example, you can keep tweaking configurations and promote the versioned snapshot to staging deployment to test the project. Once satistied with the configuration, you can promote the specific versioned snapshot to active deployment. When you [export a project](/agent-platform/projects#export-project), you can include or exclude the version history. *** # Agent behavior settings Source: https://koreai.mintlify.app/agent-platform/administration/agent-behavior-settings Agent behavior settings let you configure behavior profiles, alerts, agent transfer, call recording, API and widget data access, attachments, omnichannel support, and governance policies. | Settings | Description | | - | - | | [Behavior profiles](#behavior-profiles) | Configure overrides for agent speaking, listening, and interaction behavior. | | [Alert settings](#alert-settings) | Configure rules that trigger notifications when metrics breach thresholds. | | [Agent transfer](#agent-transfer) | Manage defaults for transfer sessions, routing, voice gateway, and PII handling. | | [Recording](#recording) | Manage call recordings, playback, retention, and compliance. | | [Public API Access](#public-api-access) | Configure external API access permissions and integration endpoints. | | [Public Widget Data](#public-widget-data) | Manage what session and agent data is exposed to widgets. | | [Attachments](#attachments) | Configure policies for file uploads, storage, and handling in conversations. | | [Omnichannel](#omnichannel) | Configure multi‑channel support and unified agent behavior across platforms. | | [Governance](#governance) | Define compliance, audit, and administrative oversight policies. | ## Behavior profiles The settings on the **Behavior Profiles** page let you define context-driven overrides for agent conversation behavior. Profiles in addition to any agent baseline when the defined conditions match, but don't modify the core definition of the agent. **Navigation**: **Project** → **Settings** → **Behavior Profiles** ### Create a behavior profile #### Create profile Selecting this option opens an editor. | Field | Description | | - | - | | **Profile Name** | A unique identifier for the profile (for example, `voice_support`). | | **Priority** | Numeric priority. Lower values take precedence when multiple profiles match. | | **WHEN Expression** | A boolean expression that determines when this profile activates (for example, `true` for always-on, or a context variable check). | Below the header, the profile editor organizes settings into three behavior sections: #### Speaking | Setting | Description | | - | - | | **Style** | Response style (for example, warm and concise). | | **Tone** | Response tone (for example, reassuring). | | **Emotion** | Emotional register (for example, calm). | | **Pace** | Response pacing (for example, steady). | | **Language Policy** | Language selection policy (**Inherit**, **Fixed**, or **Detect**). | | **Fixed Language** | The language code when **Language Policy** is **Fixed** (for example, `en-US`). | | **Max Sentences** | Maximum number of sentences per response. | | **One Thing at a Time** | Whether the agent addresses one topic per turn. | | **Tool Lead-in** | How the agent introduces tool invocations. | | **Tool Results Style** | How the agent presents tool results to the user. | | **Tool Result Points** | Number of key points to extract from tool results. | | **Internal Handoffs** | Behavior when handing off to another agent. | | **Human Handoffs** | Behavior when handing off to a human operator. | #### Listening | Setting | Description | | - | - | | **Barge-In** | How the agent handles user interruptions mid-response. **Inherit** uses the setting configured at a higher level. | | **On Pause** | Behavior when the user pauses mid-utterance. **Inherit** uses the setting configured at a higher level. | | **On Overlap** | Behavior when the user and the agent speak at the same time. **Inherit** uses the setting configured at a higher level. | | **Unclear Audio** | How the agent handles unclear or noisy input. **Inherit** uses the setting configured at a higher level. | | **Self-Correction** | How the agent detects and handles user self-corrections. **Inherit** uses the setting configured at a higher level. | | **No-Input Handling** | Behavior when the user doesn't respond within the expected timeout window. | #### No-input handling configuration | Field | Description | | - | - | | **Enabled** | Turns no-input handling on or off. **Use project default** inherits the setting configured at the project level. [Learn more](/agent-platform/abl-reference/behavior-profiles#voice-turn-taking). | | **Mode** | Controls how the agent handles no-input retries. **Use project default** inherits the project-level setting. **Dynamic** additionally exposes **Order**, **Play Style**, **Messages**, and **Generation**. | | **Timeout (ms)** | How long the agent waits for user input before treating the turn as no-input. Default: 5000 ms. | | **Max Retries** | Number of times the agent re-prompts the user after a no-input timeout before moving to the exhausted behavior. Default: 3. | | **On Exhausted** | Behavior once **Max Retries** is reached. **Use project default** inherits the setting configured at the project level, **Hangup** ends the conversation, and **Custom** plays a custom message to the user. | | **Relaxed Interval (ms)** | Timeout duration used for retries after the first, typically longer than the initial timeout to be more lenient. Default: 15000 ms. | | **Order** | The order in which the agent plays no-input messages across retries (for example, **Random**). | | **Play Style** | How the agent plays a no-input message to the user. **Use project default** inherits the project-level setting. | | **Messages** | Select **+ Add message** to configure one or more no-input prompts the agent can play to the user. | | **Generation** *(Dynamic mode only)* | Configures AI-generated no-input messages, as an alternative or a supplement to authored messages: **Enabled** (turns generated messages on or off, **Inherit** uses a higher-level setting), **Generation Model** (model used to generate the message, **Runtime default model** uses the platform default), **Prompt Library Prompt ID** (ID of the prompt used, runtime uses its active version), and **Max Output Tokens** (maximum length of the generated message, default 150). | This is only supported for pipeline voice using KoreVG. **Interaction** | Setting | Description | | - | - | | **Answer Shape** | The structure of responses (for example, direct, exploratory). | | **Detail** | Level of detail in responses. | | **Initiative** | How proactively the agent guides the conversation. | | **Grounding** | How the agent establishes shared understanding. | | **Clarification** | When and how the agent asks clarifying questions. | | **Max Clarification Questions** | Maximum number of clarification questions per turn. | | **Assume When Low Risk** | Whether the agent assumes intent when the risk of misunderstanding is low. | | **Confirm Parameters** | Whether the agent confirms parameters before executing actions. | | **Confirm Actions** | Whether the agent confirms actions before performing them. | | **Uncertainty** | How the agent communicates uncertainty. | | **Offer Next Steps** | Whether the agent proactively suggests next steps. | | **Empathy** | Level of empathetic language in responses. | | **On Correction** | Behavior when the user corrects the agent. | | **On Confusion** | Behavior when the user appears confused. | | **On Misheard** | Behavior when the system misrecognizes audio input. | | **Repair Attempts** | Maximum number of repair attempts before escalation. | | **Avoid Re-asking** | Whether the agent avoids repeating questions already answered. | | **Remember Constraints** | Whether the agent tracks and respects stated constraints across turns. | | **Closure** | How the agent closes a conversation or topic. | **Authoring Note**: A free-text field for documenting when and why this profile applies. All settings default to **Inherit**, meaning the agent's base configuration applies. Override any setting to customize behavior for the profile's conditions. Select **Save** to create the profile, or switch to **Raw DSL** to edit the profile definition as code. *** ## Alert settings The **Alert Settings** page defines rules that trigger notifications when project metrics breach a threshold. The runtime evaluates each rule on a fixed cadence and delivers a notification by email or webhook when the condition matches. **Navigation**: **Project** → **Settings** → **Alert Settings** When the project has no rules yet, the page shows an empty state. Select **Create alert rule** to open the rule editor, or **Browse available metrics** to review every metric the evaluator can alert on before you start. Typical uses include containment rate drops, error spikes, latency increases, and sentiment degradation. ### Create an alert rule Select **Create alert rule** to open the **Create alert rule** panel. The footer tracks what the rule still needs (for example, `Still needed: name, metric, recipients`) and keeps **Save rule** available once you supply those values. Select **Cancel** to discard the rule. | Field | Description | Default | | - | - | - | | Rule name (required) | A descriptive name for the rule (for example, *LLM latency p95 too high*). | Empty | | Evaluating | Controls whether the evaluator picks the rule up on its next cycle. Turn it off to keep the rule in the list but paused. | Enabled | | Subject | The metric family the rule watches: Agent, LLM, Tool, Voice, or Others. The subject determines which metrics the Metric list offers and whether a scope selector appears. | Agent | | Agent (Agent subject only) | Limits the rule to a single agent. Leave it as *Any agent (project-wide)* to watch the whole project. | Any agent (project-wide) | | LLM (LLM subject only) | Limits the rule to a single LLM. Leave it as *Any LLM (project-wide)* to watch the whole project. | Any LLM (project-wide) | | Metric (required) | The metric the rule evaluates. The list reflects the selected subject. Selecting a metric reveals its allowed aggregation and, where supported, a channel filter. | Select a metric | | Aggregation | The statistical aggregation applied to the metric within the window (for example, avg, p95, sum). Options depend on the selected metric. Only appears once a metric is selected. | Varies by metric | | Channel | Restricts the rule to a specific channel. Only appears when the selected metric supports channel filtering. | Any channel | | Condition | The comparison the evaluator applies between the metric value and the threshold (for example, `Less than <` | `Less than <` | | Threshold (required) | The value the metric must cross for the rule to fire. Use the stepper controls or enter a value directly. May display a unit suffix tied to the metric (for example, *score*). | 0 | | Window (required) | How far back the rule looks when it evaluates the metric, in minutes. | 10 min | | Evaluate every (required) | How often the evaluator runs the rule, in minutes. | 10 min | | Cooldown (required) | How long the rule stays quiet after it fires, in minutes. Use this to suppress repeat notifications for an ongoing condition. | 10 min | | Deliver via | The delivery channel for the notification: Email or Webhook. | Email | **If Deliver via : Email:** | Field | Description | Default | | - | - | - | | Recipients (required) | Destinations for the notification. Separate email addresses with commas or new lines. Every recipient receives the alert. | Empty | **If Deliver via : Webhook:** | Field | Description | Default | | - | - | - | | Webhook URL (required) | Destination URL the platform calls when the rule fires. | Empty | | Auth profile | Authentication method for the webhook call: `api_key`, `bearer`, or `basic`. | None (no auth header) | ### Subject scopes | Subject | What the rule watches | Scope selector | | - | - | - | | **Agent** | LLM metrics attributed to agent activity. | **Agent**, defaulting to all agents in the project. | | **LLM** | LLM metrics across the project, independent of the calling agent. | **LLM**, defaulting to all LLMs in the project. | | **Tool** | Tool execution volume, failures, and latency from platform events. | None. The rule applies project-wide. | | **Voice** | Voice session, TTS, ASR, and connection metrics from platform events. | None. The rule applies project-wide. | | **Others** | Trace records and conversation sentiment metrics. | None. The rule applies project-wide. | ### Available metrics #### Agent and LLM metrics The **Agent** and **LLM** tabs list the same LLM metrics group. The **Agent** tab scopes each metric to agent activity, and the **LLM** tab scopes it to the LLM. All metrics in this group read from the **LLM metrics** source and support no channel or tool filters. | Metric | Category | Status | Description | Aggregations | | - | - | - | - | - | | **LLM latency** | Operational | Existing | Per-call latency from the LLM metrics stream. | p95, p99, avg, max, min | | **LLM cost (window total)** | Operational | Existing | Sum of estimated LLM cost over the window. | sum, avg, max, min, p95, p99 | | **Total tokens** | Operational | Existing | Tokens consumed over the window. | sum, avg, p95, p99, max, min | | **LLM error rate** | Error | Existing | Share of LLM calls in the window that failed. | | #### Tool metrics Tool metrics read from the **Platform events** source. All three support channel filters, channel connection scope, and tool name and type filters. | Metric | Category | Status | Description | Aggregations | | - | - | - | - | - | | **Tool execution count** | Operational | New | Total completed or failed tool calls in the window. | Fixed expression | | **Tool error count / rate** | Error | New | Failed tool calls. The notification includes failed calls divided by terminal calls. | Fixed expression | | **Tool latency** | Operational | New | Per-call tool execution latency. | p95, p99, avg, max, min | #### Voice metrics Voice metrics read from the **Platform events** source. All support channel filters and channel connection scope, and none support tool name and type filters. | Metric | Category | Status | Description | Aggregations | | - | - | - | - | - | | **Voice session errors** | Error | New | Voice sessions that ended because of a terminal technical error. | Fixed expression | | **TTS failure count** | Error | New | Text-to-speech operations whose delivery ended in failure. | Fixed expression | | **Avg ASR confidence** | Quality | New | Average confidence from speech recognitions that reported a real score. | Fixed expression | | **Voice connection latency** | Operational | New | Time required to establish a voice gateway session. | p95, p99, avg, max | #### Other metrics The **Others** tab groups trace and sentiment metrics. **Trace records** | Metric | Category | Status | Description | Aggregations | Filters | | - | - | - | - | - | - | | **Trace duration** | Operational | Existing | End-to-end trace duration from `spatial_trace_records`. | p95, p99, avg, max, min | None | | **Slow trace count** | Operational | New | Number of root traces exceeding the configured duration threshold. Takes the `slow_ms` parameter. | Fixed query | None | **Conversation sentiment** | Metric | Category | Status | Description | Aggregations | Filters | | - | - | - | - | - | - | | **Conversation sentiment** | Quality | Existing | Average sentiment across conversations in the window. | avg, min, max, p95 | Channel filter | | **Frustration rate** | Quality | Existing | See note | See note | See note | *** ## Agent transfer The **Agent Transfer Settings** page configures defaults for agent transfer sessions, routing, voice gateway, and PII handling. These settings apply project-wide unless an individual agent or workflow overrides them at runtime. **Navigation**: **Project** → **Settings** → **Agent Transfer** The page organizes content into three collapsible sections. Select a section header to expand or collapse it. Select **Save** to apply changes or **Reset** to restore all values to defaults. | Section | Description | | - | - | | **Default Routing** | Default connection, queue, priority, and post-agent behavior. | | **Voice Gateway** | Voice gateway provider, SIP transfer method, and SIP or PSTN transfer targets. | | **PII Handling** | PII de-tokenization and detection pattern for transferred messages. | ### Default routing Default Routing sets the fallback connection, queue, priority, and post-transfer behavior that the platform uses when an agent initiates a transfer. Individual agents or workflows can override these values at runtime. | Setting | Description | Default | | - | - | - | | **Connection** | Agent desktop connection for transfers. Select a configured connection or leave as **None (use agent‑level config)** to defer to the agent's own setting. To add a new connection, select **+**, and select a supported provider from the dialog window. | None (use agent‑level config) | | **Queue** | Default queue ID for routing. The platform routes transferred conversations to this queue unless the agent specifies a different one. | `default` | | **Priority** | Default priority level for transferred conversations. Scale: 0 (lowest) to 10 (highest). | 5 | | **Post-Agent Action** | What happens after the human agent ends the conversation. **Return to bot** hands control back to the AI agent. **End session** terminates the session entirely. | Return to bot | ### Voice Gateway Voice Gateway configures transfer behavior for voice channels. These settings determine how the platform hands off calls to the agent desktop over SIP or PSTN. | Setting | Description | Default | | - | - | - | | **Gateway Type** | Voice gateway provider. Determines the SIP infrastructure the platform uses for call transfers. | Kore Voice Gateway | | **Transfer Method** | SIP transfer method for voice handoffs.
- **SIP INVITE** - sends a new SIP INVITE to establish a direct call leg with the agent's endpoint.
- **SIP REFER** - instructs the caller's endpoint to connect directly to the agent.
- **SIP BYE (release-link)** - ends the platform's call leg and releases the caller to a call already linked with the agent. | SIP REFER | | **Header Passthrough** | Forward custom SIP headers on outbound call transfers. When off, no custom headers are sent. | Enabled | | **SIP Transfer Target URI** | Project fallback SIP URI used when a call transfer doesn't provide an explicit SIP target (for example, `sip:agent@example.com`). | Empty | | **PSTN Transfer Target Number** | Phone number in E.164 format (for example, `+14155550123`) dialed when a call transfer uses PSTN. Transferred calls use the provisioned outbound caller ID instead of the original caller's phone number. | Empty | | **SIP URI is SmartAssist trunk** | Run SmartAssist transcript sync and recording sync after calls transferred to this SIP URI finish. | Disabled | | **Send Conversation Summary in SIP Header** | Includes an AI-generated conversation summary in the User-to-User (UUI) SIP header when transferring voice calls. Contact centers that support the UUI header can display the summary to agents. This setting is disabled by default and does not affect existing configurations until enabled. | Disabled | If SIP settings are also defined in the agent DSL, the runtime resolves the transfer destination using the following priority order: the agent-defined default, then the project setting. If neither is available, the transfer fails. Learn how to define [Trusted SIP Transfer Destinations](/agent-platform/abl/reference/tools#trusted-sip-transfer-destinations) in ABL. ### PII handling PII Handling controls how the platform treats personally identifiable information when transferring a conversation to a human agent. | Setting | Description | Default | | - | - | - | | **De-tokenize Before Transfer** | When enabled, the platform replaces PII tokens with their original plaintext values before sending messages to the agent desktop. Treat this as reveal-class access and enable it only for explicitly authorized destinations. | Enabled | | **Detection Pattern** | Regex pattern the platform uses to detect PII tokens in messages. The default pattern matches the `{{pii.*?}}` token format. | `\{\{pii\..*?\}\}` | Enabling **De-tokenize Before Transfer** exposes raw PII to the agent desktop. Only enable this for destinations where human agents have explicit authorization to view personally identifiable information. *** ## Public API access The **Public API Access** page configures which APIs end-users can access when authenticating through their organization's identity provider (Azure AD, Okta, Google). **Navigation**: **Project** → **Settings** → **Public API Access** ### Query API Toggle the **Query API** to allow authenticated end-users to query agents through the public API endpoint. When you enable it, the following configuration fields appear: | Field | Description | | - | - | | **Identity Providers (Auth Profiles)** | Select one or more OIDC-compatible auth profiles (**OAuth 2.0 App** or **Azure AD**). End-users authenticate through their organization's IdP. | | **Allowed Email Domains** | Comma-separated list of email domains that can authenticate. Leave empty to allow all domains. | | **Allowed Origins (CORS)** | Optional. A comma-separated list of browser origins that are allowed to make API requests. If this field is not specified, the API does not perform origin validation. If one or more origins are configured, origin validation is enforced for all incoming API requests, and requests from origins not included in the list are rejected. | | **Allowed Redirect URIs (OAuth Flow)** | Comma-separated full URIs where OAuth redirect responses can go. Exact match only — no wildcards. | **Session token duration** | Field | Description | Default | | - | - | - | | **Session Token TTL (seconds)** | How long search session tokens remain valid (60–3600 seconds). | 900 (15 min) | | **Per User (req/min)** | Maximum API requests per user per minute. | — | | **Per Project (req/min)** | Maximum API requests per project per minute. | — | *** ## Public Widget Data The **Public Widget Data** page controls the tables, columns, and SQL constructs that saved widgets can expose through the `analytics:read` scope. Projects start with sensitive analytics sources blocked, and all SQL constructs beyond a basic single SELECT stay disallowed until you enable them. Click **Save policy** to apply your changes. **Navigation**: **Project** -> **Settings** -> **Public Widget Data** ### SQL validation Choose which saved-widget SQL constructs the platform allows for `analytics:read` queries. DDL and mutation statements remain blocked regardless of these settings. | Setting | Description | | - | - | | **SQL comments** | Allows saved SQL widgets to include comments in query text. | | **Multiple SELECT statements** | Allows saved SQL widgets to run more than one SELECT, such as set-operation queries. | | **JOIN clauses** | Allows saved SQL widgets to join allowlisted analytics tables. | | **UNION, INTERSECT, EXCEPT** | Allows saved SQL widgets to combine allowlisted analytics queries with set operations. | | **CTE clauses** | Allows saved SQL widgets to use WITH clauses over allowlisted analytics tables. | | **OR conditions** | Allows saved SQL widgets to use OR inside WHERE clauses. The runtime still applies tenant and project scope outside the expression. | ### Restricted data The **Restricted data** section lets you block specific analytics tables and columns from saved widgets. Every project contains a default configuration that restricts a few sensitive sources. You can remove a default restriction or add your own. After updating the restrictions, save the policy. **Block a table** 1. Choose a table from the **Select a table** menu. 2. Click **Block table**. Blocked tables appear as removable tags. Click the delete icon on a tag to lift the restriction. The default blocked tables are: * `abl_platform.audit_events` * `abl_platform.eval_conversations` * `abl_platform.eval_scores` * `abl_platform.hallucination_evaluations` * `abl_platform.knowledge_gap_evaluations` * `abl_platform.llm_evaluate` * `abl_platform.search_queries` * `abl_platform.spatial_trace_records` * `abl_platform.custom_pipeline_results` * `abl_platform.workflow_events` **Block a column** 1. Choose a scope from the table menu. Select **All tables** to block the column everywhere it appears, or select a specific table to block it there only. 2. Choose a column from the **Select a column** menu. 3. Click **Block column**. Blocked columns appear as removable tags, grouped by name of the table. The following columns are blocked by default across all tables: * `attributes` * `content` * `conversation_json` * `evidence` * `metadata` * `output` * `payload` * `prompt` * `query_text` * `trace_events` *** ## Attachments The **Attachment Settings** page controls how files uploaded during chat sessions are accepted and processed for this project. **Navigation:** **Project** → **Settings** → **Attachments** **General** | Setting | Description | Default | | - | - | - | | **Enable Attachments** | Allows users to upload files during chat sessions. Disable this setting to prevent file uploads for the project. | Enabled | **Upload Limits** | Setting | Description | Default | | - | - | - | | **Maximum File Size** | Maximum size allowed for a single uploaded file. Files larger than this limit are rejected. | 20 MB | | **Allowed File Types** | MIME types that users can upload. You can configure up to **50** MIME types. | Default types listed below. | **Default Allowed File Types** The following MIME types are allowed by default: * **Images:** `image/jpeg`, `image/png`, `image/gif`, `image/webp` * **Documents:** `application/pdf`, `application/msword`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document`, `application/rtf` * **Spreadsheets:** `application/vnd.ms-excel`, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` * **Presentations:** `application/vnd.ms-powerpoint`, `application/vnd.openxmlformats-officedocument.presentationml.presentation` * **OpenDocument formats:** `application/vnd.oasis.opendocument.text`, `application/vnd.oasis.opendocument.spreadsheet`, `application/vnd.oasis.opendocument.presentation` * **Text formats:** `text/plain`, `text/markdown`, `text/csv`, `text/tab-separated-values`, `text/html`, `application/xml`, `application/json` * **Audio:** `audio/mpeg`, `audio/wav`, `audio/webm` * **Video:** `video/mp4`, `video/webm` **Processing** **PII Policy**: Determines how the platform handles files containing Personally Identifiable Information after file contents have been extracted. * Redact (Default): Detects and masks PII before the extracted content is indexed or made available to downstream services. * Block: Stops processing the file if PII is detected. * Allow: Processes the file without applying PII detection or redaction. PII policies are applied only when the platform extracts file contents (**Full** processing mode). They are not applied in **Metadata Only** or **Skip** mode. **Default Processing Mode**: Determines how newly uploaded files are processed before they are made available to the agent. * **Full (Default)**: Processes files and extracts content from supported file types. The extracted content is then shared with the models as context. The models receive content as per the file types. * Images: A resized version of the image is sent directly to the model as Base64. The model analyzes the image itself. No extraction is done by the platform. * Documents, audio, and video: The platform extracts the content and sends only the extracted text to the model. The raw file is not sent. In case of audio and video files, the transcripts are shared. In this mode, File metadata remains available through the attachment tools. Use this mode when the agent needs to read, search, and reason about uploaded content. * **Metadata Only**: Stores uploaded files without processing their contents. The model cannot access or understand the contents of the uploaded file. The model gets file metadata (name, size, download url). * **Skip**: Bypasses the platform's content extraction pipeline. This mode is only supported for image and pdf files. For supported types, the platform sends the original file directly to the selected LLM provider. Use this mode when you want to use the LLM provider's native document or vision capabilities instead of the platform's extraction pipeline. Supported only when the selected LLM provider and model support native file or vision input. **Points to Note** * Images in **Full** mode are sent directly to the model. Therefore, the selected model must support **vision**. * Documents, audio, and video in **Full** mode are converted into text before being sent to the model, allowing any text-capable model to process them. * **Metadata Only** and **Skip** do not extract file contents. As a result, PII policies are not applied in these modes. * In **Skip** mode, direct file transfer is currently supported only for **images** and **PDF** files. **Information** | Setting | Description | Default | | - | - | - | | **Max Files Per Session** | Maximum number of files that can be uploaded during a single chat session. This value is read-only. | 100 | *** ## Omnichannel The **Omnichannel** page configures cross-channel session continuity. **Navigation**: **Project** → **Settings** → **Omnichannel** Omnichannel settings allow users who start a conversation on one channel to continue it on another without losing context. **Conversation Recall** | Setting | Description | Default | | - | - | - | | **Enable cross-channel recall** | Allow sessions to transfer across channels. | Disabled | | **Maximum messages to recall** | Number of messages the platform carries over to the new channel. | 20 | | **Maximum age (days)** | How old a conversation can be and still qualify for recall. | 30 | **Allowed channels** Lists all supported channels (**web**, **voice**, **sms**, **whatsapp**, **email**, **slack**, **teams**) with toggles for cross-channel recall participation. **Identity Requirements** | Setting | Description | Default | | - | - | - | | **Require identity verification** | Whether the system requires identity verification for cross-channel recall. | Enabled | | **Minimum identity tier** | The minimum identity verification tier required to recall sessions. | 2 - Verified | **Consent** | Setting | Description | Default | | - | - | - | | **Require explicit consent** | Whether the user must explicitly consent before cross-channel recall. | Enabled | **Live Transcript Sync** Configure real-time transcript synchronization settings for cross-channel sessions. | Setting | Description | Default | | - | - | - | | **Enable live sync** | Turns on real-time transcript synchronization across channels during an active session. | Disabled | | **Join mode** | Controls how a secondary channel joins an in-progress session (for example, **Prompt** asks the user for confirmation before joining). | Prompt | | **Transcript mode** | Controls what gets synced to the joining channel (for example, **Final Only** syncs only finalized messages, not in-progress ones). Only configurable when **Enable live sync** is on. | Final Only | **Recent Continuity Activity** Shows operational events generated by cross-channel recall and live session continuity — for example, a session transferring channels or a live sync joining. Select **Refresh** to reload the list. If no sessions have used continuity features yet, the panel shows an empty state. Click **Save Settings** to apply changes. If you see a "Failed to save settings" error, verify that your role has write permissions for project settings. *** ## Governance The **Governance** page sets the production approval policy for this project. The policy controls how strictly the platform gates production deployments, so a stricter level requires more sign-off before an agent version can ship. **Navigation**: **Project** → **Settings** → **Governance** **Production approval policy** The page shows whether the policy is **Inherited (default)** or set explicitly for the project, along with the **Effective** level currently in force and its source (for example, Standard from the platform default). To set an explicit policy, drag the **Approval strictness** slider to a level, then click **Save policy**. | Level | Sign-off required | | - | - | | **Strict** | The strictest gate. Applies more sign-off than Standard before a version can ship. | | **Standard** | Requires approval, security, and eval sign-off. The publisher can approve their own version. | | **Open** | The most permissive gate. Applies less sign-off than Standard. | When you select a level, the page shows that level's sign-off requirements below the slider. Saving an explicit policy overrides the inherited platform default. # AI configuration Source: https://koreai.mintlify.app/agent-platform/administration/ai-configuration Configure LLM providers, model pricing, Arch settings, voice services, guardrails, and auth profiles. The **AI Configuration** settings control how your workspace connects to AI models and services. From here you register provider credentials, manage the model catalog, configure safety guardrails, and set up authentication profiles that tools and agents use at runtime. * **Navigation**: Settings > AI Configuration * **Required role**: Owner or Admin *** ## LLM providers The Agent Platform is provider-neutral. Agents can use models from multiple LLM providers, and you can switch between them without changing agent definitions. * **Navigation**: Settings > AI Configuration > LLM Providers * **Required role**: Owner or Admin The page is organized into three tabs: **Credentials**, **Model Catalog**, and **Project Access**. ### Credentials The **Credentials** tab lists all provider credentials registered in your workspace, grouped by provider. Each credential card shows the name, provider, creation date, and the number of models using that credential. **Add a credential:** 1. Go to **AI Configuration > LLM Providers > Credentials**. 2. Click **+ Add Credential**. 3. Enter a **Name** (for example, *Production OpenAI*), select a **Provider**, and enter the **API Key**. 4. Click **Add Credential**. API keys are encrypted at rest and never displayed in plaintext after the initial entry. **Delete a credential:** Click the delete icon on the credential card. Deleting a credential disconnects all models linked to it. Those models display a **No Keys** status in the Model Catalog until a new credential is connected. ### Model catalog The **Model Catalog** tab lists all models registered in your workspace. Each row shows the provider, model name, model ID, linked credentials, and status. **Workspace tier defaults** A model becomes a workspace default only when a workspace admin selects it here for a routing tier. It must be ready, active, inference-enabled, and backed by a Workspace-shared connection. Configured models may remain without a default, and a tier default never grants project access. Defaults are one-per-tier workspace catalog metadata - they don't grant or automatically apply project access. Each project must separately select an authorized binding in **Selected models** (see [Project Access](#project-access)). For each tier (**Fast**, **Balanced**, **Powerful**, **Voice**), the panel shows the tier's intended use, its associated operation tags (for example Fast: Extraction, Validation, Tool selection), and a **Workspace default** dropdown to assign a model. Below the tier defaults panel, the model table lists every registered model with its provider, model name and ID, connections, a star (favorite) icon, and **Ready** status. **Add a model from the catalog:** 1. Go to **AI Configuration > LLM Providers > Model Catalog**. 2. Click **+ Add Model**, browse or search for a provider model. 3. Review and configure the available model settings. 4. Click **Add to Workspace**. For more information, see [supported models](/agent-platform/supported-models). For information about configuring models from Microsoft Foundry, see [Microsoft Foundry](/agent-platform/administration/microsoft-foundry). **Add a custom model** 1. Go to **AI Configuration > LLM Providers > Model Catalog**. 2. Click **+ Add Model**, then select the **Custom Model** tab. 3. Fill in the following fields: | Field | Description | | - | - | | Display Name | A label for the model in the platform. | | Model ID | The model identifier, for example `gpt-4o` or `claude-sonnet-4-20250514`. | | Provider | The provider this model belongs to. | | Tier | Assign the model to a tier: Fast, Balanced, Powerful, or Voice. | | Temperature | Controls response randomness. Default: 0.7. Available fields vary by the selected model. | | Max Tokens | Maximum tokens the model can generate per response. Default: 4096. Available fields vary by the selected model. | | Endpoint URL | Optional. For custom or self-hosted endpoints. | Available fields in the Custom Model dialog depend on the selected model and provider - not every model exposes Temperature or Max Tokens. 4. Click **Add to Workspace**. **Configure model settings:** Expand any model row to view and edit its settings. **Connections** Shows credentials linked to this model, with connection visibility and health controls. * Click **+ Add Key** to connect a credential. In the **Add Connection** dialog, choose one of two connection methods: | Method | Description | | - | - | | **Credentials** | Select an existing credential from the **Credential** dropdown. | | **Use Auth Profile** | Select an **Auth Profile** instead of a direct credential. Credentials are resolved at runtime via the selected profile, which must have an **Active** status and a compatible auth type for this provider. | Optionally check **Set as primary connection**, choose **Connection visibility** (default: **Workspace-shared** - the compatibility-safe default; restrict a connection only when its project allowlist is known), then click **Create Connection**. * Each connected credential is listed with its name, type (for example **API Key**), and whether it's the **Primary** connection. Click **Test** to verify the connection, or the delete icon to remove it. * Change a connection's **Connection visibility** at any time and click **Save access** to apply. A model with no credentials shows **No Keys** status and can't serve requests. **Settings** - Configure Verbosity, Temperature, Top P, Max Completion Tokens, Compaction Threshold, Tier, OpenAI API Mode, and Response Mode. Available fields vary by provider and model capabilities - for example, reasoning models show **Verbosity** instead of Temperature and Top P. **Reasoning** For reasoning-capable models, this panel runs diagnostic checks and lets you set the reasoning effort: | Check | Description | | - | - | | **Connection and credentials** | Whether a working, credentialed connection is available. | | **Boolean thinking** | Whether the model supports a simple on/off thinking mode. Shows **Not applicable** for models that use effort levels instead. | | **Reasoning effort levels** | Whether the model supports configurable reasoning effort, and which levels it supports (for example: none, minimal, low, medium, high, xhigh). | Select the **Reasoning Effort** level from the dropdown to control how much reasoning the model applies before responding. **Capabilities** Enable supported capabilities: **Default for Tier**, **Tools**, **Vision**, **Streaming**, and **Realtime Voice**. Available capabilities are model-dependent - unsupported ones appear disabled. Click **Save** to apply changes, or **Delete** to remove the model from the workspace. ### Model tiers Tiers decouple agent definitions from specific model choices. Agents reference tiers, and the platform resolves the tier to a specific model at runtime: | Tier | Intended use | Typical models | | - | - | - | | Fast | Classification, routing, quick responses. | Claude 3.5 Haiku, GPT-4o mini, Gemini 2.0 Flash. | | Balanced | General-purpose tasks balancing quality and cost. | Claude 4 Sonnet, GPT-4o, Gemini 2.5 Flash. | | Powerful | Complex reasoning, analysis, content generation. | Claude 4 Opus, o3, Gemini 2.5 Pro. | | Voice | Real-time voice interaction. | GPT-4o Realtime, Gemini 2.0 Flash (Live). | ### Project Access The **Project Access** tab inspects and manages a single project's model bindings and feature routing, from the same authoritative configuration used by Studio and Runtime. * Select the project from the **Project** dropdown. * The tab has three sub-tabs: **Selected models**, **Feature config**, and **Resolution diagnostics**. Secret values and binding identifiers are never shown on this page. **Selected models** Shows which workspace-catalog models this project is allowed to use. * **Model access rollout** banner - indicates whether the project uses selected model bindings only, or has unresolved legacy references. * **Imported default model unavailable** banner (when applicable) - shows a legacy project default that was imported but can't run until its workspace model and credential are configured, tagged **Project default** and **Workspace model not configured**. * **Selected models** / **Eligible models** counts. * The **Selected models** list shows each bound model with its provider, connection type (for example `Workspace-shared`), status (for example **Ready**), and whether it's the **Project default**. Click the **X** to remove a model. * Click **+ Add models** to open **Add models from the workspace catalog**. Search, then click **Add** next to any workspace-shared or project-restricted connection - each entry shows its provider, connection type, and readiness (for example **Not tested** or **Ready**). * Use the **Project default (this project only)** dropdown to set which selected model is this project's default, then click **Save selection**. **Feature config** Controls how individual platform features (operations) resolve to a model, either by tier or by per-feature override. * Toggle **Per-operation routing** to switch between "every feature follows its tier" (off) and per-feature overrides (on). * Banners: * **Some features need attention** - lists features whose model binding needs a fix. * **Model access rollout** - same as in Selected models. * **Catalog features not available for execution** - lists newer catalog features not yet executable (for example: Query rewriting, Retrieval reranking). * **Tier bindings** - **Fast**, **Balanced**, **Powerful**, and **Voice** cards, each showing the feature count on that tier, a model dropdown (or **No model bound**), and a short description of the tier's intended use. Each tier draws only from this project's selected models. * Below the tier bindings, the full feature list is grouped by category: | Category | Purpose | Example features | | - | - | - | | **Arch** | Designing, diagnosing, and generating. | Arch assistant | | **Runtime** | Serving live conversations. | Default runtime model for agents, Entity extraction, Validation, Intent detection | | **Knowledge** | Answering from your content. | Progressive summarization, Question synthesis, Knowledge graph extraction | | **Insights** | Computing conversation insights. | Insight intent classification, Insight quality evaluation | | **Evals** | Testing before release. | Evaluation suite generation, Simulated user, Eval remediation | | **Pipelines** | Tuning how content is ingested. | Pipeline LLM stages | Each feature row shows an enabled checkbox, its name, description, and operation code; a tier-or-override control (a tier dropdown, or **Override with a model** to pin a specific model, plus a **Why this model?** link explaining the resolution); which model it **Resolves to**; the source it came **From** (for example **Project default**); and its **Status** (for example **Ready**). Overrides can use selected project bindings only. * Click **Save feature config** to apply changes. Select a ready model binding or repair the connection before releasing this project. **Resolution diagnostics** A read-only preview of the model and credential source Runtime actually selects for each operation. Secret values and binding identifiers are never shown. * Choose the **Resolution context** to preview - for example **Project working configuration**, or a specific deployment (for example `dev · 1.0`). * The table lists each **Operation** (for example Extraction, Validation, Tool selection, Response generation) with: | Column | Description | | - | - | | **Requested Tier** | The tier the operation asks for, and whether it's using the feature's default tier. | | **Resolves To** | The model and provider that actually serves the operation. | | **Credential** | Whether a usable credential was found (for example **Credential ready**) and the connection type. | Click **View resolution details** on a row to see the full precedence trail - for example whether a **Deployment override**, **Project feature override**, **Project model selection**, or **Workspace tier default** was applied. ### Troubleshooting | Issue | Potential causes | Resolution | | - | - | - | | Model shows **No Keys** status | No credential linked to the model. | Expand the model row and click **+ Add Key** in the Connections section. | | Model validation fails | Invalid API key, incorrect endpoint URL, or blocked outbound connections. | Confirm the API key with the provider directly. For Azure, verify the endpoint includes the deployment name. Check that outbound HTTPS connections to the provider are allowed. | | Agent can't find a model for the requested tier | No active model assigned to the tier, or the linked credential is inactive. | Verify at least one active model is assigned to the tier with a **Ready** status. Check that the model's linked credential is active. | | Token budget exceeded | High token consumption across operations. | Review consumption in **Account > Billing & Usage**. Switch high-volume operations to a Fast-tier model to reduce usage. | *** ## Model Pricing Manage workspace-level LLM pricing overrides and inspect inherited project and platform prices. * **Navigation**: Settings > AI Configuration > Model Pricing * **Required role**: Owner or Admin The page shows four summary counts: **Pricing rows**, **Workspace overrides**, **Project overrides**, and **Platform defaults**. **Add a workspace pricing override:** 1. Go to **Settings > AI Configuration > Model Pricing**. 2. Click **+ Add pricing**. 3. In the **Add workspace pricing** dialog, enter: | Field | Description | | - | - | | **Provider** *(required)* | The LLM provider, for example `openai`. | | **Model ID** *(required)* | The model identifier, for example `gpt-5-mini`. | | **Input price** *(required)* | Price per unit of input tokens. | | **Output price** *(required)* | Price per unit of output tokens. | | **Cache read price** *(optional)* | Price per unit for cached-input reads. | | **Cache write price** *(optional)* | Price per unit for writing to the cache. | | **Reasoning output price** *(optional)* | Price per unit for reasoning-token output, for models that bill reasoning separately. | | **Currency** | The currency for all prices on this row. Default: USD. | | **Effective from** *(required)* | The date this pricing override takes effect. | | **Effective to** *(optional)* | The date this pricing override stops applying. Leave blank for no end date. | | **Active override** | Whether this row is used during cost resolution. Inactive rows are retained for history but skipped during resolution. | | **Notes** *(optional)* | Free-text context, for example a commercial source, approval note, or pricing tier. | 4. Click **Save pricing**. Workspace overrides apply to every project unless a project-specific override exists. **Import workspace pricing:** Use **Import** to bulk-load effective-dated pricing rows, previewing them before committing. 1. Go to **Settings > AI Configuration > Model Pricing**. 2. Click **Import**. 3. In the **Import workspace pricing** dialog, provide the rows either by: * Clicking **Choose File** to upload a **CSV or JSON file**, or * Pasting JSON rows or CSV with headers directly into **Import content**. 4. Optionally, check **Allow overlapping windows** to permit pricing rows with overlapping effective-date ranges. Overlapping rows can make effective-price resolution depend on the latest start date. 5. Click **Preview import** to validate the rows before committing. 6. Click **Commit import** to apply the previewed rows as workspace overrides. Use **Refresh** to reload the page's counts and rows from the latest data. *** ## Arch Arch is the AI Architect component of the Agent Platform. It manages the model and credentials used for agent specification generation and chat-based orchestration. * **Navigation**: Settings > AI Configuration > Arch * **Required role**: Owner or Admin The page is organized into two tabs: **Settings** and **Audit Logs**. ### Settings **Current source:** The **Current Source** banner displays the active credential source and model Arch is currently using, for example *Model Hub · OpenAI · gpt-5.2*. **Credential source:** Choose how Arch authenticates with the LLM provider: | Option | Description | | - | - | | Platform Credits | Use platform-provided API credits. Select which model to use in the Settings tab. | | Model Hub | Use a model already configured with credentials in your Model Hub. | Arch works best with models that support tool calling and have large context windows. **Select model:** Select the model Arch uses from the dropdown. The selected model displays its provider, model ID, capability tags, and tier. If the model hasn't been tested with Arch, a warning appears. **Parameters:** Fine-tune generation behavior for chat and spec generation: | Parameter | Description | Default | | - | - | - | | Reasoning Effort | Controls how much reasoning the model applies before responding. Slide between none and high. | Low | | Verbosity | Controls the length and detail of generated responses. Slide between low and high. | Medium | | Max completion tokens | Maximum tokens Arch can generate per response. | 6,000 | | Compaction Threshold | Auto-compaction condenses earlier conversation content once context usage reaches the **Compaction Threshold**, so long-running sessions don't exhaust the model's context window. | | Click **Save Changes** to apply updates. ### Audit logs The **Audit Logs** tab shows Arch session activity for the selected time range, including cost, error count, and a browsable session list. **Summary metrics:** | Metric | Description | | - | - | | Sessions (24h) | Number of Arch sessions in the last 24 hours. | | Total Cost (24h) | Estimated cost of Arch sessions in the last 24 hours. | | Errors (24h) | Number of sessions that encountered errors in the last 24 hours. | | Listed Sessions | Total number of sessions shown in the current session list. | **Sessions list:** The list displays all Arch sessions. Use **Has Errors** to show only sessions with errors, or **Today** to show only today's sessions. Each session row shows the session ID, timestamp, number of turns, and current phase. Click a session to view the full execution flow in the detail panel. ### Traces Use the **Traces** tab on the **Arch** page to inspect execution traces from Arch AI sessions. Traces appear after Arch AI runs an execution with tracing enabled. Until then, the **Sessions** panel shows **No traces yet**. **Navigation**: **Settings** → **Arch** → **Traces** The summary cards at the top of the tab show the following: | Card | Description | | - | - | | **Sessions (24h)** | Number of Arch AI sessions in the last 24 hours. | | **Total Cost (24h)** | Total cost of Arch AI sessions in the last 24 hours. | | **Errors (24h)** | Number of errors in the last 24 hours. | | **Listed Sessions** | Number of sessions that match the current filters. | Use the filters to narrow the session list: | Filter | Description | | - | - | | **Project** | Limits sessions to a single project. Default: **All projects**. | | **Time range** | Limits sessions to a time window. Default: **Last 24h**. | | **Phase** | Limits sessions to a phase of the Arch AI workflow: **Interview**, **Blueprint**, or **Build**. Default: **Any phase**. | The Traces view has three panels: | Panel | Description | | - | - | | **Sessions** | Lists the sessions that match the current filters. Select **Refresh** to reload the list. | | **Select a session** | Shows the execution tree for the session you select. | | **Select a span** | Shows the metadata and payload references for the node you select in the trace tree. | *** ## Voice services Voice Services manages credentials for speech-to-text (STT) and text-to-speech (TTS) providers that power voice-enabled agent interactions. * **Navigation**: Settings > AI Configuration > Voice Services * **Required role**: Owner or Admin Voice testing requires at least one STT (ASR) provider and one TTS provider configured; any supported provider combination works. For Realtime Voice, configure a realtime model (S2S). ### Configure a provider 1. Go to **Settings** > **AI Configuration** > **Voice Services**. 2. Find the provider you want to configure and click **Configure**. 3. Enter the provider credentials in the configuration dialog. 4. Click **Save**. Configuration fields vary by provider. The following table shows an example for Google Cloud Speech: | Field | Description | | - | - | | Display Name | A label to identify this credential set, for example *Google Cloud Speech Credentials*. | | Service Account JSON | Paste the full Google service-account JSON used for Speech-to-Text. | | STT Model ID | Optional. The Google STT model to use, for example `chirp_3`, `chirp`, `latest_long`, `telephony`. Default: `chirp_3`. | ### BYO custom speech providers Tenant-scoped custom STT/TTS adapters available to voice pipeline channels. Use this when you want to host your own speech recognition or synthesis adapter instead of a built-in provider. You write and run the adapter; the platform routes call audio to it. Caller audio and transcripts are sent to the endpoints you configure below. Only register endpoints you trust. **Add a BYO speech provider:** 1. Go to **Settings** > **AI Configuration** > **Voice Services** > **BYO custom speech providers**. 2. Click **+ Add provider**. 3. Enter the provider details: | Field | Description | | - | - | | Display Name | A label to identify this provider, for example *Acme Speech*. | | Provider slug | A short identifier for the provider. | | API key | One key for the whole provider. KoreVG sends it as a Bearer token to every enabled endpoint. Encrypted at rest. | 4. Configure the **Speech-to-text (ASR)** and/or **Text-to-speech** adapters (see below). 5. Click **Save**. Use the **Adapter contract** panel to open the STT or TTS contract while filling in URLs, so you can see what your adapter needs to implement. **Speech-to-text · ASR** Streaming recognizer endpoint and optional picker metadata. Toggle **Enabled** to make the adapter available. | Field | Description | | - | - | | STT WebSocket endpoint | The public `wss://` endpoint for your streaming recognizer, for example `wss://speech.example.com/stt`. Private IP ranges are blocked server-side. | | Languages *(optional, Studio only)* | Languages your adapter supports, for the Studio language picker. Select **+ Add** to add more. | | STT options (JSON) | Provider-specific options forwarded to your adapter verbatim, for example `{"model":"nova-2","endpointing":300}`. | **Text-to-speech** Streaming synthesis endpoint, languages, voices, and options. Toggle **Enabled** to make the adapter available. | Field | Description | | - | - | | TTS streaming WebSocket endpoint | The public `wss://` endpoint for your streaming synthesizer, for example `wss://speech.example.com/tts`. Streaming is the only supported TTS path - batch HTTP TTS is not supported. | | Languages *(optional, Studio only)* | Languages your adapter supports, for the Studio language picker. Select **+ Add** to add more. | | Voices *(optional, Studio only)* | Voices your adapter supports, for the Studio voice picker. Select **+ Add** to add more. | | TTS options (JSON) | Provider-specific options forwarded to your adapter verbatim, for example `{"model":"v2","style":"narration"}`. | Options entered in the JSON fields are forwarded to your adapter as-is - the adapter decides what they mean. After saving, verify the adapter with a test call. A registered credential isn't proof the adapter works. *** ## Guardrails Guardrails provide content safety controls for your workspace and projects. Workspace administrators can configure guardrail providers and their available scanners, while guardrail policies are configured at the project level to evaluate agent inputs and outputs against defined safety rules. * **Navigation**: Settings > AI Configuration > Guardrails * **Required role**: Owner or Admin The page contains two sections: **[Guardrail Providers](#guardrail-providers)** and **[Guardrail Policies](#guardrail-policies)**. ### Guardrail providers Guardrail providers are the evaluation services that assess content against your policies. Workspace administrators can add and configure guardrail providers for use across projects. Configure at least one provider before creating policies. Project authors can select which configured providers the project can use. For more information, see [Select guardrail providers](/agent-platform/safety-and-guardrails#select-guardrail-providers). **Add a provider:** 1. Go to **Settings > AI Configuration > Guardrails**. 2. Click **Add Provider**. 3. In the dialog, configure the provider using the **Form** or **YAML** tab. 4. Click **Add Provider** to save. **Form tab fields:** | Field | Description | | - | - | | Name | A unique internal identifier for the guardrail provider, for example *OpenAI Moderation*. | | Display Name | A human-readable label, for example *Content Safety (OpenAI)*. | | Adapter Type | The integration type for the guardrail provider, such as OpenAI Moderation or Self-hosted / Gateway (OpenAI-compatible). | | Response format | Available when Self-hosted / Gateway (OpenAI-compatible) is selected. Specifies how the provider response is interpreted: Llama Guard (safe / unsafe + scores), Qwen3Guard (Safety / Categories / Refusal), or Generic classifier (platform prompt → safe / unsafe). | | Endpoint URL | The provider API endpoint URL. | | Model | The model used for guardrail evaluation. | | Authentication | Enable authentication for the provider. Use an Auth Profile for providers that require credentials. Raw API keys aren't accepted. | | Default Category | The default content category evaluated by the provider. | | Default Threshold | Sensitivity threshold from 0.0 to 1.0. Default: 0.7. Lower values flag more content; higher values flag only high-confidence violations. | | Circuit Breaker | Configure failure handling. **Max Failures** sets the number of consecutive provider failures that activates the circuit breaker; **Reset Timeout** sets the time, in milliseconds, before the platform attempts to recover the provider. | | Retry Configuration | Configure retry behavior. **Max Retries** sets the maximum number of retry attempts when a provider request fails; **Backoff Strategy** sets the delay, in milliseconds, between retry attempts. | | Enabled | Enable the provider for guardrail evaluation. | | Synthetic health checks | Enable periodic health checks for the provider using its configured credentials. | **Configure scanners** After adding a guardrail provider, workspace administrators can configure the scanners available for that provider. 1. In **Guardrail Providers**, find the provider you want to configure. 2. Click **Scanners** in the provider's **Actions** column. 3. In the **Scanners** panel: * Add scanners as needed. * Enable or disable individual scanners. * Set the threshold for each scanner. A lower threshold is stricter and causes the scanner to trip sooner. 4. Click **Save scanners**. **Provider health monitoring:** The platform periodically checks provider health. When a provider becomes unhealthy, its circuit breaker activates. After repeated failures, the circuit breaker opens and stops sending requests. After the reset timeout, it allows a test request through. When a provider's circuit breaker is open, the platform follows the configured fail mode: * **Fail-open** - Content is delivered without guardrail evaluation. Violations may go undetected. * **Fail-closed** - Content is blocked until the provider recovers. Safer, but may interrupt service. ### Guardrail policies The **Guardrail Policies** section displays the policies configured for a selected project. Select a project from the **Project** dropdown to view its guardrail policies. Policies are configured at the project level and attached to guardrail providers. For information about creating and configuring guardrail policies, see [Guardrail policies](agent-platform/safety-and-guardrails#guardrail-policies). *** ## Auth profiles Auth profiles store authentication configurations that agent tools use to call external services securely. Profiles are managed centrally at the workspace level and reused across all projects. * **Navigation**: Settings > AI Configuration > Auth Profiles * **Required role**: Owner or Admin The page displays auth profiles by deployment environment. Use the **Default**, **Dev**, **Staging**, and **Production** tabs to view and manage profiles for each environment. ### Add auth profiles Click **Add Profile**. Two profile types are available: | Type | Description | | - | - | | Custom Profile | Configure a custom authentication scheme with manually specified credentials and settings. | | Integration Profile | Configure authentication for a supported third-party integration. | ### Supported auth types Auth types are organized into four categories. **Basic authentication** | Type | Description | | - | - | | API Key | Static API key sent via header or query parameter. | | Bearer Token | Static bearer token in Authorization header. | | Basic Auth | HTTP Basic authentication with username and password. | | Custom Header | Send one or more custom auth headers. | **OAuth 2.0** | Type | Description | | - | - | | OAuth 2.0 App | Authorization code OAuth with PKCE and metadata discovery support. | | Client Credentials | Machine-to-machine OAuth via client credentials grant. | | Password Credentials (ROPC) | Legacy OAuth 2.0 Resource Owner Password Credentials (ROPC) flow. Use only when the provider requires resource-owner credentials. | **Advanced** | Type | Description | | - | - | | Custom Authentication | Describes a provider's authentication scheme when no built-in type fits. | | AWS IAM (SigV4) | Signs requests with AWS Signature Version 4. | | Azure AD | Authenticates through Microsoft Entra ID (Azure Active Directory) for enterprise single sign-on (SSO). | | Kerberos-SPNEGO Auth | Authenticates enterprise systems with SPNEGO/Kerberos. | | SAML | Uses a SAML assertion-based authentication flow. | | WS-Security | Authenticates SOAP services with a WS-Security UsernameToken or certificate. | | GCP Workload Identity Federation | Gives keyless access to Google Cloud services using short-lived tokens. | | GCP Service Account Key | Exchanges a Google service-account key for short-lived access tokens. | **Transport security** | Type | Description | | - | - | | mTLS | Mutual TLS with client certificate and private key. | Auth profile credentials are encrypted at rest and never exposed in logs or agent responses. Guardrail providers that require credentials must reference an auth profile. Raw API keys are not accepted directly in the guardrail provider configuration. ### Use auth profiles Once created, reference an auth profile by name in tool configurations and guardrail provider settings. The platform resolves the profile at runtime and injects the correct credentials without exposing them in agent definitions, logs, or traces. When a tool runs in the Default, Dev, Staging, or Production environment, the platform resolves the auth profile configured for that environment. # Auth types Source: https://koreai.mintlify.app/agent-platform/administration/auth-types ## API Key | Field | Description | | - | - | | Profile Name | Required. A label for the profile, for example *API Key - Production*. | | Description | Optional. Notes on what this profile is for or which service it connects to. | | Usage Mode | Chooses how the profile is authorized. **Preconfigured** - the admin supplies credentials at setup time, and users don't need to authorize. | | Visible to project members | Toggle. When on, project members can use this profile. | | Header Name | Required. The header the key is sent in, for example `X-API-Key`. | | Placement | Chooses **Header** or **Query Parameter**. | | Prefix | Optional. Text prepended to the key value. | | API Key *(Secrets)* | Required. The key value. | ## Bearer Token | Field | Description | | - | - | | Profile Name | Required. A label for the profile, for example *Bearer Token - Production*. | | Description | Optional. Notes on what this profile is for or which service it connects to. | | Usage Mode | Chooses how the profile is authorized. **Preconfigured** - the admin supplies credentials at setup time, and users don't need to authorize. | | Visible to project members | Toggle. When on, project members can use this profile. | | Authorization Prefix | Optional. Defaults to `Bearer`. | | Token *(Secrets)* | Required. The bearer token value. | ## Basic Auth | Field | Description | | - | - | | Profile Name | Required. A label for the profile, for example *Basic Auth - Production*. | | Description | Optional. Notes on what this profile is for or which service it connects to. | | Usage Mode | Chooses how the profile is authorized. **Preconfigured** - the admin supplies credentials at setup time, and users don't need to authorize. | | Visible to project members | Toggle. When on, project members can use this profile. | | Username *(Secrets)* | Required. | | Password *(Secrets)* | Required. | ## Custom Header | Field | Description | | - | - | | Profile Name | Required. A label for the profile, for example *Custom Header - Production*. | | Description | Optional. Notes on what this profile is for or which service it connects to. | | Usage Mode | Chooses how the profile is authorized. **Preconfigured** - the admin supplies credentials at setup time, and users don't need to authorize. | | Visible to project members | Toggle. When on, project members can use this profile. | | Headers | One or more header rows. Select **+ Add** to add a row. Each row needs both a header name and a value; values are treated as secrets. | ## OAuth 2.0 App | Field | Description | | - | - | | Profile Name | Required. A label for the profile, for example *OAuth 2.0 App - Production*. | | Description | Optional. Notes on what this profile is for or which service it connects to. | | Usage Mode | Chooses how the profile is authorized. **Preconfigured** - the admin supplies credentials at setup time, and users don't need to authorize. OAuth 2.0 App profiles support two additional modes, **JIT** and **Preflight**, which require a project-scoped profile. | | Scope | Automatically derived from the selected Usage Mode. Defaults to **Shared**. | | Visible to project members | Toggle. When on, project members can use this profile. | **OAuth endpoints** | Field | Description | | - | - | | Authorization URL | Required. | | Token URL | Required. | | Refresh URL | Optional. The endpoint used to request a new access token when the current one expires. | | Scopes | Optional. Space-separated scopes, for example `read:users write:tasks offline_access`. Leave empty when the provider doesn't require or advertise scopes. | | Require PKCE | Toggle. Enable when the provider requires a proof key during authorization, including OAuth 2.1-compatible providers. | | PKCE Challenge Method | Shown when Require PKCE is on. Defaults to **S256 (Recommended)**. | | Additional Authorization Parameters | Optional key=value pairs appended to the authorization URL. Select **+ Add** to add a row. | **Authorized redirect URI** Copy the generated **Callback URL** and add it to the OAuth provider's authorized redirect URIs (for example, Google Cloud Console or GitHub OAuth Apps). **Secrets** | Field | Description | | - | - | | Client ID | Required. | | Client Secret | Required. | # Catalog settings Source: https://koreai.mintlify.app/agent-platform/administration/catalog-settings Catalog settings cover the reusable material a project draws on: response templates, modules imported from elsewhere, modules this project publishes, trace metadata, and telemetry export. ## Templates The **Template Catalog** holds the response formats agents can send. It's a reference you copy from rather than a place you author. Open a template to see how it renders, adjust its payload, and copy the snippet your agent needs. **Navigation**: **Project** -> **Settings** -> **Templates** ### Find a template Search by name, or filter by category. The categories group templates by the job the response does rather than by the channel it targets. | Category | Description | | - | - | | **Content** | Templates carrying formatted text and structured message payloads, including formats specific to a single messaging platform. | | **Media** | Templates whose payload is a file the client plays, displays, or offers for download. | | **Data** | Templates presenting numeric or tabular information, where the point is the figures rather than the prose around them. | | **Input** | Templates collecting a response, where the agent expects the user to act on the message rather than only read it. | | **Feedback** | Templates gathering a rating or reporting progress back to the user. | ### Reading a template card Each card carries the template name, a short description, its category, and three badges. The badges are the part to read before you commit to a template, because they tell you whether it will work where your agent actually runs. | Badge | Description | | - | - | | **Web** | Reports how the web chat surface renders the template. A native value means the surface renders the template's own form. Anything else means it renders a substitute, so the agent still responds but the user doesn't see the layout the template describes. Weigh this alongside the channels your agent serves, since a template built for one messaging platform necessarily degrades elsewhere. | | **Preview** | Reports how the catalog's own preview pane renders the template, which isn't always how the live surface renders it. A limited preview is a limitation of this page rather than of the template, so don't rule a template out on the preview alone. | | **ABL** | Reports whether you can emit the template from ABL. An insertable template is one you can send directly. A preview-only template can be inspected here but has to reach the user by another route, and a partial value means only some of the template is available. Check this badge first when you're choosing a template to build on. | ### Inspect and use a template Selecting a card opens a detail panel that takes you from looking at a template to sending one. Its three parts let you see the rendering, change the content, and take away the code. | Section | Description | | - | - | | **Preview** | Renders the template from the payload currently in the editor, so you can see the result before you write any agent code. | | **JSON Editor** | Holds the template payload, which you can edit to try your own content in place of the sample. A character count below the editor shows how much of the allowed payload size you've used. | | **ABL Snippet** | Gives the ABL that produces this template. Select **Copy** and paste it into your agent, then replace the sample values. This is the fastest route from choosing a template to sending one, and it saves you deriving the syntax from the payload by hand. | *** ## Imported modules The **Imported Modules** page lists the reusable components this project has taken from the catalog or from other projects, including agents, tools, and workflows. **Navigation**: **Project** -> **Settings** -> **Imported Modules** Each entry records the module name, its version, the project it came from, and when this project imported it. The version and source together are what matter during an upgrade, since they tell you whose change you're taking on and who to ask before you take it. *** ## Module publishing The **Module Publishing** page turns this project's agents, tools, and workflows into modules other projects can import. **Navigation**: **Project** -> **Settings** -> **Module Publishing** Publishing places a module in the workspace catalog, where you choose the resources to include, set the version, and control who can import it. Treat a published module as an interface rather than a copy, because projects that import it inherit your future versions along with the current one. A release exports the agents, tools, and workflows you selected. Workflows come across in two forms: those you published directly, and those a published workflow tool depends on. The second kind travels whether or not you selected it, so review a release for dependencies you didn't intend to share. *** ## Trace dimensions The **Trace Dimensions** page defines the custom metadata the platform records with every trace event. **Navigation**: **Project** -> **Settings** -> **Trace Dimensions** Each dimension carries a name, a source, and a data type. Dimensions become filters in the session browser and in Insights, so define them around the questions you expect to ask, such as the customer segment or the campaign behind a conversation. A dimension you add today applies to traces recorded from that point on, which makes this a setting worth getting ahead of rather than reaching for once you need the analysis. *** ## Telemetry export The **Telemetry Export** page sends this project's live model, agent, and tool telemetry to an external observability provider, so agent behavior appears alongside the rest of your infrastructure monitoring. **Navigation**: **Project** -> **Settings** -> **Telemetry Export** ### Destination This section holds the provider connection and decides how much telemetry leaves the platform. Complete it before you enable export, since the status switch has nowhere to send to until you do. | Field | Description | | - | - | | **Connection string** | Holds the Azure Monitor connection string, which needs to carry both an instrumentation key and an ingestion endpoint. The field masks the value once saved. | | **Data export level** | Decides how much leaves the platform. Metadata covers models, token usage, latency, and tool names. Full content adds prompts, responses, and tool inputs and outputs, which makes your observability provider a second store of conversation content and brings it into scope for the same privacy review as the platform itself. | | **Trace grouping** | Decides what one trace represents. Grouping per session gives you the whole conversation in a single trace with each turn nested inside it, which suits reading conversations end to end. Grouping per turn gives accurate duration and status on each trace, which suits alerting and latency work. | | **Status** | Turns export to the configured destination on or off without discarding the configuration. | Provide an Azure Monitor connection string before you save a new telemetry exporter. ### Page actions Three controls at the foot of the page manage the configuration you've entered. | Action | Description | | - | - | | **Validate** | Checks the destination details you entered. Run it before saving, since an unreachable destination silently drops telemetry rather than reporting an error later. | | **Save** | Writes the exporter configuration to the project. | | **Reset** | Returns the form to its last saved state. | # General settings Source: https://koreai.mintlify.app/agent-platform/administration/general-settings General settings govern project membership, authentication keys, model availability, runtime behavior, data lifecycle, compile-time variables, and localization assets. ## Members The **Members** page controls who reaches your project and what they can do inside it. **Navigation**: **Project** -> **Settings** -> **Members** The page lists every project member with their name, email, role, and join date. Select **Add Member** to grant access to an existing workspace member, then assign a role. Removing a member revokes their access at once rather than at their next sign-in. ### Project roles A role bundles the permissions a member holds everywhere in the project. Assign the narrowest role that still lets someone do their job, since the roles differ less in what they can see than in what they can change. | Role | Capabilities | | - | - | | **Admin** | Manages members, settings, agents, and deployments. Assign it to the people who own the project's configuration, since this role can change anything the other three roles depend on. | | **Developer** | Creates and modifies agents, tools, workflows, imports, and project resources. This is the working role for anyone building the agent. | | **Tester** | Reads project resources, creates and reads sessions, runs simulations, and views analytics. It lets someone exercise the agent thoroughly without risking the configuration they're testing. | | **Viewer** | Reads project resources, sessions, and analytics. Use it for stakeholders who need reporting rather than the build. | *** ## API keys The **API Keys** page issues and revokes the credentials that carry programmatic access to the project runtime. **Navigation**: **Project** -> **Settings** -> **API Keys** | Tab | Description | | - | - | | **SDK Keys** | Credentials that client-side SDKs and web widgets present to the project runtime. These travel to browsers and devices, so treat them as public and scope them accordingly. | | **Platform Keys** | Credentials for server-to-server integrations and administrative API access. These stay on infrastructure you control and carry broader authority than SDK keys. | Select **Create Key**, name it descriptively, and optionally set an expiration date. Each entry shows the key name, a masked prefix, and the date something last used it. The last-used date is the field that matters during an audit, because it identifies keys you can revoke without breaking anything. Deleting a key revokes it permanently and immediately. Anything still presenting that key stops working, so confirm the last-used date before you delete. Issue a separate key for each environment and rotate them on a schedule. A single shared key means one compromise forces you to rotate everywhere at once. *** ## Models Use the **Models** page to choose which models your project can use and which model handles each platform feature. It has two tabs: * **Model Configs**: choose the models available to the project. * **Feature config**: assign models to individual features. **Navigation**: **Project** -> **Settings** -> **Models** ### Model Configs Use this tab to select the models your project can use. Only selected models appear in the project's model pickers and feature settings. The tab shows how many models you've selected out of the models your workspace makes available to the project. Each selected model shows its provider, whether it's **Workspace-shared** or **Project-restricted**, and its status: **Ready**, **Not tested**, **Connection failed**, or **Inactive**. To change the selection, select **Add models**, choose from the eligible models, and then select **Save selection**. Only project administrators can change the selection. Other roles see the tab as read-only. If a selected model is no longer available to the project, the tab shows a warning. Select **Remove unavailable models** to clear them, and then replace them as needed. #### Project default The project default is the model the project uses when no more specific setting applies. It applies to this project only and doesn't change the workspace. * **Choose a default.** Use the **Project default (this project only)** field. The default must be one of the selected models. In a project that uses selected models only (see [Model access rollout](#model-access-rollout)), you must choose a default before you can save more than one selected model. * **New projects.** A new project starts with the workspace's default models selected, and one of them is set as the project default. If the workspace has no default model, the tab shows **No model configured** until you select a model. * **If the default becomes unavailable.** Features that use the project default switch to the next available selected model. Features assigned directly to a specific model don't switch, and they stop working until you fix or replace that model. Once a project uses selected models only, the platform never uses a model that isn't selected. The platform doesn't retry a failed request with another model. #### Model access rollout The **Model access rollout** banner shows whether your project is limited to its selected models. New projects are limited from the start. Existing projects move over in stages, and the banner shows the current stage. The banner also shows the number of unresolved references. A warning banner means the project needs repair before it can be limited to selected models. The count isn't an error in your current setup. ### Feature config Use this tab to assign models to platform features. Different features need different models. Assigning a smaller, faster model to simple tasks and a stronger model to complex ones lowers cost. The **Per-operation routing** switch turns feature routing on or off for the project. When it's off, every feature uses the project default, and any saved tier models and overrides are kept but ignored. When it's on, each feature uses the model assigned to its tier, so changing a tier's model changes every feature on that tier. You can also assign a specific model to a single feature. Use overrides only when a feature needs something different, because they're easy to forget. If feature routing is turned off for your whole deployment, the switch is unavailable and the tab shows a notice. Your saved settings are kept and apply again when routing is turned back on. #### Tiers Assign one of the project's selected models to each tier. | Tier | Use it for | | - | - | | **Fast** | High-volume, simple tasks where low cost matters most. | | **Balanced** | Everyday work. | | **Powerful** | Complex tasks that need the best judgment, at higher cost and slower speed. | | **Voice** | Real-time speech-to-speech conversations. | If a tier has no model, its features use the project default. If you configure a tier but no ready model matches it, the features on that tier can't run. The page warns you about missing models and broken connections before you release the project. #### Features Features are grouped by area: Arch, Runtime, Knowledge, Insights, Evals, and Pipelines. Each row shows the following: | Column | Description | | - | - | | **Tier or override** | Whether the feature uses its tier's model or a model you assigned directly. You can only assign selected models. | | **Resolves to** | The model the feature actually uses. Check this column when troubleshooting. | | **From** | Why that model was chosen: a feature override, a tier, or the project default. | | **Status** | Whether the feature can run (see the following table). | | Status | Meaning | | - | - | | **Ready** | The feature uses a working model. | | **Not tested**, **Connection failed**, **Inactive**, **Unknown** | The model's connection isn't confirmed working. Check the model's connection. | | **Not configured** | No model is assigned. | | **Off** | The feature is turned off. | | **Unavailable** | The platform doesn't support running this feature yet. | Select **Why this model?** on a row to see how the model was chosen. Select **Save feature config** to apply your changes. *** ## Runtime configuration The **Runtime Configuration** page holds the behavioral controls that govern how the project's agents read input, handle multiple requests in one turn, fill in unstated values, tune voice latency, recover from failures, occupy callers during waits, expose their own activity, protect personal data, and bound their own tool use. **Navigation**: **Project** -> **Settings** -> **Runtime Config** Changes here apply to every agent deployment in the project, so treat this page as project-wide policy rather than per-agent tuning. Each group appears as a collapsible card. Select **Save** to apply changes, or **Reset to defaults** to return every setting on the page to its factory value. ### Extraction pipeline This section decides how the runtime turns a raw user message into structured field values. | Field | Description | | - | - | | **Extraction Strategy** | Chooses which extraction stages run and in what order. Language detection and resolution always run, whatever you choose. The default, **Auto**, runs the stages in sequence: pattern matching runs first, the ML model extracts fields the patterns didn't match, and the LLM extracts anything still missing. Auto gives the best overall accuracy. Change it when your fields sit at one extreme or the other, either structured enough that a model call is waste, or conversational enough that patterns alone leave fields unfilled. The ML stage supports English, French, Spanish, Italian, and Portuguese. For other languages, extraction falls back to the LLM stage. | | **Confidence Threshold** | Sets the minimum confidence an extracted entity needs before the runtime accepts it. Accepts values from 0.0 to 1.0 inclusive. Default: 0.50. Raise it when the agent accepts values the user didn't give, and lower it when the agent keeps asking for values the user already supplied. | | **Include Confidence** | Attaches confidence scores to extracted entity values. On by default. Turn it on when downstream logic or analytics needs to see how certain an extraction was. | ### Multi-intent recognition This section handles messages that ask for more than one thing at once. Without it, the runtime acts on the first request it recognizes and the rest of the message goes unanswered. | Field | Description | | - | - | | **Enable Multi-Intent** | Turns on detection of several intents in one message. On by default. Turn it off for narrow transactional flows, where a second detected intent is more often a misread than a real request. | | **Strategy** | Sets the order in which the runtime works through detected intents. The choice decides whether the user gets a fast answer to their first request or waits while the agent works the whole message. Default: `primary_queue`. | | **Unknown Relationship Strategy** | Chooses the fallback for same-turn supervisor routing when relationship data isn't available. Relationship data normally decides ordering, so without it the runtime needs a standing instruction rather than a judgment. Default: `parallel`. | | **When is a request finished?** | Determines when the runtime considers one customer request complete before moving to the next queued request. Applies only to the `primary_queue` strategy. FLOW agents and agents with **COMPLETE** conditions always finish based on their completion conditions. Default: **When the agent replies**. Recommended: **When the agent confirms it is done**. | | **Capture every request in one routing decision** | When enabled, the supervisor identifies all user requests during the initial routing decision so additional requests in the same message are not missed. Recommended when using two or more specialist agents. | | **Max Intents** | Caps how many intents the runtime acts on in one turn. Default: 3. Each additional intent comes from a less certain reading of the message, so raising this raises the chance of acting on something the user didn't ask for. | | **Confidence Threshold** | Sets the minimum classifier confidence needed to act on a detected intent. Default: 0.6. Raise it when the agent answers requests the user didn't make, and lower it when the agent ignores genuine second requests. | | **Queue Max Age (ms)** | Sets how long a queued intent stays eligible for handling. Default: 600000 (10 minutes). It stops the agent from returning to a request the user has since abandoned or resolved another way. | **Multi-intent strategies** | Value | Behavior | | - | - | | `primary_queue` | Handles the first intent immediately and queues the rest, addressing them in sequence once the primary completes. | | `sequential` | Addresses intents one at a time in detection order, finishing each before starting the next. | | `parallel` | Dispatches all intents at once and resolves them concurrently. | | `disambiguate` | Asks the user which request to handle first. | | `auto` | Lets the runtime choose based on the intent relationship and conversation context. | **Unknown relationship strategies** | Value | Behavior | | - | - | | `sequential` | Addresses the intents one at a time in detection order. | | `parallel` | Handles the intents concurrently. | | `disambiguate` | Asks the user which intent to prioritize. | [Learn More](/agent-platform/abl-reference/nlu#multi-intent-handling-multi_intent) ### Field inference This section lets the runtime use an LLM to infer values the user hasn't stated, which shortens conversations at the cost of occasionally guessing wrong. | Field | Description | | - | - | | **Confidence Threshold** | Sets the minimum confidence needed to accept an inferred value. Default: 0.8. Inference trades accuracy for fewer questions, and this field is where you set that trade. | | **Require User Confirmation** | Presents inferred values to the user before the agent applies them. On by default. Keep it on wherever a wrong inference costs money or changes a record, since confirmation is far cheaper than reversal. | | **Model Tier** | Selects the tier that serves inference calls. Default: `fast`. Inference runs often and on small decisions, so this choice affects per-turn latency more than most model settings. | | **Max Fields Per Pass** | Caps how many fields one inference call attempts. Default: 3. Fewer fields per pass means more calls, each of them more reliable. | ### Currency conversion This section decides where exchange rates come from when an agent quotes or converts an amount. Agents that never handle money can leave it alone. | Field | Description | | - | - | | **Currency Mode** | Selects whether the runtime converts currency using **static** rates held in the platform or **live** rates fetched from an API. Default: `static`. Static rates keep results reproducible across a conversation and across environments. Live rates keep them current. Choose on whether your users compare quotes over time. | ### Reasoning pipeline The reasoning pipeline runs a lightweight classifier ahead of each reasoning turn so the runtime can route programmatically and narrow the tool set before the main model call. **Experimental feature. Read before enabling.** * The pipeline needs a configured `tool_selection` model in your LLM provider settings. Without one, the pipeline skips silently rather than reporting an error. * It adds one extra LLM call before each reasoning turn, typically 200-500 ms. In parallel mode, the classifier and tool filter run simultaneously. * If the pipeline model fails three times in a row, a circuit breaker bypasses the pipeline for 60 seconds so the failure doesn't add latency to every turn. | Field | Description | | - | - | | **Enable Pipeline** | Runs the classifier ahead of reasoning to enable short-circuit routing and tool filtering. Everything else in this section depends on it. | | **Execution Mode** | Decides whether the classifier and tool filter run together or one after the other. **Parallel** runs them simultaneously, which costs less time. **Sequential** waits for the classifier result before deciding whether to run the tool filter at all. Default: `parallel`. | | **Pipeline Model** | Selects the model that performs intent classification. **Default** uses the project's `tool_selection` model. The pipeline runs on every turn, so this choice sets a recurring latency and cost floor for the whole project. | | **Short Circuit** | Skips the reasoning loop when classifier confidence is very high. Most of the pipeline's latency saving comes from here, and so does most of its risk, because no reasoning step follows to catch a misclassification. | | **Short Circuit Threshold** | Sets the minimum confidence (0.0-1.0) a classification needs before the runtime routes programmatically. Default: 0.85. Treat it as the safety margin on short-circuiting. | | **Tool Filter** | Reduces the tool set sent to the LLM based on classifier analysis. Large catalogs degrade tool selection accuracy, so filtering helps most in projects that expose many tools. | | **Max Tools** | Caps how many tools reach the reasoning loop. Default: 6. | | **Keyword Veto** | Overrides classifier routing when specific keywords appear. Use it for terms where you'd rather not depend on a classifier judgment at all. | | **Veto Keywords** | Holds the comma-separated keywords that trigger the veto, for example `refund, cancel, complaint`. | | **Intent Bridge** | Maps classifier output to session state so WHEN conditions and multi-intent dispatch can read it. Without it, the classifier's work stays inside the pipeline and your authored logic can't act on it. | | **Programmatic Threshold** | Sets the confidence needed for Tier 1 actions, where the runtime declines or routes without a model call (auto-decline, auto-route). Default: 0.85. | | **Guided Threshold** | Sets the confidence needed for Tier 2 guided reasoning, where the runtime keeps the reasoning loop but shapes it by hiding tools and adding routing hints. Default: 0.5. Set it below the programmatic threshold, since guided actions leave the model room to recover and programmatic ones don't. | | **Out-of-Scope Decline** | Declines out-of-scope queries programmatically, without an LLM call. It removes a model call from every off-topic turn, which pays off most on public channels where off-topic volume runs high. | | **Multi-Intent Signal** | Injects multi-intent hints into the reasoning turn when the classifier detects two or more intents. | ### Tool call policy This section bounds the deterministic CALL actions that tool lifecycle hooks invoke. The limits apply only to those hook-driven calls. Side-effecting target tools still run through the normal confirmation flow, so this section doesn't weaken confirmation. | Field | Description | | - | - | | **Boundary CALLs Enabled** | Allows tool lifecycle hooks to invoke another tool deterministically. On by default. Turning it off removes an entire class of chained behavior, so confirm nothing depends on it before you do. | | **Max CALL Depth** | Caps nested boundary CALL depth in a single turn. Default: 3. The runtime clamps this value to 5 even if you save a higher one, so use this field to set a stricter limit than the platform's own, not a looser one. | | **Max CALLs Per Turn** | Caps boundary CALL fan-out across one runtime turn. Default: 8. The runtime clamps this value to 20 even if you save a higher one. Fan-out limits matter more than depth limits, because a single wide hook can issue far more calls than a deep chain. | ### Streaming safety This section sets project-level controls for how the runtime behaves when a safety check intervenes on a response that's already streaming. It's the one safety control where timing matters as much as outcome. Existing projects stay on **Legacy passthrough** until you change this setting. | Field | Description | | - | - | | **Safety Mode** | Decides how the runtime handles final-output safety interventions for streaming responses. The difficulty is timing: by the time an intervention fires, part of the response may have reached the user. Review the value explicitly rather than assuming the project runs current behavior. | **Safety modes** | Value | Behavior | | - | - | | **Legacy passthrough** | Keeps the behavior that existed before this setting. Default for projects created before the setting was introduced. | | **Buffered** | Holds streamed output back until the safety check completes, so nothing unchecked reaches the user. Adds delay before the first words appear. | | **Gated** | Releases streamed output in stages as each portion passes the safety check. | | **Terminate forward** | Stops sending further output when an intervention fires. Text already delivered stays with the user. | | **Retract capable** | Allows the runtime to retract output already delivered when an intervention fires, on channels that support retraction. | ### Output item analytics Runtime responses often carry repeated structures such as a list of results. This section captures those repeated scalar items so you can run independent analytics queries on them. Mappings are project-scoped and additive. The runtime captures only the item arrays you configure and the scalar dimensions you declare. It doesn't store raw response JSON in analytics rows. A counter at the top shows how many mappings you've configured out of the 25 allowed. Select **Add mapping** to create one. Mappings only take effect for responses produced after you add them, so declare a dimension before you need it. You can't recover a field retrospectively. | Field | Description | | - | - | | **Mapping key** | Names the stable analytics category for this item family. Queries group on this key, so keep it stable once collection starts. Renaming it splits your history into two unrelated series. | | **Item array path** | Absolute response path to the repeated array, such as `$.items[*]`. | | **Item key path** | Optional relative path, such as `id`, that gives each item a stable identity across responses. Set it when you need to follow the same item over time rather than count occurrences. | | **Max items** | Caps the rows captured per response for this mapping. Default: 25. It bounds what an unusually large response costs you in analytics writes. | | **Description** | Optional author-facing note explaining what the repeated item represents. It's the only place a later reader learns why the mapping exists. | | **Item dimensions** | Declares the scalar fields inside each item that the runtime records. A counter shows how many of the 50 allowed dimensions you've configured. Select **Add dimension** to declare one, each with a name, a path, and a type. The runtime doesn't store fields you don't declare. With no dimensions configured, rows still include the mapping, session, trace, and item metadata. | Select the trash icon on a mapping to delete it. ### Voice low-latency profile This section holds channel-scoped defaults for fast voice responses. It tunes model routing, reasoning, context size, and filler behavior for voice while preserving semantic response quality, and explicit developer overrides still take precedence. It appears for projects with a voice channel. When the project has an active voice channel but the recommended defaults aren't applied, a banner reads **Recommended voice defaults are not active**. Select **Apply recommended defaults** to apply the low-latency profile in one step: fast model routing, inline gather, bounded context, and generative filler defaults. #### Voice setting differences The **Voice setting differences** table compares each voice setting's current value with the recommended one, so you can apply the profile wholesale or adopt it setting by setting. Select **Apply** on a row to apply only that recommendation, or **Apply recommended defaults** to apply them all. The rows shown depend on the profile version and on which settings your project already has. The recommended profile changes these groups of settings: | Group | What the profile sets | | - | - | | Profile | Selects the `customer_service_voice_low_latency_v1` profile. | | Model routing | Moves `response_gen`, `reasoning`, `coordination`, and `summarization` to the `fast` tier. | | Reasoning | Turns extended reasoning off (`reasoning.enabled` = `false`) and sets effort to `none`. | | Execution | Turns on inline gather (`execution.inline_gather`). | | Compaction | Bounds how much context voice carries into each turn. Covers tool results (`compaction.tool_results`), prior turns (`compaction.prior_turns`), and prompt context (`compaction.prompt_context`). Tool results use structured compaction with a 16000-character maximum and a 4000-character structured threshold. | | Fillers | Sets how spoken fillers are generated and bounded. Covers the generation strategy, native generative filler, the voice delay, pending-tool status nudges, output limits (tokens and characters), and the generation budget. | | Session | Controls synchronous session compaction (`session.synchronousCompaction`). It stays off on the live voice path. | Each row carries a tag that tells you what kind of change it makes: | Tag | Meaning | | - | - | | `latency` | Changes speed or cost only. | | `semantic_risk` | Can change how the agent reasons, so review these rows individually before you apply them. The reasoning settings carry this tag. | | `operational` | Changes how the runtime operates rather than how fast it responds or how the agent reasons. | Applying the recommended defaults overwrites your current values for every row listed. To keep a setting, apply the rows one by one instead. #### Profile settings | Field | Description | | - | - | | **Profile** | Shows the applied profile name and version, for example `customer_service_voice_low_latency_v1 · v1`. Versioned profile metadata is preserved in project export and import. | | **Voice model routing** | Sets the model tier for each semantic operation on voice: `response_gen`, `reasoning`, `coordination`, `summarization`, `tool_selection`, `extraction`, and `validation`. Fast is the voice default for semantic operations. `realtime_voice` stays on the voice-capable tier (`voice`). | | **Reasoning/thinking** | Toggle plus a level dropdown (`none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`). The profile disables extended reasoning by default for latency. Explicit agent and project settings still win. | | **Inline gather** | Merges entity gathering into the reasoning turn, which removes a separate model call. | | **Prompt context targets** | Bounds voice prompt context before generation. Default values: 6000 and 16000. Required fields and semantic tool outputs remain subject to tool contracts. | | **Tool-result compaction** | Uses structured projections first so voice doesn't add a serial summarization call, while preserving essential tool fields. Settings: **Strategy** (`structured`), **Max tool-result chars** (16000), **Structured threshold** (4000), **Recent results to keep** (1), **Tool description chars** (80), and **Summary timeout (ms)** (1500). | | **Prior-turn compaction** | Keeps a compact semantic preview of prior turns without running synchronous session compaction. Settings: **Strategy** (`compact`) and **Assistant preview chars** (120). | | **Prompt projection** | Bounds projected context while retaining the current request and complete small structured results. Settings: **Auto tool-result chars** (2000), **Projection strategy** (`preview`), and **Include complete small results** (on). | | **Generative filler bounds** | Sets the limits for generated fillers. The opener must be ready within the generation budget (at most 2 seconds) or the static fallback plays. Later lines in the same streamed batch can still arrive as follow-ups. Each line must be sayable in about 4 seconds. Defaults: 2000, 160, and 4000. | #### Filler strategy and coordination Native speech-to-speech (S2S) fillers are preferred when the provider advertises the capability. Pipeline generation and static speech remain bounded fallbacks. | Field | Description | | - | - | | **Generation source** | Chooses where fillers come from. Default: `s2s_preferred_then_pipeline_llm`, which uses the provider's native filler when available and falls back to a pipeline-generated one. | | **Earliest voice delay (ms)** | Sets the earliest point at which a voice filler can play. Default: 300. | | **Follow-up gap (ms)** | Sets the pause between a filler and the next follow-up line. Default: 1200. | | **Native S2S delay (ms)** | Sets the delay before a native S2S filler plays. Default: 300. | | **Pending-tool nudges** | Sets how many filler nudges can play while a tool is pending. Default: 1. | | **Pending-tool deadline (ms)** | Sets how long the runtime waits on a pending tool before nudging. Default: 2000. | | **Max fillers per turn** | Caps fillers in a single turn. Default: 5. | | **Allow native generative filler** | Lets the provider's native S2S generate fillers. On by default. | | **Allow pipeline generative filler** | Lets the pipeline LLM generate fillers when native generation isn't available. On by default. | | **Filler history messages** | Sets how many recent messages the filler generator sees. Default: 0. | | **Previous fillers** | Sets how many already-emitted fillers the generator sees, to avoid repetition. Default: 0. | | **Max output tokens** | Caps the length of a generated filler. Default: 32. | | **Native S2S filler prompt** | Optional provider instruction for native fillers, for example `Keep the caller informed briefly while the request is processing.` Immutable safety rules remain enforced by the runtime. | Synchronous session compaction stays disabled on the live voice path. Adjust prompt and tool-result projections instead, so the authoritative turn retains its semantic contract. ### Channel recovery and graceful failure The Channel Recovery and Graceful Failure policy defines how Runtime responds when model generation, tool execution, provider communication, or voice operations are delayed, interrupted, or fail. It enables channel-specific recovery behavior with bounded recovery, localized messages, sequential model fallback, and safe latency hedging, while preserving conversation state and preventing duplicate or invalid actions. Recovery is opt-in per channel. Existing channels keep their current behavior until you enable this policy on that channel. Voice uses the voice adapter. Other channels use their native progress and response semantics. Recovery policies are versioned. Publishing a policy doesn't apply it to any channel until you turn on that channel's opt-in. | Setting | Description | | - | - | | **Policy Status** | Sets whether the policy is in **Draft** or **Published** state, shown alongside the policy version (for example, v1). Runtime reads only published policies. Drafts are stored for review and don't change live sessions. | | **Default Locale** | Sets the fallback locale for a published recovery message when the caller's locale isn't available. Default: `en-US`. | | **Channel Opt-in** | Turns recovery on for individual channels: **Voice**, **Web chat**, **SDK/WebSocket**, and **Messaging**. All channels are off by default, and only the channels you turn on apply this policy. | | **Parent turn deadline (ms)** | Sets the hard upper bound for the complete recoverable interaction. Default: 30000. It's separate from voice filler and TTS budgets. When the deadline is reached, Runtime stops waiting and begins the configured recovery flow. | | **Progress acknowledgement delay (ms)** | Sets how long Runtime waits before emitting a bounded, channel-appropriate progress update. Default: 1200. This helps reduce perceived silence during recoverable delays. | | **Recovery retries** | Sets the maximum retry attempts for policy-approved transient failures. Default: 1. Side effects and unknown outcomes are never blindly retried. | | **Allow progress updates** | Keeps the interaction active with localized progress while authoritative work is pending. On by default. | | **Enable safe request hedging** | Lets Runtime race duplicate requests to cut latency. Off by default. Runtime only races same-model, streaming, cancellable requests. Unknown or mutating tools fail closed, while platform-certified read-only tools use a plan-only race and execute at most one winner. | | **Sequential model fallback** | Turns on fallback to alternate model or provider candidates after a recoverable model failure, with a field for how many candidates Runtime may try (default: 1). Runtime selects fallback candidates only after capability, safety, tool-contract, and channel checks. | | **Model/provider candidates** | JSON array of ordered fallback model or provider candidates. Default: `[]`. Keep the logical model choice separate from provider selection, and mark hedge eligibility only for pure generation. | | **Localized recovery message catalog** | JSON array of localized, versioned message records that Runtime uses during recoverable delays and failures. Each record has a `key` (for example, `recovery.tool_waiting`), `channel`, `locale`, `text`, `allowedVariables`, `maxCharacters`, `status`, and `version`. Use only allowlisted variables. The catalog rejects internal error codes and executable markup. Runtime picks the message that matches the conversation locale. | ### While the caller waits This group configures what a caller experiences while the agent thinks or works: spoken filler phrases, background hold-music audio, or both together. The settings interact, so read the whole group before you change any one part. **How filler settings take effect.** Agent no-input messages and channel-specific Gemini settings take precedence over these project defaults. Otherwise, Runtime uses project filler pools and timing, then localized platform defaults. Native realtime fillers are available only for the `voice_realtime` tier. Genesys realtime supports pending-tool fillers but not the native coordinator filler. #### Filler settings Fillers are transient chat and voice status messages the runtime emits while work is in progress. They exist because silence during a long turn reads as a failure, particularly on voice. When the agent is processing a request, Runtime waits for the configured delay before playing a filler message. Depending on the configuration and channel capabilities, it can play a static filler, generate a contextual filler, or use provider-native realtime fillers until the final response is ready. | Field | Description | | - | - | | **Fillers Enabled** | Master switch for the filler system. With it off, nothing else in this section emits anything. | | **Chat Fillers** | Shows `status_update` messages in chat clients. | | **Voice Fillers** | Speaks status updates on voice channels. Voice needs fillers more than chat does, because a chat user can see that something is in progress and a caller can't. | | **Filler language default** | Sets the fallback language for filler speech when the session has no explicit language. Use a BCP 47 tag such as `en-US`. | | **Filler language override** | Forces all generated and static fillers to use this language, regardless of the session language. Use a BCP 47 tag such as `en-US`. | | **Chat Delay** | Sets the delay before static chat fillers are emitted. Default: 1200 ms. Set it above the time a normal turn takes, so fillers mark genuinely slow turns rather than interrupting fast ones. | | **Voice Delay** | Sets the delay before static voice fillers are emitted. Default: 500 ms. Callers tolerate silence for less time than chat users, so this typically sits below the chat delay. | | **Follow-up Gap** | Voice only. Sets the pause after a filler finishes playing before the next generated follow-up line (800-3000 ms). Default: 1200 ms. Follow-ups continue until the answer starts or the per-turn limit is reached. | | **Native realtime turn delay** | Sets the delay before the native Gemini realtime hold phrase. Default: 500 ms. It's independent from normal voice fillers. | | **Cooldown** | Sets the minimum interval between fillers across turns. Default: 3000 ms. Generated voice follow-ups within one turn are paced by the follow-up gap instead. | | **Max Per Turn** | Caps fillers emitted during one turn. Default: 5. | | **Pending-tool status nudges** | Sets the upper bound for native Gemini pending-tool progress messages per turn. Default: 3. Max Per Turn also limits the effective cap. | | **Contextual filler deadline** | Sets the maximum wait (ms) for a generated pending-tool filler before the runtime uses the fallback, when static fillers are enabled. Default: 500. | | **Static Fillers** | Uses a deterministic fallback phrase when no contextual filler is available. On voice, a static filler is only ever the opener, and follow-ups are always generated. Turning this off never plays a pre-written line in place of one that failed checks. | | **Localized static filler pools (JSON)** | Locale-keyed phrase arrays used by chat, voice-pipeline, and native realtime fillers. Default: `{"en": ["One moment."], "fr": ["Un instant."]}`. | | **Localized status-tag instruction (JSON)** | Configures localized instructions used to generate status-tag-based filler messages. | | **Status Tags** | Uses the status tags the response model emits as fillers, which lets the model say what it's actually doing instead of emitting a generic wait message. | | **Generated Fillers** | Enables LLM-generated contextual filler messages instead of static filler text. Runtime uses a lightweight model to generate wait messages that are relevant to the current conversation. This provides more natural progress updates while adding a small amount of processing time during long-running operations. | | **Include emitted filler in agent context** | Lets later same-turn voice reasoning account for a contextual filler the runtime already emitted. It doesn't add the filler to conversation history, so this changes what the agent knows within the turn rather than what the transcript records. | | **Filler Model** | Selects the model that generates contextual fillers. | | **Output Tokens** | Caps the response length for built-in contextual filler generation. Fillers should be short, and a low cap enforces that more reliably than the prompt does. | | **Prompt Override Tokens** | Caps the response length when a filler prompt override is in use. Overrides usually ask for more than the built-in prompt does, which is why they carry a separate limit. | | **Conversation History** | Sets how many recent messages the filler prompt includes. More history makes fillers more specific and each generation call more expensive. | | **Previous Fillers** | Sets how many already-emitted fillers the prompt includes. Its purpose is avoiding repetition within a turn, so it matters most when the max per turn runs high. | | **Filler Prompt** | Selects a prompt-library version that replaces the built-in filler prompt. Select **Clone built-in** to start from the platform's prompt rather than an empty one. The prompt accepts placeholders for the user message, the conversation history, and previously emitted fillers. | #### Background audio Background audio is looped hold music the voice gateway plays during compute waits. It covers silence without the agent having to say anything. | Field | Description | | - | - | | **Background Audio Enabled** | Plays uploaded hold audio while the agent works. | | **Audio File** | Holds the WAV or MP3 clip the gateway fetches. | | **Start Delay** | Sets the silence before the audio begins. This is the field that most often stops background audio from working: a spoken filler interrupts the audio, so a filler that arrives first prevents the audio from starting at all. Either lower this delay or raise the spoken filler delay so one has room to begin before the other. | | **When To Play** | Chooses when background audio plays. Select at least one option. **Between sentences** plays only when barge-in is enabled and the text-to-speech voice supports streaming. When barge-in is disabled, background audio plays only **Before the agent starts responding**. | #### Greeting audio Greeting clips play as soon as a call connects, ahead of the agent's first turn. They cover the gap between answer and first response, which is the moment a caller is most likely to think the line is dead. #### Activity updates This section sets the visibility policy for what the runtime reveals about its own work. The policy is a ceiling rather than a guarantee, because channels apply only what they support. The exposure settings deserve particular care. Tool, workflow, and agent names describe your internal architecture, and a name that helps an internal user diagnose a problem tells an external user how your system is built. | Field | Description | | - | - | | **Fillers** | Exposes the status messages generated while runtime work is in progress. | | **Action Progress** | Exposes progress for memory, knowledge, workflow, tool start, and retry events. It's the safest form of progress reporting, since it says work is happening without saying what. | | **Tool Preview** | Controls whether tool input and output previews are hidden or redacted. Previews can carry data the user supplied moments ago, so redaction and hiding aren't equivalent choices. | | **Decisions** | Exposes safe decision labels as action progress. | | **Thought Summaries** | Exposes model and step thinking summaries when the runtime emits them. Summaries explain reasoning that the agent may not stand behind in its final answer, so exposing them can raise questions the response then contradicts. | | **System Notices** | Exposes authentication, approval, and waiting-for-input notices. These usually need to stay visible, because they tell the user that the agent is waiting on them rather than working. | | **Debug Details** | Exposes redacted debug activity meant for internal review. | | **Expose Tool Names** | Shows tool names in activity messages. | | **Expose Workflow Names** | Shows workflow and step names. | | **Expose Agent Names** | Shows agent names where the channel supports it. In multi-agent projects this reveals that a handoff occurred, which reads as competence internally and as confusion externally. | | **Maximum Text** | Caps visible characters per activity update. | ### Voice no-input defaults This section sets what happens when a voice caller goes silent. Silence is ambiguous, since it can mean the caller is thinking, has walked away, or didn't hear the prompt, and these settings decide how long the agent waits before treating it as a problem. | Field | Description | | - | - | | **Enabled** | Turns on no-input handling for voice sessions. | | **Mode** | Chooses between authored prompts and contextual generated prompts. Generated prompts fall back to the authored and default prompts, so choosing them adds a behavior rather than replacing one. | | **Timeout** | Sets the silence that must pass before a no-input prompt fires. Too short interrupts a caller who's reading something out, and too long lets a caller believe the call has dropped. | | **Max Retries** | Sets how many no-input prompts the agent issues before it takes the terminal action. | | **Relaxed Interval** | Sets the extended timeout interval used in dynamic mode. | | **Play Order** | Sets the order in which no-input messages play. | | **Play Style** | Sets how messages repeat across retries. Repeating the same prompt signals a system that isn't listening, so this choice affects how a stuck caller reads the failure. | ### Caller backchannel This section sets the project default for caller backchannel acknowledgements. These are language-neutral sounds, such as "uh-huh", that the runtime treats as acknowledgements rather than interruptions, so the agent keeps speaking. Every agent that doesn't set its own backchannel inherits these defaults. | Field | Description | | - | - | | **Enabled** | Turns on backchannel handling for the project. | | **Phrases** | Holds the acknowledgement phrases. This is one set for all callers, regardless of language, and the runtime sends it to the voice gateway. Type a phrase and press Enter to add it. | Backchannel is only effective with at least one phrase. If you turn it on without adding any, the page shows the warning **Backchannel is on but has no phrases**. ### On hold This section sets the project default for phrases a caller says to make the agent pause, such as asking it to wait a moment. Phrases are locale-aware. Every agent that doesn't set its own on-hold behavior inherits these defaults. On hold runs entirely in the runtime and needs no gateway change. | Field | Description | | - | - | | **Enabled** | Turns on on-hold detection for the project. | | **All locales (base)** | Holds phrases that apply in every language. Use it for phrases that work regardless of locale. | | **Add language** | Adds a language-specific phrase list. Select a language, then add the phrases callers use to ask for a pause in that language. | | **Grace (ms)** | Sets the grace period that applies to a hold. Default: 30000. | | **Max holds** | Sets the maximum number of holds allowed. Default: 2. | | **Suppress reply** | Controls whether the agent suppresses its reply when it detects an on-hold phrase. Off by default. | On hold is only effective with at least one phrase. If you turn it on without adding phrases in any language, the page shows the warning **On-hold is on but has no phrases in any language**. ### Barge-in continuation window When a caller interrupts (barges in) and speech recognition splits their sentence into separate phrase-finals, this section makes the runtime hold each one briefly and merge any follow-on phrase into a single turn. Without it, the agent would treat each fragment as its own turn and may answer a half-finished sentence. This is a project-wide default, and an agent can override it. | Field | Description | | - | - | | **Enabled** | Merges segmented barge-in speech into one turn. On by default. Turn it off to commit each phrase immediately, which is the legacy behavior. | | **Continuation window (ms)** | Sets how long the runtime holds a barge-in final while waiting for a continuation before it commits. Range: 300-2000 ms. Default: 700. A longer window catches slower speakers but delays the agent's response. | | **Maximum hold (ms)** | Sets the absolute ceiling on how long one turn can be held, regardless of merges. Range: 500-8000 ms. Default: 2500. | | **Maximum merges** | Sets the most phrase-finals the runtime folds into one committed turn. Range: 1-20. Default: 4. | | **Maximum characters** | Sets the most characters of merged transcript before the runtime forces a commit. Range: 1-20000. Default: 2000. | ### Voice speech recognition These are project-wide defaults. A channel connection can override them, so check the connection before you conclude the project value is what's running. | Field | Description | | - | - | | **Default minimum STT confidence** | Rejects transcripts below this confidence and treats them as no-input. Confidence scores are provider-relative rather than absolute, so a value that works for one speech provider won't carry over to another. Calibrate in staging before you set it in production, and set it to zero to disable the gate entirely. | ### PII redaction This section controls how the runtime handles personally identifiable information in conversations. Baseline credential and secret scrubbing protects logs and read APIs whether or not PII protection is on, so the settings here extend that protection rather than establishing it. | Field | Description | | - | - | | **Enable PII Protection** | Detects and redacts PII in conversations. It's the switch the rest of this section depends on. | | **Redact Input** | Redacts PII in user messages before they reach the model. Redaction is context-aware and exempts fields the agent is actively gathering, so turning it on doesn't break a flow that legitimately asks for a phone number. | | **Redact Output** | Redacts PII in agent responses before delivery. Its purpose is stopping the agent from echoing back PII the user already provided, which is a common way collected data reaches a transcript. | | **Classify explicit DTMF using project PII patterns** | Matches a completed keypad entry to the project's enabled PII patterns, so a recognized value carries a type and a validation status while anything else stays a generic token. Raw digits never reach the model either way, so this setting changes how an entry is labelled rather than whether it's protected. | | **Detection methods allowed** | Sets the ceiling on which detection engines the project's patterns may use. Each pattern still selects its own method on the PII Protection page, so this field decides what's available to them rather than what they do. | ### Lookup tables Lookup tables are the reference data the runtime uses to validate field values and perform fuzzy matching during extraction. Project lookup tables defined here are the canonical source for gather validation and fuzzy matching. Agent-local `LOOKUP_TABLES` remain available only as an experimental compatibility lane, so build new work on project tables. | Field | Description | | - | - | | **Table Name** | Names the table for reference from field validation. | | **Source** | Selects where the table's values come from, which decides whether you maintain them here or elsewhere. | | **Values** | Holds the comma-separated values for an inline table. | | **Case Sensitive** | Requires an exact case match. Leave it off for anything a user types, since typed input rarely matches your stored casing. | | **Fuzzy Match** | Accepts near matches. It rescues misspellings and transcription errors at the cost of occasionally accepting a value the user didn't mean, so it suits voice input more than typed input. | *** ## Data retention The **Data Retention** page sets how long evaluation and scoring data stays available to this project. **Navigation**: **Project** -> **Settings** -> **Data Retention** Retention is a deletion policy rather than a storage preference. Once a window passes, the data is gone and no longer available for trend analysis, so set these values around the longest comparison you expect to run rather than the storage you expect to use. | Category | Description | | - | - | | **Eval conversations** | Holds evaluation conversation transcripts. Transcripts are what let you re-read a failed evaluation rather than only seeing that it failed. | | **Eval scores** | Holds evaluation score records. Scores outlive their usefulness more slowly than transcripts, since trend comparison needs the numbers rather than the conversations. | | **Production scores** | Holds production scoring data, which is the basis for the quality trends in Insights. | | **Synthetic eval runs** | Holds synthetic evaluation run data. These runs are reproducible on demand, which is why they carry the shortest window. | The **Retention Windows** section below the summary sets the time-to-live in days for each category. Select **Save** to apply changes or **Reset** to restore defaults. *** ## Config variables The **Config Variables** page defines key-value pairs that the platform resolves at compile time through `{{config.KEY}}` syntax. **Navigation**: **Project** -> **Settings** -> **Config Variables** Use config variables for values that several agents share and that change between environments, such as endpoints, feature flags, and persona-specific content. The alternative is repeating the value in each agent, which turns an endpoint change into an audit. ### Add a variable Each variable is one key the platform substitutes wherever your agents reference it. The fields below define the key, its value, and the context a later reader needs. Select **Add Variable**, then complete the fields. | Field | Description | | - | - | | **Key** | Uniquely identifies the variable and forms the reference used in code. Changing a key later breaks every reference to it, so settle naming before the value spreads. | | **Value** | Holds what the platform substitutes at compile time. You can override it per deployment environment, which is the mechanism that lets one project target different endpoints. | | **Namespaces** | Groups related variables so a large set stays navigable. | | **Description** | Explains what the variable is for. It's the only context a later reader gets when deciding whether a variable is safe to change. | Config variables resolve at compile time rather than at runtime, so a changed value reaches your agents only after recompilation. *** ## Localization The **Localization** page manages locale JSON assets in Studio and syncs them through the project's Git integration. **Navigation**: **Project** -> **Settings** -> **Localization** The platform stores locale assets as project-level JSON files and exports them to Git under the same path contract that project import and export use. Keeping paths in the canonical shape is what lets assets round-trip cleanly, so a path you improvise here becomes a merge problem later. Counters at the top report the assets, locales, and shared assets the project holds. A status indicator reports when Git isn't connected, with a link to **Open Git Settings**. Until you connect Git, assets stay in project storage and don't reach version control, which means no diffs and no review. Search by locale, file path, or description, and narrow the list by locale or by scope when the asset count grows past browsing. ### Create a localization asset An asset is one locale JSON file holding translated strings, either shared across the project or scoped to a single agent. Creating one here puts it under the same path contract Git and project export use. Select **New Asset** to open the editor, then complete the fields and select **Create**. Saving writes the asset into project storage and keeps the Git export path aligned. | Field | Description | | - | - | | **Relative Path** | Sets the locale file path in the canonical locale and asset shape. This path determines where the asset lands in Git and whether import and export find it again. The path also indicates whether the asset is shared by all agents or used by a specific agent. | | **Description** | Gives translators and reviewers the context the JSON itself doesn't carry. | | **JSON Editor** | Edits the locale content directly. Select **Upload JSON** to bring in an existing file, or **Prettify** to reformat what's there. | * **For shared messages** : Use a shared locale asset for messages that can be used by multiple agents. For example: 'locales/en/\_shared.json\`. * **For Agent specific messages**: Use an agent-specific locale asset for messages used by a particular agent. For example: 'locales/en/agent\_name.json`, where `agent\_name\` is the name of the agent which can use this asset. ### Use Localized Messages in ABL Reference localized messages in ABL using the `{{locale.}}` syntax. ```yaml theme={null} AGENT: SupportBot FLOW: main LISTEN RESPOND: {{locale.greeting}} LISTEN RESPOND: {{locale.menu}} ``` The same localization key retrieves the message for the resolved language. # Integrations settings Source: https://koreai.mintlify.app/agent-platform/administration/integrations-settings Integrations connect your project to the external systems it depends on: a Git repository for version control, and the authentication credentials that let agents reach third-party services. ## Git integration The **Git** page connects a repository so agents and localization assets live under version control rather than only in Studio. **Navigation**: **Project** -> **Settings** -> **Git** ### Connect a repository Select **Connect Repository**, complete the fields, then select **Test connection**. Test before you connect, because a wrong branch, path, or credential fails at the first sync rather than at connection time. Select **Connect** to finish. | Field | Description | | - | - | | **Provider** | Selects the Git hosting service. It determines which credential types the **Auth Profile** picker accepts, so set it before you prepare a credential. | | **Repository URL** | Points at the repository over HTTPS or SSH. It addresses the repository only. The folder this project occupies inside it comes from **Sync Path** further down. | | **Repository Layout** | Decides whether this project shares a repository with others or takes one of its own. Choose a shared folder when you want every project's history in one place and one set of repository permissions. Choose a dedicated repository when a project needs its own access control or its own release cadence. | | **Branch** | Sets the branch the project reads and writes. Point non-production projects at a branch other than the one your builds run from, so an experiment in Studio doesn't reach it. | | **Sync Path** | Sets the folder inside the repository that this project owns. Use the repository root when the project has the repository to itself, and a named folder when several projects share one. | | **Path Preview** | Shows the location the platform resolved from your layout, path, and branch. Read it before you connect. It's the one place the three fields appear as the single destination they combine into. | | **Auth Profile** | Selects the stored credential the sync uses. Select **Create Git token profile** to build one without leaving the dialog. | | **Conflict Strategy** | Decides what happens when Studio and the repository both change the same thing. Settle this before your first push rather than during your first conflict, since the strategy determines which side loses work. | ### Credentials by provider The picker lists only profile types that carry a token or key the sync can present on its own. OAuth App profiles never appear, whichever provider you choose, because they authorize tools and connectors interactively and the sync has no user to prompt. A working OAuth App profile for the same provider is therefore not a substitute, and its absence from the list isn't a fault. | Provider | What to prepare | | - | - | | **GitHub** | A Bearer Token profile holding a personal access token, or an SSH Key profile. | | **GitLab** | A Bearer Token or API Key profile holding a project access token, or an SSH Key profile. OAuth Token grants don't appear here either. | | **Bitbucket** | A raw repository or workspace access token, or an SSH Key profile. Atlassian API tokens need storing in the combined email and token form rather than as the token alone, which is the most common reason a valid Bitbucket credential fails. OAuth Token grants don't appear here either. | | **Azure DevOps** | A Bearer Token or API Key profile holding an Azure DevOps personal access token. Grant it Code read and write, add Code manage if you want branch and pull request operations, and add a webhook subscription scope if you want automatic sync. SSH Key and OAuth profiles aren't supported for this provider at all, so plan on a token. | ## Once the integration is live, you can push agent definitions, pull repository changes into Studio, and resolve conflicts when edits collide. ## Auth profiles An auth profile is a reusable credential that connectors, tools, and integrations reference by name. Defining credentials here rather than inside each tool means a rotation touches one profile instead of every tool that uses it. **Navigation**: **Project** -> **Settings** -> **Auth Profiles** ### Project and workspace profiles Every profile carries a scope, and the scope decides where it's managed and who can change it. Check it before you plan any credential work, because some of the profiles in this list aren't yours to edit. | Scope | Description | | - | - | | **Workspace** | Credentials the tenant shares across every project. They appear in this list so you can see what's available, but the actions column offers only **Manage in Workspace**. Edit them under workspace administration, where a change reaches every project at once. | | **Project** | Credentials this project owns. Only these carry the enable toggle, edit, and overflow actions in the project list. | The distinction decides who can act. If a workspace profile needs different credentials for one project, create a project profile rather than asking for the workspace one to change, since editing the shared profile affects projects you don't own. ### Find a profile Environment tabs across the top narrow the list by environment, and each tab carries the number of profiles that resolve for it. Filters on the right narrow by status, authentication type, and source, and a search box matches on profile name. The counts differ between tabs because environments inherit credentials they don't define. A profile appears under an environment whether it holds credentials specific to that environment or falls back to the default set. ### Profile list The list holds every profile this project can reach, whether it owns them or inherits them from the workspace. The columns tell you what each profile authenticates, where it's managed, and whether it still works. | Column | Description | | - | - | | **Name** | Identifies the profile. This is the name tools reference, so renaming affects everything bound to it. | | **Auth Type** | Reports the authentication mechanism the profile implements. | | **Scope** | Reports whether the workspace or this project owns the profile, and therefore where you can edit it. | | **Owner** | Names the person who created the profile. It's the first thing to check before you disable a credential you didn't set up. | | **Created At** | Dates the profile. Useful when you're auditing which credentials predate a rotation policy. | | **Status** | Reports whether the profile is active or revoked. A revoked profile stays in the list rather than disappearing, so anything still referencing it fails with a traceable cause rather than a missing reference. | ### Create a profile Select **New Profile** and choose one of two routes. The route decides how much you configure by hand and how much the platform already knows. | Route | When to use it | | - | - | | **Integration Profile** | The service you're connecting to is one the platform already supports. The integration carries the authentication mechanisms that service accepts, so you supply credentials rather than working out how the provider expects to be authenticated. | | **Custom Profile** | The service isn't in the integration list, or you need a mechanism the integration doesn't cover. You choose the authentication type yourself and complete every field. | Prefer the integration route where it's available. It removes the part of this task most likely to go wrong, which is matching a provider to the right authentication mechanism and endpoint set. ### Integration profiles The integration picker lists the services the platform supports, grouped by what they're for: communication, productivity, storage, CRM and sales, service management, AI and development, and document processing. Each tile names the service, the authentication types that service accepts, and how many profiles the project already holds for it. Read the profile count first, because an existing profile is usually a credential to reuse rather than a duplicate to create. The tiles also tell you what you're in for before you commit. Some services accept a single authentication type, some accept several and leave the choice to you, and a few need no credential at all. Search by name when the service you want isn't in view, then select **Continue**. ### Custom profiles Choosing **Custom Profile** takes you to the authentication type selector. Choose the type the provider requires. The type determines the configuration and secret fields you complete next, and you can't change the fundamental mechanism afterward without creating a new profile. **Basic authentication** | Type | When to choose it | | - | - | | **API Key** | The provider issues a long-lived static key and expects it on every request. It's the simplest type and handles no expiry or refresh, so rotation stays entirely your responsibility. | | **Bearer Token** | The provider issues a static token that belongs in the Authorization header. It differs from API Key mainly in placement, so follow what the provider's documentation specifies. | | **Basic Auth** | The provider still accepts a username and password pair. These credentials rarely expire on their own, which makes them the ones most often forgotten in a rotation. | | **Custom Header** | The provider needs more than one header, or a header arrangement the API Key type can't express. | **OAuth 2.0** | Type | When to choose it | | - | - | | **OAuth 2.0 App** | A user grants access to their own account. This is the only type that produces per-user credentials, so choose it when the agent acts on behalf of the person it's talking to rather than on behalf of your organization. | | **Client Credentials** | The project itself is the client and no end user is involved. You get token refresh without ever prompting anyone to authorize. | | **Password Credentials (ROPC)** | The provider offers no alternative. The flow requires your project to hold the resource owner's username and password, which is why most providers now discourage it. Treat it as a compatibility path rather than a design choice. | **Advanced** | Type | When to choose it | | - | - | | **AWS IAM (SigV4)** | The AWS service signs requests rather than accepting a token. | | **Azure AD** | The provider authenticates through Microsoft Entra ID. | | **Kerberos-SPNEGO Auth** | An internal enterprise service authenticates through the domain rather than through a token. | | **SAML** | The provider expects a SAML assertion rather than a credential on the request. | | **WS-Security** | A SOAP service expects WS-Security headers. | **Transport security** | Type | When to choose it | | - | - | | **mTLS** | The provider authenticates the client at the transport layer with a certificate rather than at the application layer with a credential. It pairs with network controls in a way token-based types can't. | ### Fields common to every profile These fields appear whichever authentication type you choose. They cover how you identify the profile, who supplies the credential, and who can use it. | Field | Description | | - | - | | **Profile Name** | Names the profile for reference from tools and connectors. Choose a name that carries the provider and the environment, because a list of profiles sorted by name is how anyone later works out what to rotate. | | **Description** | Records what the profile is for. It's the only context another member gets when deciding whether a credential is safe to disable. | | **Usage Mode** | Decides who supplies the credential and when. It's the most consequential choice on the form, because it also determines the profile's scope and whether users ever see an authorization prompt. | | **Scope** | Reports the sharing model the platform derived from the usage mode. You don't set it directly, so change the usage mode when you need a different scope. | | **Visible to project members** | Controls whether project members can use the profile. Turn it off for credentials that only specific automated paths should reach. | **Usage modes** | Mode | Behavior | | - | - | | **Preconfigured** | An administrator supplies credentials at setup time and users never authorize anything. The resulting scope is shared, so every session uses the same credential and the provider sees one identity for all your traffic. | | **Preflight** | The user authorizes upfront, before the session starts. The resulting scope is per-user, so the agent acts as the person it's talking to and the provider's own permissions apply per individual. | **Block until authorized** appears with authorization-based modes. When it's on, the session holds incoming messages and the welcome message until the user finishes authorizing. It trades a slower greeting for the certainty that the agent never starts work it can't complete, which suits agents whose first action always needs the credential. ### Secrets Every type collects its secrets in a separate section. The platform encrypts them at rest and never displays them again after saving. That's worth planning around: you can replace a secret but you can't read one back. Record credentials in your own secret store at the time you create the profile, because the platform won't serve as your copy. ### Environment credentials An existing profile holds a credential set per environment. Environments that carry no credentials of their own fall back to the default set, and the editor states which ones are falling back. Fallback is a convenience and a risk in the same mechanism. A profile with only default credentials works in production immediately, using the same credential as development. Add environment-specific credentials wherever production must not share an identity, a rate limit, or an audit trail with your lower environments. ### Configuration by authentication type The configuration and secret fields below are specific to each type. Complete them, then select **Create Profile**. **API Key** | Field | Description | | - | - | | **Header Name** | Names the header or parameter that carries the key. | | **Placement** | Decides whether the key travels in a header or a query parameter. Prefer a header where the provider supports it, since query parameters reach server logs and browser history. | | **Prefix** | Adds a string ahead of the key value for providers that expect one. | | **API Key** | The key itself. | **Bearer Token** | Field | Description | | - | - | | **Authorization Prefix** | Sets the scheme name the provider expects ahead of the token. Change it only when the provider documents something other than the standard scheme. | | **Token** | The token itself. | **Basic Auth** | Field | Description | | - | - | | **Username** and **Password** | The credential pair the provider accepts. | **Custom Header** | Field | Description | | - | - | | **Headers** | Holds one or more header name and value pairs, both required on each row. Add a row for each header the provider needs. | **OAuth 2.0 App** | Field | Description | | - | - | | **Discovery / Server URL** | Points at the provider's base URL. Select **Discover and fill** to populate the endpoints, scopes, and PKCE settings automatically. Try this before completing the endpoint fields by hand, since a discovered configuration matches what the provider actually publishes. | | **Authorization URL** | The endpoint that presents the consent screen to the user. | | **Token URL** | The endpoint that exchanges an authorization code for a token. | | **Refresh URL** | The endpoint that renews an expired token. Without it, users reauthorize whenever the token expires. | | **Scopes** | Requests the permissions the agent needs. Leave it empty for providers that neither require nor advertise scopes. Requesting more than the agent uses makes the consent screen harder for users to accept. | | **Require PKCE** | Adds a proof key to the authorization exchange. Enable it wherever the provider supports it, and expect it to be mandatory for providers that follow OAuth 2.1. | | **PKCE Challenge Method** | Selects the challenge algorithm. | | **Additional Authorization Parameters** | Appends extra key and value pairs to the authorization URL for providers that need parameters outside the standard set. | | **Callback URL** | The redirect the provider must send users back to. Copy it into the provider's authorized redirect list before you authorize, because authorization fails at the provider rather than in Studio when it's missing. | | **Client ID** and **Client Secret** | The application credentials the provider issued. | **Client Credentials** | Field | Description | | - | - | | **Authorize Using** | Selects how the client proves its identity to the token endpoint. | | **Token URL** | The endpoint that issues tokens. | | **Refresh Token URL** | The endpoint that renews them. | | **Scopes** | Requests permissions, separated by spaces. | | **Client ID** and **Client Secret** | The application credentials the provider issued. | **Password Credentials (ROPC)** | Field | Description | | - | - | | **Token URL** | The endpoint that issues tokens. | | **Scopes** | Requests permissions, separated by spaces. | | **Audience** | Names the intended recipient of the token for providers that require it. | | **Client ID**, **Client Secret**, **Username**, **Password** | The application credentials plus the resource owner's own credentials, which this flow needs and the other OAuth types don't. | **AWS IAM (SigV4)** | Field | Description | | - | - | | **Region** | Sets the region used in the signature. A mismatch here produces a signature error rather than a permission error, which is worth knowing when you're diagnosing a failure. | | **Service** | Names the AWS service the signature targets. | | **Bucket** | Names the S3 bucket, for S3 targets only. | | **Endpoint** | Points at an S3-compatible endpoint when the target isn't AWS itself. | | **Access Key ID** and **Secret Access Key** | The IAM credentials. | | **Session Token** | Carries the token for temporary credentials. Profiles built on temporary credentials stop working when those credentials expire, so use long-lived credentials for anything running unattended. | **Azure AD** | Field | Description | | - | - | | **Azure Tenant ID** | Identifies the directory that issues tokens. | | **Resource** | Names the resource the token grants access to. | | **Scopes** | Requests permissions, separated by spaces. | | **Client ID** and **Client Secret** | The registered application credentials. | **Kerberos-SPNEGO Auth** | Field | Description | | - | - | | **Service Principal URL** | Identifies the service principal the ticket targets. | | **Auth Check URL** | The endpoint used to verify that authentication succeeded. | | **Authentication Type** | Sets the SPNEGO GSS flag the enterprise authentication path uses. | **SAML** | Field | Description | | - | - | | **IdP Metadata URL** | Points at the identity provider's metadata. | | **Entity ID** | Identifies this service to the identity provider. | | **ACS URL** | The endpoint that consumes the assertion. | | **Private Key** and **Certificate** | The key material used to sign and verify assertions. | **WS-Security** | Field | Description | | - | - | | **mustUnderstand** | Sets the SOAP mustUnderstand flag on the security header, which tells the recipient it must process the header rather than ignore it. | | **Password digest algorithm** | Selects the digest algorithm. Move away from the default only when the SOAP provider explicitly requires it, since many older SOAP stacks reject the stronger algorithm. | | **Username**, **Password**, **Certificate** | The credential the header carries, with an optional certificate. | **mTLS** | Field | Description | | - | - | | **Client Certificate (PEM)** and **Client Private Key (PEM)** | The certificate and key that identify this client during the TLS handshake. | | **CA Certificate (PEM)** | Supplies the certificate authority when the provider uses one the platform doesn't already trust. | ### Authorize an existing profile Profiles that use an authorization-based usage mode need someone to complete the flow before the credential works. Open the profile and select **Authorize**, then select **Save Changes** to keep any edits you made alongside it. # Configure Microsoft Foundry Source: https://koreai.mintlify.app/agent-platform/administration/microsoft-foundry Use models from Microsoft Foundry, including OpenAI, Anthropic, Meta, and Mistral models, through a single provider integration. Microsoft Foundry hosts models from multiple providers, including OpenAI, Anthropic, Meta, Mistral, and others, through a unified deployment platform. The Microsoft Foundry catalog lets you add these models, and the platform automatically routes inference calls using the appropriate wire format, headers, and authentication scheme. ## Key benefits * **One provider for multiple models** — Add Microsoft Foundry once and select models from different model families. * **Automatic format routing** — The platform automatically detects the API format required by the model and routes requests accordingly. * **Flexible authentication** — Use an API key or an Azure AD Service Principal. * **Backward compatibility** — Existing connections continue to work without changes. *** ## Supported models The platform supports any model deployed on Microsoft Foundry. The model catalog lists popular models for easy setup. If your deployed model is not listed in the catalog, you can add it using the **Custom Model** option. Custom models use the OpenAI Chat format by default. You can override the format if needed. *** ## Prerequisites Before you configure the provider, ensure you have the following: 1. An **Azure AI Foundry resource** with at least one model deployed. 2. **The resource endpoint**, available in the Azure portal under your Foundry resource's overview. For example, `https://your-resource.openai.azure.com`. 3. **Authentication credentials**, either: * An **API key** from the Azure portal (**Keys and Endpoint** section), or * A **Service Principal** with the `Cognitive Services User` role on the resource for automatic token management. *** ## Authentication methods Microsoft Foundry supports the following authentication methods: | Authentication method | Description | Recommended for | | - | - | - | | **API key** (Manual Credential) | Uses an API key from the Azure portal to authenticate requests. The platform sends the key with each request. | Quick setup, individual use, and development or testing environments | | **Service Principal** | Enterprise-grade authentication using OAuth 2.0 client credentials. The platform automatically acquires, caches, and refreshes bearer tokens — no manual token rotation required. | Production workloads, team or organization deployments, and environments that require centralized credential management | For information about configuring each authentication method, see: * [Add a model with an API key](#add-a-model-with-an-api-key) * [Add a model with a Service Principal](#add-a-model-with-a-service-principal) *** ## Add a model with an API key Use an API key to authenticate requests to Microsoft Foundry. The platform sends the key with each request. * For OpenAI-format models, the key is sent in the `api-key` header. * For Anthropic-format models, it is sent in the `x-api-key` header. **Procedure:** 1. Go to **Admin > LLM Providers**. 2. In the **Model Catalog**, select **Add Model**. 3. Select **Microsoft Foundry > Add manually**. 4. Select one of the following options: * **Choose from popular models** — Select a model from the curated list of Foundry models. * **Enter deployment details** — Enter the deployment details for a model that is not available in the curated list. 1. **Display Name** — Enter a name for the model. 2. **Model ID** — Enter the deployment or model ID. 3. **Tier** — Select the appropriate model tier. 5. Select **Add to Workspace**. 6. From the model list, locate the Microsoft Foundry model and expand the model row. 7. Under **Connections**, select **Add Key**. 8. In the **Add Connection** dialog, select **Create new credential** and enter the credential details. | Field | Description | | - | - | | **Name** | A display label for this connection. | | **Provider** | Microsoft Foundry is selected. | | **API Key** | Enter your Azure API key. | | **Auth Mode** | Only shown for Anthropic-format models (`API Key` or `Entra Token`). | | **Endpoint** | Enter the base URL of your Foundry resource, for example, `https://your-resource.openai.azure.com`. Do not include a path suffix. The platform automatically appends the appropriate API path based on the model format. | | **Deployment ID** | Deployment name in your Azure OpenAI resource. | | **Anthropic Version** | Only for Anthropic models (for example, `2023-06-01`). | 9. Select **Test Connection** to verify the connection. 10. Select **Save**. *** ## Add a model with a Service Principal Use a Service Principal to authenticate with Microsoft Foundry using OAuth 2.0. The authentication flow is as follows: * Register a Service Principal in Azure AD and grant it access to your Foundry resource. * Create an Azure AD Auth Profile in the platform with the Service Principal credentials. * When connecting a model, select the auth profile instead of providing an API key. * At inference time, the platform acquires a short-lived token (`https://cognitiveservices.azure.com/.default` scope), caches it, and refreshes it automatically before expiry. **Token lifecycle**: Service Principal tokens are acquired on demand, cached, and refreshed before expiry. Tokens are invalidated when the auth profile is updated or deleted. If token acquisition fails, the LLM call returns an authentication error. **Procedure:** 1. Go to **Admin > LLM Providers**. 2. In the **Model Catalog**, select **Add Model**. 3. Select **Microsoft Foundry**. 4. Select **Use Service Principal**. 5. Select **Create a new profile**. 6. Enter the profile details: | Field | Required | Description | | - | - | - | | Auth Type | Yes | Service Principal (Azure AD). | | Profile Name | Yes | Enter a name for the profile. | | Description | No | Enter a description for the profile. | | Visible to project members | — | Specify whether project members can use this profile. | | Tenant (Directory) ID | Yes | Enter your Azure AD tenant ID. | | Subscription ID | Yes | Enter your Azure subscription ID. | | Application (Client) ID | Yes | Enter the Service Principal application ID. | | Client Secret | Yes | Enter the Service Principal secret value. | | Default scope | No | Optionally specify a default scope. | | Resource Group | No | Select the resource group that contains the Foundry account. | | Foundry Account | No | Select the Foundry account. | | Foundry Project | No | Select the Foundry project. | 7. Select **Create profile**. 8. In the **Add Model** dialog, select the Service Principal profile you created. 9. If the profile does not have a default scope, select the **Resource Group**, **Foundry Account**, and **Foundry Project**. 10. Select **List deployments**. The platform retrieves the model deployments available in the specified Foundry scope. *** ## Endpoint Reference | Model Format | Full Endpoint (constructed by platform) | API Key Header | Bearer Token Header | | - | - | - | - | | OpenAI Chat | `{your-base-url}/openai/v1/chat/completions` | `api-key: ` | `Authorization: Bearer ` | | Anthropic Messages | `{your-base-url}/anthropic/v1/messages` | `x-api-key: ` | `Authorization: Bearer ` | *** ## Migration from the legacy provider If you previously configured models using the **Microsoft Foundry (Anthropic)** provider (`microsoft_foundry_anthropic`): * No action is required. Existing models, connections, and credentials continue working. The legacy provider name is treated as an alias for the unified Microsoft Foundry provider. * New models should be added using the unified Microsoft Foundry provider, which supports all model families. * The legacy provider card is no longer shown in the UI for new configurations, but all existing runtime behavior is preserved. *** ## Troubleshooting | Symptom | Likely cause | Resolution | | - | - | - | | "Test Connection" fails with 401 | Invalid API key or expired token | Verify the key in Azure portal; for Service Principal mode, check that the secret hasn't expired. | | "Test Connection" fails with 404 | Incorrect endpoint or model not deployed | Confirm the base URL and that the model is deployed on the resource. | | Model not in catalog | Model is not listed in the catalog | Use **Custom Model** and specify the deployment name manually. | | Auth token acquisition failed | Service Principal credentials invalid or insufficient permissions | Verify Tenant ID, Client ID, and Client Secret; ensure the Service Principal has the **Cognitive Services User** role. | | Wrong response format or parsing errors | Model format mismatch | Verify the model is correctly identified; use format override if using a custom model. | *** ## Limits and considerations * **Rate limits** are governed by your Azure AI Foundry resource tier, not by this platform. * **Token caching** (Service Principal mode) uses preemptive refresh. In rare cases during Azure AD outages, cached tokens may expire without refresh. Calls fail with a clear authentication error and automatically recover when Azure AD is available again. * **Streaming** works identically for all models; no special configuration is required. * **Vision and tool use** capabilities depend on the specific model deployed, not the provider configuration. The model catalog shows supported capabilities for each model. *** ## FAQs | Question | Answer | | - | - | | **Do I need to specify which API format my model uses?** | No. The platform detects the API format automatically from the model catalog. For custom models that are not in the catalog, the platform defaults to the OpenAI format. | | **Can I use the same endpoint for multiple models?** | Yes. If you have multiple models deployed on the same Foundry resource, they share the same base endpoint. Create separate model entries and point their connections to the same URL. | | **What's the difference between an API key and a Service Principal?** | An API key requires you to provide a key from the Azure portal. A Service Principal uses centrally managed credentials, short-lived tokens, automatic token refresh, and Azure RBAC for fine-grained access control. | | **Will my existing Anthropic-on-Foundry setup break?** | No. The legacy provider is fully supported as a runtime alias. Existing configurations continue to work without changes. | # Project settings overview Source: https://koreai.mintlify.app/agent-platform/administration/project-settings-overview Project settings give administrators and developers control over who can access the project, which models and tools are available, how the runtime behaves, and what security and compliance policies the system enforces. The **Settings** section covers membership, API keys, model configuration, runtime tuning, data retention, integrations, agent behavior overrides, PII protection, and channel policies. To access **Settings**, navigate to your project and select **Settings** from the sidebar. ## Before you begin * Most settings pages require **Admin** access. Developers can view settings but can't modify membership, API keys, or security controls. * Adding project members requires them to exist as workspace members. Only workspace **Owners** and **Admins** can invite new people into the workspace. * Model configuration requires at least one LLM provider configured at the workspace level under **Admin** → **Models**. * Git integration requires a repository URL and authentication (SSH key or personal access token) prepared in advance. ## Access project settings **Navigation**: **Project** → **Settings** Membership, API keys, models, runtime config, data retention, variables, and localization. Git version control and authentication profiles for connectors. Behavior profiles, alerts, human handoff, agent assist, PII protection, API access, attachments, and omnichannel support. Response templates, imported modules, publishing, and trace dimensions. LLM thinking, task prompts, escalation templates, and project deletion. # Workspace account Source: https://koreai.mintlify.app/agent-platform/administration/workspace-account Manage top-level workspace configuration, secrets, billing, and channel integrations. The **Account** section is your workspace's administrative hub — where you control workspace identity, manage tool credentials, monitor usage, and connect external channels. * **Navigation**: **Settings** > **Account** * **Required role**: Owner or Administrator *** ## Workspace settings Manage your workspace name, URL slug, and configuration from **Settings** > **Account** > **Workspace Settings**. ### General | Property | Description | | - | - | | Workspace Name | The display name for your workspace. | | URL Slug | Lowercase letters, numbers, and hyphens only. Used in URLs. | Click **Save Changes** to apply updates. ### Workspace info The **Workspace Info** section shows the following read-only details about your workspace. | Property | Description | | - | - | | Workspace ID | The unique identifier for this workspace. | | Status | The current status of the workspace (for example, `Active`). | | Created | The date the workspace was created. | ### Production approval policy Set the workspace-wide baseline for how strictly production deployments are gated. Projects inherit this baseline unless they set their own override. A badge indicates whether the policy is **Overridden at this workspace** or using the platform default, alongside the **Effective** policy currently applied (for example, *Standard - workspace baseline*). Drag the **Approval strictness** slider to one of three positions, from **Strict** to **Open**. The **Standard** position requires approval, security, and eval sign-off; the publisher may approve their own version. Click **Save policy** to apply. ### Call recording **Call Recording** lets you capture voice call audio, store it, and play it back from the session it belongs to. Recording is disabled by default at every level, so no audio is captured until an account admin and a project admin both turn it on. Call recording is an Enterprise feature. If the Call Recording section does not appear in Workspace Settings, your workspace doesn't have it enabled. Contact your Kore representative to get it enabled. You can't enable it in the platform. ### Danger zone Deleting a workspace is permanent and removes all projects, members, and data. This action cannot be undone. Click **Delete Workspace** to permanently delete the workspace. You are prompted to confirm before the action completes. *** ## Secrets Manage workspace tool secrets, proxy configurations, and OAuth tokens from **Settings > Account > Secrets**. The Secrets page has three tabs: **Proxy Configs**, **OAuth Tokens**, and **External Vaults**. ### Types of secrets * Auth Profiles: API keys and credentials used by agent tools to call external services (for example: CRM API key). * LLM provider credentials: API keys for LLM providers (for example: Anthropic, OpenAI, Azure, Google, Bedrock). * OAuth tokens: Encrypted access and refresh tokens for OAuth 2.0 connectors (for example: Slack, Google Drive, Salesforce). * Proxy credentials: Proxy authentication credentials (username, password, certificates) for organizations routing outbound traffic through a proxy. * External vault references: Pointers to secrets held in your own cloud vault, resolved at execution time. ### Proxy configs The **Proxy Configs** tab lists HTTP proxy configurations for outbound tool requests, with a running count (for example, *0 configs*) and a refresh icon to reload the list. **Add a proxy config:** 1. Go to **Settings > Account > Secrets**. 2. Click the **Proxy Configs** tab. 3. Click **Add Config**. 4. In the **Add Proxy Config** dialog, enter: | Field | Description | | - | - | | **Name** *(required)* | A descriptive label for this proxy, for example *Corporate Proxy*, *EU Proxy*. | | **Proxy URL** *(required)* | The proxy server address, for example `https://proxy.example.com:8080`. | | **Auth type** *(required)* | The authentication type the proxy expects. Default: **None**. | | **Environment** *(required)* | The environment this proxy applies to, or **All environments (global)** to apply it everywhere. | | **URL patterns** *(required)* | Comma-separated patterns for outbound requests that should route through this proxy, for example `*.example.com, api.partner.com`. | | **Bypass patterns** *(optional)* | Comma-separated patterns to exclude from the proxy, for example `localhost, *.internal`. | | **Priority** *(required)* | Determines evaluation order when multiple proxy configs could match the same request. Default: `0`. | | **CA certificate** *(optional)* | PEM-encoded CA certificate content, if the proxy uses a custom or private CA. | | **mTLS client certificate** *(optional)* | PEM-encoded client certificate, for mutual TLS. | | **mTLS client private key** *(optional)* | PEM-encoded private key matching the client certificate. | 5. Toggle **Enable immediately** to activate the proxy as soon as it's saved. 6. Optionally, click **Test connection** to check the current proxy URL, authentication, and TLS settings before saving. 7. Click **Add Config**. ### OAuth tokens The **OAuth Tokens** tab lists all third-party providers authorized via OAuth. Tokens appear here when tools request authorization. ### External vaults The **External Vaults** tab lets you register a secrets vault in your own cloud directory. Secret values are read at execution time and are never stored by the platform - only a reference to the vault entry is kept. Currently only **Azure Key Vault** is available. **AWS Secrets Manager** and **GCP Secret Manager** appear in the provider list but are not yet selectable. **Add an external vault:** 1. Go to **Settings > Account > Secrets**. 2. Click the **External Vaults** tab. 3. Click **Add Vault**. 4. In the **Add Vault** dialog, enter: | Field | Description | | - | - | | **Provider** | The vault provider. Currently **Azure Key Vault**. | | **Name** | An identifier for this vault. Lowercase letters, digits, and underscores only. Used in `{{secret....}}` references and can't be changed later — choose it deliberately. | | **Vault URL** | Your vault's URL, for example `https://my-vault.vault.azure.net`. | | **Azure Tenant ID** | Your Azure AD tenant ID. | | **Authentication** | How the platform authenticates to your vault. See **Authentication options** below. | 5. Click **Add Vault**. **Authentication options** | Option | Description | | - | - | | **Platform managed (recommended)** | Kore governs the credential, rotation, and trust settings; these are read-only here. | | **Service principal** | Use a credential you control. Select an existing **Azure AD auth profile** holding the service principal's client ID and secret - or create one first if none exist. | *** ## Billing and usage View your subscription plan, manage credits, and configure usage alerts from **Settings > Account > Billing & Usage**. ### Plan and credits | Card | Description | | - | - | | Current Plan | Your active subscription tier. Click **Upgrade Plan** to move to a higher tier. | | Credit Balance | Credits allocated to your workspace. Displays `No credits allocated` if none are assigned. | | Top Up Credits | Add credits to your workspace balance. | For custom deals, dedicated resources, BYOK encryption, and enterprise SLAs, click **Contact Sales**. ### Usage and billing units Filter billing unit consumption by: * **Time range**: 7 days, 30 days, or 90 days. * **All Projects**: Filter by a specific project or view all projects together. ### Alert configuration Configure alerts to be notified when usage approaches defined thresholds. Existing alerts are listed with their type, threshold, channel, and target. **Add an alert rule:** 1. Click **Add Alert**. 2. In the **Add Alert Rule** dialog, enter: * **Alert Type**: The type of alert (for example, Usage Threshold). * **Threshold (%)**: The usage percentage at which the alert triggers (for example, 80). * **Channel**: The notification channel (for example, Webhook, Email). * **Target (URL)**: The webhook URL or email address to notify. * **Cooldown (minutes)**: Minimum time between repeated alert notifications (for example, 60). 3. Click **Add Alert**. *** ## Connectors Manage channel connections and SDK channel integrations from **Settings > Account > Connectors**. The Connectors page has two tabs: **Channel Connections** and **SDK Channels**. ### Channel connections The **Channel Connections** tab lists all configured channel connections. Click **Add Connection** to set up a new channel. #### Supported connectors **Messaging platforms** | Connector | Auth type | Capabilities | | - | - | - | | Slack | OAuth 2.0 | Send/receive messages, manage channels, file uploads. | | Microsoft Teams | OAuth 2.0 | Send/receive messages, adaptive cards. | | WhatsApp Business | API key | Send/receive messages, templates, media. | | Telegram | Bearer token | Send/receive messages, inline keyboards. | **Communication** | Connector | Auth type | Capabilities | | - | - | - | | Twilio | API key + secret | SMS, voice calls, WhatsApp via Twilio. | | SendGrid | API key | Transactional and marketing email. | | Email (SMTP) | Basic auth | Send email via any SMTP server. | **CRM and business tools** | Connector | Auth type | Capabilities | | - | - | - | | Salesforce | OAuth 2.0 | CRUD operations on objects, SOQL queries. | | HubSpot | OAuth 2.0 | Contacts, deals, companies, tickets. | | Zendesk | API key / OAuth 2.0 | Tickets, users, organizations. | **Webhooks** | Connector | Auth type | Capabilities | | - | - | - | | Inbound webhook | None / HMAC signature | Receive events from external systems. | | Outbound webhook | API key / Bearer / Custom | Send events to external endpoints. | **Add a channel connection:** 1. Go to **Settings** > **Account** > **Connectors**. 2. Click the **Channel Connections** tab. 3. Click **Add Connection**. 4. In the **Add Channel Connection** dialog, enter: * **Connection Name**: A descriptive name for this connection (for example, `Production Slack`). * **Channel Type**: Select the channel platform: Slack, Microsoft Teams, Email, WhatsApp, or Voice. 5. Enter the credentials for the selected channel type. For Slack: * **Bot Token**: The Slack bot token (`xoxb-...`). * **Signing Secret**: The Slack app signing secret. * **Webhook URL**: The Slack webhook URL (`https://hooks.slack.com/`). 6. Click **Create Connection**. ### SDK channels The **SDK Channels** tab lists all SDK channels configured for programmatic access to your agents. Each SDK channel provides a unique API key for web widget and mobile app integrations. | Property | Description | | - | - | | Name | The channel name. | | Environment | The environment this channel is associated with (dev, staging, or production). | | API Key | The public API key for this SDK channel. Click the copy icon to copy it. | | Rate Limit | The maximum requests per minute allowed for this channel. Unlimited if not set. | | Status | Whether the channel is **Enabled** or **Disabled**. | | Actions | Edit or delete the channel. | **Add an SDK channel:** 1. Click **Add Channel**. 2. In the **Add SDK Channel** dialog, enter: * **Channel Name**: A descriptive name (for example, *Web Widget Production*). * **Project**: The project this channel is associated with. * **Environment**: The target environment: dev, staging, or production. * **Enabled**: Toggle on to activate the channel immediately. * **Rate Limit (RPM)**: Maximum requests per minute. Leave empty for unlimited. * **Allowed Origins**: Comma-separated list of allowed origins for browser-based integrations (for example, `https://example.com`, `https://app.example.com`). 3. Click **Create Channel**. # Workspace analytics Source: https://koreai.mintlify.app/agent-platform/administration/workspace-analytics Monitor agent performance, sessions, request-level traces, and individual LLM generations across your workspace. **Workspace Analytics** gives you visibility into how agents behave in production — from aggregate metrics to individual session transcripts and step-level execution logs. * **Navigation**: Settings > Analytics * **Required role**: Owner, Admin, or Operator; Member for project-scoped views. Use the **All Projects** dropdown and time range controls (24h, 7d, 30d, 90d) to filter data across all tabs. *** ## Agents The **Agents** tab shows aggregate performance metrics for all agents in the selected project and time range. Agent performance metrics appear once agents start processing sessions. ### Metrics | Metric | Description | | - | - | | Total sessions | Number of agent sessions in the selected time range. | | Avg. session duration | Mean time from session start to last message. | | Resolution rate | Percentage of sessions that reached a successful outcome. | | Avg. messages per session | Mean number of user and agent messages per session. | | Total tokens consumed | Input and output tokens across all models. | | Total estimated cost | Calculated LLM cost for the period. | | Error rate | Percentage of sessions that encountered errors. | Filter by date range, project, agent, or model. Charts show session volume over time, token usage, cost trends, top agents by session count and cost, and error breakdown. *** ## Sessions The **Sessions** tab lists individual user sessions across all agents in the selected project and time range. Session data appears once agents start processing requests. ### Browse sessions 1. Go to **Settings** > **Analytics** > **Session Explorer**. 2. Search by session ID, user message content, or agent name. 3. Filter by status, date range, or agent. 4. Click a session to view the conversation transcript, tool calls, model usage, state transitions, and metadata. *** ## Traces The **Traces** tab provides a step-by-step execution log of agent runs. It offers two views: **Event Stream** and **SQL Query**. Trace events appear once agents start processing sessions. ### Event stream The **Event Stream** view shows a chronological log of all agent events in the selected project and time range. Each trace records a span tree — a hierarchical view of all operations: | Event type | What it captures | | - | - | | LLM calls | Prompt, response, tokens, and latency. | | Tool executions | Name, input, output, and duration. | | Step transitions | Flow step changes for agents with a FLOW section. | | Guardrail evaluations | Category, score, and decision. | | Knowledge base queries | Query text, retrieved chunks, and relevance scores. | Filter events by type: | Filter | Description | | - | - | | All | Show all event types. | | LLM | LLM call events. | | Tool | Tool invocation events. | | Decision | Agent decision and routing events. | | Error | Events that resulted in an error. | | Handoff | Agent handoff and delegation events. | | Agent | Agent lifecycle events. | | Session | Session start and end events. | Use the search bar to find events by session ID, agent name, or event type. The total event count is shown next to the search bar. **Trace search syntax:** | Operator | Example | Description | | - | - | - | | `agent:` | `agent:customer-support` | Filter by agent name. | | `status:` | `status:error` | Filter by outcome. | | `model:` | `model:claude-4-sonnet` | Filter by model used. | | `duration:>` | `duration:>2s` | Filter by total duration. | | `tokens:>` | `tokens:>5000` | Filter by token count. | ### SQL query The **SQL Query** view lets you run custom queries against trace event data. Admin access is required to use the SQL Query view. | Control | Description | | - | - | | Example Queries | A dropdown of pre-built queries to help you get started. | | SQL editor | Write or paste a SQL query. Use `{tenantId:String}` and `{projectId:String}` for isolation. | | Clear | Clear the current query from the editor. | | Execute | Run the query and view results. | *** ## Generations The **Generations** tab lists individual LLM calls across all agents in the selected project and time range, with token, latency, and cost details for each call. Generation data appears once agents start making LLM calls. ### Filtering Generations can be filtered at two levels: **1. Top-level filters** (apply to the whole list): | Filter | Behavior | | - | - | | Project | Single-select. | | Environment | Multi-select. | | Channel | Multi-select. | | Time range | Presets only: 24h, 7d, 30d, 90d. | | Search | Full-text search with search-as-you-type. | **2. Advanced (per-column) filters** - refine the top-level results further. Click **Filters** to add one or more conditions: 1. Click **+ Add filter**. 2. Choose a field - Model, Name, Status, Status Code, Channel Type, Source, Source Subtype, Source ID, and others. 3. Choose an operator (for example: **is**) and a value. 4. Click **Apply Filters** to run the query, or **Clear all** to remove every condition. **Columns** | Column | Description | | - | - | | Model | The LLM model used for the call. | | Name | Name of the generation (for example: agent, guardrail evaluation, or action). | | Status | Outcome of the call (for example: Success). | | Channel | Channel the request originated from (for example: Web Debug). | | Source | Origin of the call (for example: Runtime, Runtime Internal, Guardrails). | | Total Tokens | Combined input and output tokens for the call. | | Latency | Total time taken for the call to complete. | | TTFT | Time to first token - latency until the first output token is returned. Populated for streaming calls only; shows "Not applicable" otherwise. | | Cost | Estimated cost of the call. | | Start Time | Timestamp when the call started. | Additional columns - **Session**, **Channel Type**, **Source Subtype**, and **Source ID** - are available but hidden by default. * Reorder columns by dragging them in the **Customize Columns** sidebar. * Model is pinned by default. ### Generation payload Click any row to view the exact request and response, and call metadata. This is the primary place to troubleshoot a specific call - **Event ID** and **Call ID** are the two fields to use when correlating a generation with logs or traces elsewhere. | Field | Description | | - | - | | Event ID | Unique identifier for the trace event. Use to correlate with trace data. | | Call ID | Unique identifier for the LLM call. Use to correlate with provider-side logs. | | Session | Session ID the call belongs to. | | Provider | LLM provider (for example: openai). | | Input Tokens | Tokens sent in the request. | | Output Tokens | Tokens returned in the response. | | Total Tokens | Combined input and output tokens. | | Latency | Total call duration. | | TTFT | Time to first token (see Columns above). | | Status | Outcome of the call. | The panel has two tabs: * **Request** - the payload sent to the model. * **Response** - the JSON response returned by the model, including the generated text and provider metadata. ### Export Click **Export** to download the current list as a CSV. Only the entries visible on the current page are exported (up to 50 rows). # Workspace and team settings Source: https://koreai.mintlify.app/agent-platform/administration/workspace-and-team Configure workspace structure, roles, member access, security policies, encryption keys, and environment variables. **Workspace and Team** settings are your control center for access, security, and runtime configuration. From here you decide who can do what, how the platform protects data, and which values agents use at runtime. * **Navigation**: **Settings** -> **Team** * **Required role**: Owner or Admin *** ## Workspace structure The Agent Platform organizes resources across three levels. You don't need all three. Start with a workspace, add projects as your work grows, and set up an organization only when you need to manage several workspaces together. | Level | Description | | - | - | | **Workspace** | Your team's dedicated environment. It holds all agents, models, connectors, secrets, and members. Each workspace stays fully isolated, so nothing in one workspace reaches another. | | **Project** | A group of related agents and resources for a specific use case, inside a workspace. A project inherits the workspace's AI model configuration but can override model tier assignments. | | **Organization** | Optional. It sits above workspaces and carries shared billing and governance across several teams. A single-workspace setup doesn't need one. | **What the workspace controls:** * Who has access and what they can do, through team membership and roles. * Which AI models and providers your team can reach, through LLM policies, token budgets, and rate limits. * Which features your plan tier unlocks. **What projects add:** * Projects draw their members from workspace members and give them project-specific roles. * You can scope environment variables and secrets per project and per environment, covering development, staging, and production. *** ## Roles and permissions The platform uses hierarchical role-based access control. Each role inherits every permission the roles below it hold, so you grant the level someone needs rather than assembling one from parts. ### Workspace roles Workspace roles apply everywhere in the workspace. Assign the narrowest role that still lets someone do their job. | Role | Description | Key permissions | | - | - | - | | Owner | Holds full control. One person owns each workspace. | Transfers ownership, deletes the workspace, manages billing, and performs every admin operation. | | Admin | Manages team members, models, and workspace settings. | Invites and removes members, configures models, manages secrets and connectors, and reads audit logs. | | Operator | Manages agent deployments and watches production. | Deploys agents, reads analytics, manages environment variables, and views sessions. | | Member | Builds and tests agents inside assigned projects. | Creates and edits agents, runs tests, and manages project-level resources. | | Viewer | Reads workspace resources without changing them. | Views agents, project configurations, and analytics dashboards. | ### Project roles Project roles apply inside one project. A member can hold different project roles in different projects. | Role | Description | Key permissions | | - | - | - | | Admin | Holds full control over the project's resources and settings. | Manages every project resource, configures settings, and oversees operations. | | Developer | Creates and modifies agents, tools, workflows, and project assets. | Builds and updates agents, tools, workflows, and assets. | | Tester | Reads project resources and runs simulations and evaluations. | Reaches project resources, runs tests, and validates agent behavior. | | Viewer | Reads the project without changing it. | Views project resources and configurations. | **How workspace and project roles interact:** * Workspace Owners and Admins hold workspace-wide authority and can administer every project without joining it. * Other workspace members need explicit project membership before they reach a project. * Whoever creates a project becomes its Admin and can add other workspace members from **Settings** -> **Members**. *** ## Managing members Only workspace Owners and Admins manage team membership. Everything in this section needs one of those two roles. ### View current members Go to **Settings** -> **Team** -> **Members**. The list shows each member's name and email, workspace role, status, and join date. Status covers active, suspended, locked, and deactivated. ### Invite a member Invitations carry the role you choose, so decide the role before you send one rather than correcting it afterward. 1. Select **Invite member**. 2. Enter the invitee's email address. 3. Select a role: Admin, Operator, Member, or Viewer. 4. Select **Send invite**. The invitee receives an email holding a unique link that names the workspace and their assigned role. Invitations expire after 7 days, and the platform clears expired ones automatically. Manage pending invitations from the **Pending Invitations** section on the Members page. | Action | What it does | | - | - | | Resend | Sends a fresh invitation email with a new token and restarts the 7-day expiry. | | Revoke | Cancels the invitation so nobody can use the link. | If a member reports that the invitation never arrived, find it in the pending list and resend. You don't need to revoke it first. ### Change a member's role A role change takes effect immediately. The member's permissions update on their next page load or API call. 1. Find the member in the Members list. 2. Select the role dropdown next to their name. 3. Select the new role and confirm. **Rules the platform enforces:** * You can't assign a role above your own. An Admin can't promote anyone to Owner. * Only the workspace Owner promotes a member to Admin. * Only the workspace Owner transfers ownership. * You can't demote yourself. Ask another Owner or Admin to change your role. ### Remove a member Removing a member cuts their access at once, so confirm the person no longer needs the workspace before you start. 1. Find the member in the Members list. 2. Select the three-dot menu next to their row. 3. Select **Remove member** and confirm. **What happens when you remove a member:** * They lose access to every workspace resource immediately. * The platform drops their project memberships inside this workspace. * Resources they created, such as agents and knowledge bases, stay in the workspace. * Sessions they started keep running, but they can't start new ones. Removing a member doesn't delete their Agent Platform account. They keep access to any other workspace they belong to. ### Transfer workspace ownership Only the current workspace Owner transfers ownership, and the transfer changes both people's roles at once. 1. Go to **Settings** -> **Team** -> **Members**. 2. Select the three-dot menu next to the target member. 3. Select **Transfer ownership**, then confirm by typing the workspace name. After the transfer, the previous Owner becomes an Admin and the new Owner gains full control, including the ability to delete the workspace. *** ## Role management Role Management decides what people can see and do across your workspace and projects. Roles come in two types. * **Custom roles**, which you define yourself. * **System roles**, which the platform provides and which cover the most common access levels. Find Role Management under **Settings** -> **Role Management**. *** ## Custom roles Custom roles let you build access levels that match your team, such as a role that only reads configuration or one that releases to a limited set of environments. The **Custom roles** tab lists every custom role in your workspace, showing its name and description, how many permissions it carries, when someone created it, and quick actions to edit or delete it. ### Creating a custom role Select **+ Create Role** to open the creation panel, then complete these fields. * **Role name**, a short identifiable name. * **Description**, optional context for what the role does. It earns its place once you hold several similar roles. * **Parent role**, optional. Base the role on an existing custom role to inherit its permissions, or choose **No parent role** to start from nothing. The panel shows the resulting parent chain, so you can see what you're inheriting before you save. ### Assigning permissions The panel groups permissions by category. Select the actions this role may perform. A running count at the bottom tracks how many you've selected. The picker gives identifiers without describing them. See the [Permissions reference](/agent-platform/permissions-reference) for what each permission grants, which ones reverse a protection, and which ones to keep apart from one another. The picker shows permissions outside your own role's grant scope but won't let you select them. You can't grant access you don't already hold. ### Scoping release environments Release scoping decides where a role may push configurations, which matters most for production. When the role includes the Workbench release permission, you can also restrict where it releases configurations, across **Development**, **Staging**, and **Production**. The scoping covers release actions only. It grants no production access on its own, and you must still select the underlying release permission separately. Select **Create** to save the role, or **Cancel** to discard it. *** ## System roles The **System roles** tab lists the built-in roles, grouped by where they apply. You can't edit or delete them, but you can expand any role to see the grants it carries. Select **View grants** next to a role to read its full permission list. *** ## When to use custom or system roles Choose a **system role** whenever a built-in option already matches what someone needs. System roles need no setup and stay current on their own. Build a **custom role** when you need finer control, such as read-only access to a subset of permissions, a role scoped to specific release environments, or a role that extends another custom role through a parent chain. *** ## Security and compliance Go to **Team** -> **Security & Compliance** to manage authentication and access controls. The page carries two tabs: **SSO** and **Audit Logs**. **Required role**: Owner or Admin. ### Single sign-on SSO lets your users authenticate through an external identity provider. **Plan requirement**: Enterprise plan only. Only organization owners or admins can manage SSO for a workspace. Other roles see the SSO tab as read-only, with a **Read-only** badge and an **Org admin access required** banner. **SSO Protocol** Under **SSO Protocol**, select the protocol your identity provider uses: | Protocol | Description | | - | - | | **SAML 2.0** | For enterprise identity providers like Okta, Azure AD, and OneLogin. | | **OpenID Connect** | For OIDC-compatible identity providers. | Then select an **Identity provider** from the dropdown. Choosing **Custom SAML 2.0** lets you enter the IdP SSO URL, entity ID, and certificate details manually. **Values to copy into your identity provider** After you select a protocol and identity provider, the **Values to copy into your identity provider** box shows the endpoints the platform generates. You create and edit nothing here. In your identity provider, set the service-provider audience or entity ID to the SP Entity ID, and set the assertion consumer service (ACS) endpoint to the ACS URL. These are distinct values. | Field | When to use it | | - | - | | **SP Entity ID / Audience URI** | Enter it as the **Entity ID**, **Identifier**, or **Audience URI** in your identity provider. It identifies the Agent Platform service provider and lets the identity provider confirm that a SAML assertion targets this application. | | **ACS URL (SP- and IdP-initiated)** | Enter it as the **Assertion Consumer Service URL** or **Reply URL** in your identity provider. The same URL serves both service-provider-initiated and identity-provider-initiated sign-ins. | | **SAML metadata URL** | Give it to your identity provider so it imports the service provider metadata automatically, covering the entity ID, ACS URLs, supported bindings, and signing certificates. | Each field has a copy button. Select **Refresh** at the top of the page to reload the current values. Refreshing neither regenerates nor changes them. **SAML Configuration** Enter your identity provider's SAML details. For **Custom SAML 2.0**, you enter these values manually. | Field | Description | | - | - | | **SSO Entry Point URL** | Your identity provider's SAML SSO URL, for example `https://idp.example.com/sso/saml`. | | **Issuer / Entity ID** | Your identity provider's issuer or entity ID. | | **X.509 certificate 1** | The signing certificate your identity provider uses. Paste it in PEM format, including the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` lines. | The platform accepts up to 2 signing certificates. Use a second certificate to rotate certificates without downtime. * To keep the saved certificate, leave the certificate field blank. * To replace the saved certificate, enter the new certificate in the first field. * To rotate, select **Add certificate** to append a second certificate without removing the current one. After you enter the details, use the buttons below the form: | Button | Description | | - | - | | **Save Configuration** | Stores the values. | | **Test configuration** | Verifies the connection with your identity provider. | | **Activate SSO** | Turns SSO on for the workspace. | | **Pause SSO** | Turns SSO off without deleting the configuration. | **Set up OIDC SSO** Go to **Team** -> **Security & Compliance** -> **SSO**, select **OpenID Connect**, enter the configuration details, and select **Save Configuration**. **SSO Access Policy** Control workspace access behavior after successful SSO authentication. These settings save separately from your provider credentials. | Setting | Description | Default | | - | - | - | | **Force SSO** | Require all users to sign in via SSO. | Disabled | | **Auto-accept pending invites** | Automatically accepts a single pending invitation for a user who signs in via SSO but has no workspace membership yet. | Disabled | Once you enable **Force SSO**: * Verified domain users lose password-based login. * Users outside the verified domain carry on as before. * Workspace Owners keep password-based login as a fallback. Select **Save Access Policy** to apply changes. **Recovery Exclusions** Use recovery exclusions to give specific users emergency access that bypasses SSO or MFA. Each exclusion needs a documented reason and an evidence reference, so the bypass leaves an audit trail. The **Recovery Exclusions** list shows each active exclusion with its bypass type and expiry, for example **SSO bypass · Never expires**. Select the **X** next to an exclusion to revoke it. To grant an exclusion, complete the **Grant an exclusion** fields, then select **Grant exclusion**. | Field | Description | | - | - | | **User ID** | The ID of the user who receives the exclusion. | | **Normalized email** | The user's email address in normalized form. | | **Bypass type** | The requirement to bypass, such as **SSO bypass**. | | **Reason** | Why you're granting the exclusion. Include a ticket or evidence reference, for example `JIRA-123`. | | **Expires at** | The date and time the exclusion ends. | | **Never expires** | Keeps the exclusion active until you revoke it manually. When you turn it on, **Expires at** no longer applies. | Grant exclusions only for emergency access, and set an expiry unless the user needs a permanent exception. **JIT Provisioning** Just-in-time (JIT) provisioning automatically creates a workspace membership when a user authenticates through SSO for the first time. | Setting | Description | | - | - | | **Enable SSO JIT provisioning** | Lets eligible SSO users receive a workspace membership automatically. | | **Enable IdP group-to-role mapping** | Assigns workspace roles from your identity provider's group or attribute claims. It requires JIT provisioning to be enabled with a provisioning mode other than **Disabled**, and you must activate the workspace SSO provider first. Mapping applies only to eligible SSO provisioning, and existing manual memberships stay unchanged. | | **Provisioning mode** | Controls how new SSO users join the workspace. See the modes below. | | **Default role for new SSO members** | The role a user receives when the platform first creates their membership. Choose **Member** or **Viewer**. The platform also applies this role when the user's IdP claims match no mapping rule. A matching mapping rule can change an existing SSO-managed role. Manual and invitation-managed roles stay unchanged. | | **Allowed email domains** | Limits automatic provisioning to users with these email domains. | | Provisioning mode | Behavior | | - | - | | **Disabled - users must be invited** | The platform doesn't auto-provision users. Users must receive an invitation. | | **Enable - auto-provision with default role** | The platform creates a membership with the default role when a user first signs in through SSO. | | **Invite-only - require pending invite** | A user can join only if they have a pending invitation. | Allowed email domains apply only to new automatic SSO provisioning. They don't change existing members or workspace invitations. The workspace owner's domain is always included and can't be removed. You can add up to 50 domains. To add a domain, enter it under **Add another allowed domain**, then select **Add domain**. Enter the domain only, such as `example.com`, not an email address. Select **Save** to apply your JIT provisioning changes. **Group-to-Role Mapping** Group-to-role mapping assigns a workspace role based on a claim or SAML attribute that your identity provider sends. Configure and activate an SSO provider before you add rules. To add a rule, complete the following fields, then select **Add rule**. | Field | Description | | - | - | | **IdP claim / SAML attribute** | The claim or attribute name your provider sends, for example `groups`. Enter it exactly as it appears in the provider's assertion or token. | | **Claim value** | The value to match. | | **Match condition** | How the platform compares the claim to the value, such as **Member of**. | | **If matched** | The action to take when the rule matches, such as **Grant role**. | | **Workspace role** | The workspace role to grant. | | **Rule priority** | The order in which rules run. A lower number runs first. Default: 100. | How the platform applies rules: * If the user's claims match no rule, a new eligible member receives the JIT default role. * If you have active rules but the user's sign-in carries no claims, the platform creates no membership and uses the existing workspace access-required flow. * A matching rule can change the role of an existing SSO-managed member. It doesn't change roles that were assigned manually or through an invitation. *** ## Audit logs Audit logs give you a tenant-scoped, filterable record of every significant workspace action. Use them to investigate incidents, confirm compliance, and review team activity. * **Navigation**: **Settings** > **Team** > **Audit Logs** * **Required role**: Owner or Admin * **Export**: Professional and Enterprise plans Select **Refresh** to reload the log, and **Export CSV** to download the view you're currently filtering. Use the filter bar to narrow results by search text, date range, or category. #### Filtering * **Search** matches actor, action, resource, trace, IP, or metadata. * **From / To** set the date range. Quick presets - **1h**, **24h**, **7d**, **30d** - apply a range in one click, and **Reset** clears every active filter. * **Categories** appear as toggle buttons; select one or more to narrow the log to those event types. * **More filters** exposes additional filter fields beyond search, date, and category. * Select **Apply** to run the filters. #### Summary metrics These five figures sit above the log and describe the view your filters produce. | Metric | Description | | - | - | | Matching events | Counts every event your current filters match. | | Failed on page | Counts the failed events on this page. | | Actors on page | Counts the distinct actors on this page. | | Active filters | Counts the filters you've applied. | | Last event | Times the most recent event. | #### Event categories Categories group actions by the part of the platform they touch, which is how you narrow a broad search quickly. The platform organizes events into the following categories: Auth & access, Workspace governance, Project, agent & workflow configuration, Deployment lifecycle, Evaluation, Channel configuration, Tools, modules & credentials, Data protection, Operate sessions, Insights, dashboards & reports, KMS, Connector configuration, and Archives, retention & Git. #### Log table columns Each row records one action, and these columns tell you who did what, where, and when. | Column | Description | | - | - | | Timestamp | Times the action. | | Category | Names the event category. | | Action | Names the specific action, such as `agent.dsl_updated`. | | Actor | Identifies the user or system that acted. | | Target | Names the resource the action affected. | | Project | Names the associated project. | | Source | Names the origin, either Studio or runtime-store. | | IP | Records the request IP address. | | Trace | Records the trace ID when one exists. | #### Viewing event details Select any row to open a detail panel for that event. Alongside the columns shown in the table, the panel adds: | Field | Description | | - | - | | Event type | The full event name, matching the Action column (for example, `project_member_role_changed`). | | Actor type | Identifies whether the actor was a `user` or a system process. | | Resource type | Names the type of resource the target belongs to (for example, `project_member`). | | Environment | Names the environment the event occurred in (for example, `dev`). | | Metadata | A JSON object with event-specific detail - for a role change, for example, the target user ID and the previous and new role. | #### Retention Retention decides how far back an investigation can reach, so check it before you rely on the log for compliance evidence. * Audit logs follow the workspace retention policy. * Professional and Enterprise plans keep at least 90 days. * Contact support to extend retention. *** ## Key management The Key Management Service lets you control the encryption keys that protect sensitive workspace data. Rather than relying on platform-managed keys, you can supply your own key material from a supported cloud provider. * **Navigation**: **Settings** -> **Team** -> **Key Management** * **Required role**: Owner * **Plan requirement**: Enterprise plan only ### Configuration Select a **Provider** to configure the tenant-level KMS. The provider you choose decides which fields the page shows. **Supported KMS providers** | Provider | Key type | Region support | | - | - | - | | **Platform Managed** | Platform-governed keys | Managed by platform | | **AWS KMS** | Symmetric CMK (AES-256) | All AWS regions | | **Azure Key Vault** | RSA or AES keys | All Azure regions | | **Azure Managed HSM** | Symmetric AES-256 keys | All Azure regions | | **Google Cloud KMS** | Symmetric AES-256 keys | All GCP regions | | **External KMS** | Any REST-compatible KMS | Self-hosted or SaaS | #### Platform Managed Use the platform-managed KMS provider. This mode stores no tenant-specific external key connection. Kore governs Platform Managed encryption. You can select it, but its policy, compliance, rotation, and re-encryption settings stay read-only. To bring your own key, choose a different provider. #### AWS KMS AWS KMS signs and wraps with a customer managed key you already hold in AWS. The auth method you choose decides whether the platform reaches it using static AWS credentials or by assuming a role in your AWS account. | Field | Description | | - | - | | AWS KMS Endpoint | Holds the URL of the AWS KMS. | | AWS Region | Names the AWS region that hosts the key. | | KMS Key ARN | Holds the full ARN of the CMK, such as `arn:aws:kms:us-east-1:123456789:key/abcd-1234`. | | Auth Method | Chooses **Service Account / Service Principal** or **AssumeRole**. | **Service Account / Service Principal** Store encrypted AWS access keys for this tenant. Use temporary credentials only with a session token. | Field | Description | | - | - | | Access Key ID | Required. The AWS access key ID for the credentials the platform stores. | | Secret Access Key | Required. The AWS secret access key paired with the Access Key ID. | | Session Token | Optional. Use for temporary STS credentials. | **AssumeRole** AssumeRole lets the platform obtain short-lived AWS credentials by assuming an IAM role in your account, rather than storing long-lived access keys. Store the customer IAM role ARN. The platform uses the tenant external ID when assuming this role. | Field | Description | | - | - | | Customer IAM role ARN | Required. Holds the ARN of the IAM role the platform assumes, such as `arn:aws:iam::123456789012:role/tenant-kms-role`. | **AWS trust policy setup** Generate the trust policy values the AWS account administrator must configure before you save or test AssumeRole. Enter the customer IAM role ARN, then select **Generate setup policy**. Select **Test Connection** to verify the provider connection before you save the configuration. #### Azure Key Vault Azure Key Vault holds the key, and the auth method you choose decides whether the platform reaches it through your own service principal or its own managed application. | Field | Description | | - | - | | Key Vault URL | Points at the vault. | | Azure key version (optional) | Pins this scope to one Azure key version using the 32-character hexadecimal version ID. Leave it empty to follow Azure's current key version. | | Key Name | Names the key inside the vault. | | Auth Method | Chooses **Service Account / Service Principal** or **Platform workload identity**. | **Service Account / Service Principal** Use the Azure app registration or service principal that can read, wrap, and unwrap this key. For cross-tenant access, register this application in the customer Azure tenant through admin consent, then assign the Key Vault Crypto User role on the target key vault. | Field | Description | | - | - | | Tenant ID | Identifies the Azure directory. | | Client ID | Identifies the application. | | Credential type | Chooses **Client secret** or **Certificate (PEM)**. | | Client Secret | Required when you choose Client secret. | **Platform workload identity** Enter the customer Entra directory ID. The platform uses its own managed Azure application, so you supply no client ID, secret, or federated credential. | Field | Description | | - | - | | Customer Entra directory ID | Identifies the customer's Azure directory. | Your Azure administrator must grant the platform's managed Azure application access to the target key vault. Select **Test Connection** to verify the provider connection before you save the configuration. #### Azure Managed HSM Azure Managed HSM stores the key in dedicated hardware, which suits compliance regimes that won't accept a software-backed vault. | Field | Description | | - | - | | **Managed HSM URL** | Points at your HSM instance endpoint. | | **Azure key version (optional)** | Pins the scope to a specific Azure key version using the 32-character hexadecimal version ID. Leave it empty to follow Azure's current key version. | | **Managed HSM Key Name** | Names the key to use. | | **Auth Method** | Chooses **Service Account / Service Principal** or **Platform workload identity**. | **Service Account / Service Principal** Use the Azure app registration or service principal that can read, wrap, and unwrap this key. For cross-tenant access, register this application in the customer Azure tenant through admin consent, then assign the Key Vault Crypto User role on the target key vault. | Field | Description | | - | - | | Tenant ID | Identifies the Azure directory. | | Client ID | Identifies the application. | | Credential type | Chooses **Client secret** or **Certificate (PEM)**. | | Client Secret | Required when you choose Client secret. | **Platform workload identity** Enter the customer Entra directory ID. The platform uses its own managed Azure application, so you supply no client ID, secret, or federated credential. | Field | Description | | - | - | | Customer Entra directory ID | Identifies the customer's Azure directory. | #### Google Cloud KMS Google Cloud KMS holds the key, and the platform addresses it through its full resource path rather than a name. | Field | Description | | - | - | | Location | Names the GCP location. | | Key Resource Name | Holds the full resource path, such as `projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key`. | | Auth Method | Chooses **Default Credentials** or **Service Account / Service Principal**. | | Credential Overrides (JSON) | Optional. Overrides the default credentials with a JSON object. | #### External KMS Connect any REST-compatible KMS, whether you host it yourself or consume it as a service. | Field | Description | | - | - | | External KMS Endpoint | Holds the base URL of the external KMS. | | External Key ID | Identifies the key at the external provider. | | Auth Method | Chooses **API Key**, **mTLS**, **OAuth2 Client Credentials**, or **HMAC-SHA256**. | | Credential Overrides (JSON) | Optional. Holds a JSON object carrying provider-specific credential fields. | **External endpoint validation** Before you save the tenant KMS configuration, enter the endpoint, key ID, and auth method, then select **Validate Endpoint** to confirm that the platform reaches and authenticates with the external provider. ### Policies These two settings decide how the platform behaves when the KMS becomes unreachable and which compliance profile it applies. | Setting | Options | Default | | - | - | - | | **Failure Policy** | **Fail Closed**, which stops all encryption and decryption.
**Graceful Degradation**, which continues with cached DEKs until the KMS reconnects. | Fail Closed | | **Compliance Level** | **Standard**, **PCI DSS**, **HIPAA**, **GDPR**, **SOC 2**, **NIST SP 800-57**, **FIPS 140-3** | Standard | ### Rotation and re-encryption settings Keys rotate automatically when your provider supports scheduled rotation, or manually when you create a new key version on demand from the Encryption Keys tab. Tune how often the platform generates DEKs, how long a KEK may live, and whether retired DEKs are ever destroyed. | Setting | Description | | - | - | | DEK Epoch Interval (hours) | Sets how often the platform generates a DEK, in hours. | | DEK Max Usage Count | Caps how many times a DEK is used before it rotates. | | KEK rotation policy (days) | Sets how often the KEK rotates, in days. | | Last KEK rewrap completed | Read-only. Times the most recent KEK rewrap, or reads `Never` when none has run. | Re-encryption also carries **Concurrency**, which sets parallel jobs, **Batch Size**, which sets records per batch, and **Max Retries**, which sets how many times a failed job retries. Select **Save Configuration** to apply your changes. ### Scopes Scopes let you run different KMS providers for tenant environments, projects, and project environments, so one project can hold its own key without changing the rest. *** ## Environment variables Environment variables are key-value pairs that inject configuration into your agents and tools, so you don't hardcode values in ABL definitions. Use them for endpoints, feature flags, timeouts, and anything else that differs between environments. The platform resolves them at runtime and scopes them by environment and project. A variable takes its identity from its **key** within a project. It can carry a **Global** value that every environment shares, plus optional values for **Development**, **Staging**, and **Production**. When a key holds both a global value and an environment-specific one, the environment-specific value wins. Environment variables behave as follows: * **Environment-specific**. One key carries different values in development, staging, and production. * **Encrypted at rest**. The platform encrypts stored values using AES-256-GCM. * **Resolved at runtime**. The agent resolves values when it runs. * **Immediately effective**. Changes reach new agent sessions without a redeploy. - Environment variables belong to a project. - Manage variables centrally from **Workspace Settings** -> **Env Variables**, or from the project's **[Deployments -> Environments](/agent-platform/deployment)** page. ### Add environment variables Adding a variable takes five steps, and the namespace you choose at step 4 is the one decision you can't change afterward without recreating the variable. 1. Go to **Workspace Settings** -> **Env Variables**. 2. Select the project. 3. Select the target environment, or create a **global** variable that covers every environment. 4. Select **Add Variable**. The platform adds the variable to the **Default** namespace. To use a different namespace, select it from the **Namespace** dropdown before you create the variable. 5. Enter the following details, then select **Save**. | Field | Description | | - | - | | Key | Names the variable uniquely within the selected project and environment. The key must start with a letter, contain only letters, numbers, and underscores, and stay unique inside the project and environment. | | Value | Holds the value the variable carries for the selected environment. | | Description | Tells project collaborators what the variable is for. | | Secret | Masks the value after you save it. Turn it on for anything sensitive. | ### Reference environment variables Reference a variable using `{{env.KEY}}` syntax. The agent resolves it at runtime based on the active deployment environment. You can reference environment variables in these constructs: * HTTP tool endpoint, headers, auth, and request body * MCP tool call arguments * RESPOND text and rich content, covering quick replies, cards, forms, and KPI or chart widgets * ESCALATE and handoff messages, GATHER re-prompts, and guardrail block or redirect messages * Agent persona, goal, and limitations, meaning the system prompt **Example: HTTP tool endpoints** ```xml theme={null} TOOLS: lookup_customer(customer_id: string) -> {name: string, tier: string} description: "Look up customer details" type: http endpoint: "{{env.API_URL}}/customers/{customer_id}" method: GET auth: bearer timeout: "{{env.TIMEOUT}}" ``` **Example: response templates** ```xml theme={null} COMPLETE: - WHEN: ticket_created IS SET RESPOND: | Your ticket has been created. Expected response time: {{env.SLA_HOURS}} hours. Support portal: {{env.SUPPORT_PORTAL_URL}} ``` **Example: memory** ```xml theme={null} MEMORY: PERSISTENT: - PATH: user.preferences TTL: "{{env.MEMORY_TTL_DAYS}}d" ``` ### Edit a variable Select the **Edit** icon next to a variable to change its value, description, or secret setting. Your change reaches new agent sessions immediately. Sessions already running keep the values they resolved when they started, and only new sessions pick up your change. HTTP tools and MCP tools are the exception and use the updated values as soon as you save. ### Delete a variable Select the **Delete** icon and confirm. Before you delete a variable, confirm that nothing references it. Anything that references a deleted variable hits a runtime resolution error. ### Import or export environment variables A bulk import creates or updates many variables at once, which saves you entering them one by one when you set up a new environment. 1. Select **Import**. 2. Select the target environment. 3. Paste the keys in JSON format, as shown below. ```xml theme={null} [ {"key": "API_KEY", "value": "sk-xxx", "isSecret": true}, {"key": "DB_URL", "value": "postgres://..."} ] ``` 4. Review the import preview. 5. Select **Import**. You can also export environment variables as a `.json` or `.env` file. ### Variable namespaces Namespaces organize environment variables into groups, which keeps a large set navigable. Every project holds a **default** namespace, and you can create up to 25 namespaces per project. #### Create a namespace 1. Go to **Settings** -> **Environment Variables**. 2. Select **Namespaces**. 3. Enter a namespace name, display name, description, and color. 4. Select **Create**. # Agent Tables Source: https://koreai.mintlify.app/agent-platform/agent-tables Agent Tables provide a built-in structured data store for your agents. You can create tables in Studio to store operational data such as customer records, appointments, orders, inventory, or support tickets. Agents can access this data at runtime through **Table tools** without requiring an external database. Unlike conversational memory or knowledge sources, Agent Tables store structured, persistent business data that agents can create, query, update, and delete, depending on the operations permitted to the agents. The **Tables** page appears under **Resources** only if the feature is enabled for your workspace. If you don't see it, [contact Support](https://support.kore.ai/). ## Step 1: Create a table * Navigate to **Resources > Tables.** * Click **New Table.** * Enter table details. * Display name: User-friendly table name displayed in Studio. * Table name (slug): Unique identifier used internally and by runtime. This must be used in the queries. * Scope: Determines who can access the table's data. * Description: Briefly describe the usage of the table. | **Scope** | **Description** | **Typical use cases** | | - | - | - | | **Project** | Shares data across all sessions and end users within the project. Suitable for reference or operational data that should be available to every conversation. Agent writes are audited as declassification events. | Product catalogs, inventory, FAQs, departments, pricing information | | **End User** | Creates a private data partition for each customer. Each end user can access only their own records. Data is retained across conversations until deleted or erased. | Customer profiles, saved addresses, preferences, loyalty points, order history | | **Session** | Creates a private data partition for a single conversation. Data is automatically cleared when the session ends. | Shopping carts, in-progress forms, temporary selections, conversation state, scratchpad data | * Next, define the table schema by specifying the columns and their properties. * **Name** - Specifies the unique name used to identify the column in the table. * **Type** - Defines the data type that the column can store, such as String, Number, or DateTime. To add a reference key to a column in another table, use Reference key. Set the target table and column, and choose what happens when the referenced row is deleted: Restrict (block the delete), Set null, or Cascade (delete the referencing rows). A Reference column can't be sensitive. * **Required** - Makes the column mandatory when creating or updating a row. * **Default value** - Automatically assigns a value if none is provided during row creation. The options are **None**, **Auto ID** (a generated unique ID, for String columns), and **Now** (the current server time, for Datetime columns). * **Indexed** - Creates an index on the column to improve query performance for filtering and sorting. * **Unique** - Ensures that each row contains a distinct value for the column. * **Sensitive** - Encrypts the column data at rest and redacts its values in queries and logs to protect sensitive information. * Save the table. You can add columns to an existing table later. Changes that drop a column or change its type are blocked. ## Step 2: Add records After creating a table, add records to populate it with data that your agents can retrieve and update during conversations. * Click **Add record**. * Enter values for each column. Required fields must be completed before saving the record. * Click **Save** to add the record to the table. Repeat these steps to add additional records as needed. **Points to note** * Values entered must match the data type defined for each column. * Columns marked as **Required** must contain a value. * Open the table details to review the records added to the table, view the table schema, or run sample queries to test. ## Step 3: Create a tool for the table After creating a table and adding records, create a **Table tool** so your agents can access and manipulate the table data during conversations. * Go to **Tools > Tables**. * Create a new table tool and configure the following properties. | Field | Description | | - | - | | Tool name | Enter a unique name for the tool. This name is used to identify the tool in your agent. | | Description | Provide an optional description of the tool's purpose. | | Table | Select the Agent Table that the tool will read from or write to. | | Joinable tables | Select additional tables that this tool can join in SQL queries. Joins are supported only on declared foreign-key columns. Leave this empty if the tool accesses a single table. | | Scope | Displays the scope of the selected table (Project, End User, or Session). This should match the scope defined for the table. | | Operations | Choose the operations that the tool can perform on the table. Follow the principle of least privilege. Enable only the operations required by your agent. For example, if the agent only retrieves information, select only **get**, **query**, and **count**. | | SQL statement | You can provide an SQL template to define how the tool interacts with the selected table. The statement can include parameter placeholders (for example, `:status`) that are populated from the tool's input parameters at runtime. Example: `SELECT * FROM customer_orders WHERE status = :status`. Note that this query is used to limit the results presented to the agent. | | Input parameters | The values the agent supplies when it calls the tool. Each `:name` placeholder in the SQL statement needs a matching parameter. Click **Parse** to create the parameters from the statement automatically. Supported types are string, number, integer, and boolean. Add a description to each parameter so the agent knows what to pass. | | Variable Namespaces | Select all the namespaces that the tool can access. | * After completing the configuration, click **Create Tool**. The Table tool is added to your project and can be attached to one or more agents. * To control which **variable namespaces** the tool can access, open the tool after you create it and select the namespaces on its detail page. This is optional. ### Use a SQL statement The SQL statement is optional. Use it when you want the tool to do one specific, predictable thing instead of letting the agent build its own queries. When you add a statement, the agent can't change what the statement does. It supplies only the values for the `:name` placeholders. This lets you control exactly which columns, filters, and sort order the agent can use. Examples: **1. Look up orders by status \[query]:** ```sql theme={null} SELECT order_id, status, total FROM customer_orders WHERE status = :status ORDER BY order_id LIMIT 10 ``` **2. Look up one order \[query]:** ```sql theme={null} SELECT * FROM customer_orders WHERE order_id = :order_id ``` **3. Add a row \[insert]:** ```sql theme={null} INSERT INTO customer_orders (order_id, status) VALUES (:order_id, :status) ``` **4. Update a row \[update]:** ```sql theme={null} UPDATE customer_orders SET status = :status WHERE order_id = :order_id ``` **5. Delete a row \[delete]:** ```sql theme={null} DELETE FROM customer_orders WHERE order_id = :order_id ``` **Rules** * The statement must be a single statement that starts with **SELECT**, **INSERT**, **UPDATE**, or **DELETE**. The matching operation must be enabled on the tool. For example, a **SELECT** statement needs **query**, and an **UPDATE** statement needs **update**. * Write placeholders as `:name`, for example, `:status`. Never paste user-provided values into the statement. * The statement must contain at least one placeholder. A statement with no `:name` placeholder returns an error. If a tool needs no inputs, don't use a SQL statement. * Every placeholder must receive a value when the tool is called. If a value is missing, the call fails with the error: **Missing query template parameter "name"**. Define each placeholder as an input parameter, and mark it as required so the agent always provides it. * A statement can filter and sort only on indexed columns. * Supported: **WHERE**, **ORDER BY**, **LIMIT**, **JOIN** (on declared foreign keys), and column lists. * Not supported: **GROUP BY**, **HAVING**, **OFFSET**, **UNION**, **WITH**, subqueries, and aggregate functions such as **COUNT** or **SUM**. To count rows, enable the **count** operation. * A read returns at most **50 records**. If the response has more records, the result includes a cursor that the agent can use to fetch the next set of records. * If a call returns **"Query templates are not enabled for this project"**, SQL statements are turned off for your workspace. Contact your administrator. ## Step 4: Add the tool to the agent To add the tool to the agent, use Studio and attach the table tool. Alternatively, use the ABL file to add the tool. ```yaml theme={null} TOOLS: find_orders(status: string) -> object description: "Find customer orders by status" type: table table: customer_orders scope: project operations: [query] query_template: "SELECT order_id, status, total FROM customer_orders WHERE status = :status LIMIT 10" ``` For all Table tool properties, see the [Tools reference documentation](/agent-platform/abl/reference/tools#table-tools). ## Best Practices 1. **Use placeholders for values.** Always write `:paramName` in SQL statements. Never build a statement from user text. 2. **Grant the fewest operations.** Enable only what the agent needs. Prefer read-only tools. 3. **Choose the right scope.** Use End user for personal data, Session for temporary data, and Project only for shared data. Agent writes to Project tables are audited. 4. **Mark personal data as Sensitive.** Do this for phone numbers, IDs, and similar fields. 5. **Index the columns you search or sort by.** Queries on non-indexed columns are rejected. 6. **Describe every parameter.** Clear descriptions help the agent pass the right value. 7. **Validate inputs.** Have the agent confirm values with the user before it writes them. 8. **Test before you publish.** Use the Query tab to test the table, and test the tool with realistic parameter values, including missing ones. # Analytics and insights overview Source: https://koreai.mintlify.app/agent-platform/analytics-and-insights **Insights** tells you how your agents perform: how conversations end, how well the agent handles them, how customers react, and what it all costs. The section spans executive reporting, quality evaluation by dimension, customer voice, channel diagnostics, ad hoc exploration, and the pipelines that produce the underlying scores. The documents below group pages by subject rather than by screen, so one document often covers more than one page in the left navigation. Use the middle column to find the document that covers the page you're on. | Document | Pages covered | When to use it | | :- | :- | :- | | [Usage insights](/agent-platform/analytics-insights/executive-dashboard-usage) | At a Glance, Billing and Usage | Start here. Gives the outcome, quality, sentiment, and cost position for a period, and reconciles what the project consumed with what it billed. | | [Quality insights](/agent-platform/analytics-insights/performance-quality) | Quality Monitor, Agent Performance | Open when the quality figure on At a Glance moves and you need to know which aspect of quality slipped and which agent caused it. | | [Customer insights](/agent-platform/analytics-insights/customer-voice) | Customer Insights, Feedback | Open when you need the customer's side of the conversation: what they asked for, how their sentiment moved, and what they said in their own words. | | [Voice analytics](/agent-platform/analytics-insights/voice-analytics) | Voice Analytics | Open when voice conversations underperform their chat equivalents. Separates agent reasoning problems from call quality, speech recognition, and latency problems. | | [Agent transfer](/agent-platform/analytics-insights/agent-transfer) | Agent Transfer | Open when escalation volume concerns you more than the agent's own handling, and you need queue and human handling performance for transferred conversations. The page appears only after you connect Kore SmartAssist to the project. | | [Analytics pipelines](/agent-platform/analytics-insights/analytics-pipelines) | Pipelines | Open to see which evaluations run today, or to define your own evaluation logic when the built-in dimensions don't score what your business cares about. | | [Analytics explorer](/agent-platform/analytics-insights/analytics-explorer) | Analytics Explorer | Open when the prepared pages don't answer your question. Gives near real-time event, model, token, and cost data, session and trace drill-down, and custom queries over project event data. | ## Before you begin Insights pages report on data that other parts of the platform produce, so several of them stay empty until you put the source in place. * You need at least **Viewer** access to the project. * **Quality Monitor**, **Agent Performance**, and **Customer Insights** depend on analytics pipelines. Enable pipelines in **Settings** before you expect scores on these pages. * **Voice Analytics** needs at least one voice channel deployment. Without one, the page has no calls to report on. * **Agent Transfer** needs an active Kore SmartAssist connection that your Agent Transfer settings select. Until then, the page doesn't appear in the navigation at all. * Metric cards across the section show a dash until a pipeline evaluates enough conversations in the selected period. A dash means the measure has no data, not that it scored zero. # Agent transfer Source: https://koreai.mintlify.app/agent-platform/analytics-insights/agent-transfer **Agent Transfer** reports on conversations that leave the AI agent and reach a person. It covers transfer efficiency by channel, queue wait times and abandonment, and the performance of the human agents who handle escalated conversations. Use it when escalation volume concerns you more than the AI agent's own handling. The quality pages tell you how well the agent performed before it escalated. This page tells you what happened afterward. ## Page availability This page appears only when the project connects to Kore SmartAssist. Unlike the rest of Insights, it depends on an integration you set up yourself, so a project that never configured SmartAssist won't show the page at all. ### Requirements The page appears once both of the following hold true. | Requirement | Where you set it | | - | - | | The project holds a Kore SmartAssist connection with an active status. | **Project** -> **Integrations** | | The Agent Transfer settings select that same connection. | **Project** -> **Settings** -> **Agent Transfer** -> **Connection** | Both requirements point to the same connection. An active SmartAssist connection that your Agent Transfer settings don't select doesn't satisfy the second requirement, which matters in projects that carry more than one SmartAssist connection from an import or an earlier setup. ### Behavior before setup completes Until the project meets both requirements, the platform hides the page rather than showing it empty. **Insights** omits it from the navigation, universal search doesn't return it, and opening its URL shows setup guidance in place of the dashboard. Four situations produce that setup state: * The Agent Transfer settings select no connection. * The settings select a connection that no longer exists. * The settings select a connection that isn't a Kore SmartAssist connection. * The settings select a SmartAssist connection whose credentials expired or that someone revoked. The last situation also removes a page you could previously reach. If Agent Transfer disappears from a project that used it, check the connection status before you check anything else. ### What the gate doesn't affect The gate covers this dashboard only. Escalation routing, SmartAssist webhooks, and customer satisfaction collection all continue to run whether or not the page appears, so a hidden page never means that transfers stopped reaching your agents. ### A visible page that reports an error A connection can satisfy both requirements and still carry incomplete credentials or metadata. In that case the page appears and reports a connection error in place of metrics. Read the two states differently. A missing page means the setup isn't complete. A visible page reporting a connection error means the setup is complete but the credentials behind it need attention. ## Navigation **Navigation**: **Project** -> **Insights** -> **Agent Transfer** A date range control at the top of the page scopes every section below it. ## Page sections The page divides into three sections, each answering a different question about the same escalated conversations. | Section | Description | | - | - | | **Efficiency** | Reports transfer counts, average handling time, and resolution rates, split across voice, chat, and overall transfers. Read it to see whether escalations concentrate in one channel, which usually points to a coverage gap in that channel rather than a general escalation problem. | | **Queue Performance** | Reports average wait time, longest wait time, abandonment rate, and handling rate for each queue. Use it to separate a staffing problem from a demand problem. Long waits and high abandonment in one queue point to staffing, while waits that spread evenly across queues point to volume the whole team can't absorb. | | **Agent Performance** | Reports conversations handled, average handle time, resolution rate, and customer satisfaction for each human agent who takes escalations. | The **Agent Performance** section here measures the people who receive escalations. The **Agent Performance** page under **Quality & Performance** measures the AI agents that raise them. The two report on different populations and different scales, so don't read one as a continuation of the other. # Analytics explorer Source: https://koreai.mintlify.app/agent-platform/analytics-insights/analytics-explorer The **Analytics Explorer** page monitors event volume, LLM performance, token consumption, and cost in near real time. Use it to investigate production incidents, track the impact of model changes, or audit LLM spend during peak traffic. **Navigation**: **Project** -> **Insights** -> **Analytics Explorer** **Date range**: Set at the upper-right area of the page, for example **Last 7 days**. Every tab follows this range. **Filters**: Select **All environments** and **All channels** at the top of the page to narrow the data by deployment environment and channel. Filter selections are reflected in the page URL, so you can bookmark a filtered view or share it with a colleague. Analytics Explorer The page organizes its detail into six tabs. | Tab | What it shows | | - | - | | **Overview** | Headline volume, error, token, and cost metrics for the selected period. | | **LLM Performance** | Call volume, latency, token, and cost metrics, broken down by model. | | **Sessions Explorer** | Session-level records with filters, columns, and export. | | **Traces Explorer** | Individual trace events, with timeline and waterfall views. | | **Generations** | Every recorded model call, with latency, token, cost, and credential detail. | | **Query** | Ad-hoc SQL queries on project event data. | ## Overview Six metric cards summarize the selected period at a glance. | Metric | Description | | - | - | | **Sessions** | Total sessions in the selected period. A session is a single end-to-end interaction between a user and the agent system. | | **Messages** | Total messages across all sessions, including both user messages and agent responses. | | **LLM Calls** | Total LLM API calls agents made during the period. Includes calls to all configured models, for example routing, generation, and evaluation. | | **Errors** | Total errors during agent execution, including LLM failures, timeout errors, and tool invocation errors. | | **Tokens** | Total LLM tokens, both input and output, across all calls. | | **Cost** | Estimated cost based on token usage and per-model pricing. Use it to track spend relative to budgets or compare cost-efficiency across model configurations. | ## LLM performance **LLM Performance** reports call volume, latency, tokens, and cost for the model calls in the selected period, broken down by model. Use it to benchmark models and to find the ones that drive latency or spend. Five metric cards summarize the period. | Metric | Description | | - | - | | **LLM Calls** | Total model calls in the selected period. | | **Avg Latency** | Mean time a model call took to complete. | | **P95 Latency** | Latency that 95% of calls finished within. Read it alongside the average, because a few slow calls can raise it while the average stays flat. | | **Tokens** | Total input and output tokens across all calls. | | **Cost** | Estimated cost based on token usage and per-model pricing. | ### Charts Two charts show how latency and cost develop across the period. | Chart | Description | | - | - | | **Latency Trend** | Plots call latency across the selected period, so you can spot when calls slowed down. | | **Cost by Model** | Shows each model's share of total cost, with a legend that lists the models in the period. | ### Model performance The **Model Performance** table lists one row per model, sorted by **Cost** in descending order, so the models that cost the most appear first. | Column | Description | | - | - | | **Model** | Model that served the calls, for example `gpt-realtime-1.5`. | | **Provider** | Provider of the model, for example `openai`. | | **Calls** | Number of calls the model served. | | **Tokens** | Total tokens the model's calls consumed. | | **Cost** | Estimated cost of the model's calls. | **LLM Performance** groups calls by model, not by credential. To see which credential or auth profile supplied provider access for a call, use the **Credential Name** column on the [Generations](#generations) tab. ## Sessions explorer **Sessions Explorer** lists every session in the selected period so you can isolate the ones worth investigating. Four metric cards summarize the set. | Metric | Description | | - | - | | **Total Sessions** | Sessions recorded in the selected period. | | **Active** | Sessions currently in progress. | | **Avg Duration** | Mean session duration across the set. | | **Error Rate** | Percentage of sessions that recorded at least one error. | ### Narrow the list Status chips filter the list to **All**, **Active**, **Completed**, **Escalated**, **Failed**, or **Ended**. Below the chips, five controls refine the results further. | Control | Description | | - | - | | **Search sessions** | Matches sessions by keyword. | | **Channel** | Filters by the channel the session arrived on. Defaults to **All channels**. | | **Environment** | Filters by deployment environment: `development`, `staging`, `production`, or `working-copy`. Defaults to **All environments**. | | **Source** | Filters by session source. Defaults to **All sources**. | | **Filters** | Opens additional filter options. | Select **Columns** to choose which columns the table displays. Select **Export** to download the session list. Sessions started from a project's working copy, the pre-deployment draft, are recorded under the `working-copy` environment. Filter it out to keep pre-deployment testing from skewing your production metrics, or select it to review draft behavior on its own. ### Session list | Column | Description | | - | - | | **Session ID** | Unique session identifier, with a copy control. | | **Agent** | Agent that handled the session, for example `MainSupervisor`. | | **Status** | Current session state, for example `ended`. | | **Environment** | Environment the session ran in. | | **Last Activity** | Time since the last recorded activity. Sorts the table by default. | | **Traces** | Number of trace events the session generated. | | **Duration** | Total session duration. | | **Tokens** | Total tokens the session consumed. | | **Cost** | Estimated cost for the session. Values below the display threshold show as `<$0.001`. | | **Errors** | Error count for the session. Non-zero values are highlighted. | Expand a row to see the full session ID, channel, trace event count, and last activity. Select **View Traces** to open that session in **Traces Explorer**. ## Traces explorer **Traces Explorer** inspects the individual events inside a session. ### Filter traces Type chips filter events to **All**, **LLM Call**, **Tool Call**, **Decision**, **Handoff**, **Error**, or **Agent**. Each applied filter appears as a chip below the search field, for example `Type: LLM Call`. Remove a single filter from its chip, or select **Clear all** to remove them all. **Reset filter** shows the number of active filters and restores the unfiltered list. Use **Search traces** to match events by keyword, **Filters** for additional filter options, and **Export** to download the trace list. ### Sessions panel The left panel lists the sessions in scope. Each card shows the agent name, status, span count, turn count, and duration. Select a session to load its events in the detail panel. ### Timeline and waterfall The detail panel shows the selected session's events in one of two views. A counter reports how many events the current filter matches, for example `4 of 84 events`. | View | Description | | - | - | | **Timeline** | Lists events in sequence as expandable cards. Use it to read an event's full payload. | | **Waterfall** | Plots spans on a shared time axis, with each span's offset from the session start and its duration bar. Use it to find latency gaps and see where errors occurred. | **Waterfall** opens with a summary bar reporting the trace mode (for example, **Historical**), **Spans**, **Duration**, **Tokens**, **Cost**, and **Errors**. Each span row shows a status icon for success or failure, the agent and model, the offset from session start, and the duration bar. Chips on the row repeat the span duration, token count, and cost. ### Event detail Every event card carries the event type, agent name, and duration, plus `phase`, `run`, and `cause` badges. Expand a card for the full record. | Field | Description | | - | - | | **Trace ID** | Identifier for the trace the event belongs to. | | **Session ID** | Session the event belongs to. | | **Span ID** | Identifier for this span. | | **Event ID** | Identifier for this event. | Each identifier has a copy control. An **LLM Call** event adds model execution detail: **Model**, **Provider**, **Input Tokens**, **Output Tokens**, **Total Tokens**, **Cost**, and **Streaming**. Expand **Messages** to read the prompt and user turns, and **Response** to read the model output. A **Tool Call** event shows the **Tool** name, an expandable **Input**, and **Show raw data** for the unformatted payload. ## Generations The **Generations** tab lists every model call the platform recorded, drawn from a unified LLM call ledger. The ledger captures calls from across the platform in one place, including agent turns, pipeline runs, and guardrail evaluations, so you can account for model spend and latency across the whole project instead of one surface at a time. Each call also records the credential that supplied provider access, so you can attribute token consumption and cost to a specific credential or auth profile. Use **Search generations** to match calls by keyword, **Filters** for additional filter options, **Columns** to choose which columns the table displays, and **Export** to download the list. ### Generation list | Column | Description | | - | - | | **Model** | Model that served the call, for example `gpt-5.2`. | | **Name** | Caller that issued the request: an agent name such as `MainSupervisor`, or an internal operation such as `pipeline_generate_text` or `guardrail_evaluation`. | | **Status** | Outcome of the call, for example `Success`. | | **Channel** | Channel the call originated on, for example `web_debug`. Shows `--` for calls not tied to a channel, such as pipeline runs. | | **Source** | Part of the platform that issued the call. See [Sources](#sources). | | **Latency** | Time the call took end to end. | | **Credential Name** | Name of the credential or auth profile that supplied provider access for the call. See [Credential attribution](#credential-attribution). | | **Provider** | Provider of the model that served the call, for example `openai`. | | **Credential Type** | Kind of source that supplied provider access, for example `credential`. See [Credential attribution](#credential-attribution). | | **Total Tokens** | Input and output tokens combined. | | **Cost** | Estimated cost for the call. Values below the display threshold show as `<$0.001`. | | **Start Time** | Date and time the call started. | | **TTFT** | Time to first token. Shows `NA` when the call didn't report one. | ### Sources The **Source** column shows a readable label rather than an internal identifier, so you can tell at a glance which part of the platform issued a call. | Source | What it covers | | - | - | | **Runtime** | Model calls agents make while handling conversations. | | **Runtime Internal** | Supporting model calls the runtime makes outside the agent's own turn. | | **Pipelines** | Model calls made by analytics pipelines, such as LLM-as-judge evaluations. | | **Guardrails** | Model calls made while evaluating content with guardrail policies. | Filter by **Source** to separate what your agents spend from what evaluation and safety checks spend. Pipeline and guardrail calls run on every qualifying conversation, so they can account for a large share of total calls without appearing in any single agent's numbers. ### Credential attribution **Credential Name** and **Credential Type** identify the source that supplied provider access for each call. Use them to see which credential or auth profile consumed tokens and cost when a project and its workspace use several model configurations side by side. | Credential type | What it identifies | Credential name shown | | - | - | - | | `credential` | A named credential that a model connection uses. | The credential name. Credential names can't change, so the name always matches the name at the time of the call. | | `auth_profile` | An auth profile that a model connection uses. | The auth profile's current name. When you rename an auth profile, every call it supplied, past and future, shows the new name. | | `platform_credit` | Model access that the platform manages. | `platform-credit` | | `direct_key` | A legacy model connection that stores a provider key directly, with no credential or auth profile. | `Direct-key` | Credentials and auth profiles that share a name stay separate. A project-level credential, a workspace-level credential, and a credential in another project never combine, even when their names match. The following rules decide which credential a call reports and how long it keeps it. | Situation | Behavior | | - | - | | Calls recorded before attribution | Don't carry a credential. The platform doesn't backfill earlier calls. | | Replacing a provider key | Create a new credential for the new key. The new credential becomes a separate source, and its usage doesn't merge with the old credential's. | | Failed calls | Carry a credential on the same terms as successful calls. | | Pipeline and guardrail calls | Carry a credential when they write to the LLM call ledger. | | Deleting a credential or auth profile | Calls keep their credential name for as long as the platform retains the call. | | Secrets | Attribution stores only the credential identity and name. It never stores provider keys or secret values. | ## Query The **Query** tab runs SQL on project event data for investigations the pre-built views don't cover. 1. Select a table, for example `abl_platform.platform_events`. 2. Write your SQL, or select **Example Queries** to start from a template. Templates include a starter query for the feedback table, which returns ratings, comments, and the custom dimensions captured on each session. 3. Select **Execute**. Select **Clear** to empty the editor. Queries use the same scoping tokens as dashboard widgets. Use `{tenantId:String}` and `{projectId:String}` for tenant and project isolation, and `{from:DateTime64(3)}` and `{to:DateTime64(3)}` to follow the selected time range. `{sessionId:String}` is optional. Results are capped at 1,000 rows. The following query counts events by type and flags errors: ```sql theme={null} SELECT event_type, count() AS cnt, countIf(has_error = 1) AS errors FROM abl_platform.platform_events WHERE tenant_id = {tenantId:String} AND project_id = {projectId:String} AND timestamp >= {from:DateTime64(3)} AND timestamp <= {to:DateTime64(3)} GROUP BY event_type ORDER BY cnt DESC LIMIT 100 ``` The results panel reports the row count and runtime, for example `Results — 20 rows (11ms)`. Select **Copy** to copy the result set, or **Add to dashboard** to save the query as a widget on a custom dashboard. # Analytics pipelines Source: https://koreai.mintlify.app/agent-platform/analytics-insights/analytics-pipelines This document covers the pipeline framework in Analytics and Insights: the built-in pipelines that power Agent Performance, Quality Monitor, and Customer Insights, and the visual node-based editor for building custom evaluation logic. Navigation: **Project** → **Insights** → **Pipelines** Each pipeline card displays its name, description, enabled/disabled status (green "Enabled" badge or gray "Disabled" badge), trigger count, and last processed timestamp (or "Never processed" if the pipeline hasn't run yet). Use the search bar at the top to find pipelines by name. Pipelines ## Sections | Section | Purpose | | - | - | | **Built-in** | Pre-configured pipelines that ship with the platform, ready to enable with a single toggle. | | **Custom** | User-defined processing workflows created using the visual pipeline editor. | | **Recent Runs** | Pipeline execution history with timestamps, durations, status (success/failure), and links to output data. | | **Data** | Pipeline output data available for dashboard integration, export, and downstream consumption. | ## Built-in pipelines The platform ships with eleven pre-built pipelines covering evaluation, classification, detection, and monitoring needs. Enable each pipeline with a single toggle — no configuration required for the defaults. | Pipeline | Description | | - | - | | **Sentiment Analysis** | Per-message sentiment scoring with conversation-level trajectory analysis. Feeds the Sentiment Trajectory chart in Customer Insights. | | **Intent Classification** | Classifies conversation intent using LLM analysis with customer-defined taxonomy or auto-discovery. Feeds the Intent Distribution chart in Customer Insights. | | **Quality Evaluation** | LLM-as-judge quality evaluation with configurable rubric dimensions. Feeds the Quality Score in Agent Performance and Quality Monitor. | | **Hallucination Detection** | Detects unsupported claims, self-contradictions, and factual accuracy issues in agent responses. | | **Knowledge Gap Analysis** | Identifies gaps in knowledge base coverage by analyzing retrieval precision and uncovered topics. | | **Guardrail Analysis** | Evaluates guardrail effectiveness — detects false positives, false negatives, and bypass attempts. | | **Context Preservation** | Evaluates whether agents preserve relevant user and workflow context through the conversation. | | **Friction Detection** | Detects user frustration signals — rephrased questions, message escalation, caps, and exclamation patterns. | | **Anomaly Detection** | Monitors analytics metrics for statistical anomalies using z-score and SPC (Statistical Process Control) charts. | | **Drift Detection** | Monitors analytics metrics for gradual performance drift by comparing baseline and current windows. | | **Evaluation Run** | Executes persona × scenario × evaluator matrix evaluation with bias mitigation and trajectory scoring. | ## Custom pipelines Custom pipelines let you define your own analytics logic using a visual, drag-and-drop node-based editor. Use custom pipelines to build organization-specific evaluation criteria that go beyond the built-in set — for example, regulatory compliance checks, brand voice adherence, or domain-specific accuracy scoring. ### How the editor works The editor presents a visual canvas where you construct a pipeline as a directed graph of connected nodes. A **Node Palette** on the left side provides draggable node types organized into categories: | Category | Node types | | - | - | | **Data** | Read Conversation, Read Message Window, Aggregate, Database Query, Filter, Transform Data, Inspect Output | | **Logic** | Sub-Pipeline (reuse an existing pipeline as a step), Wait for Event (pause execution until a named event fires) | The core pattern: define a trigger, connect one or more processing nodes, then wire the output to a storage or metrics node. Pipelines-2 ### Pipeline structure | Component | Description | | - | - | | **Trigger** | Defines when the pipeline runs. Trigger types include Kafka events, scheduled intervals, in-platform events (for example, conversation completed), or filtered subsets of sessions. | | **Processing nodes** | The evaluation and transformation logic applied to each triggered item. Nodes can read conversation data, query databases, aggregate metrics, filter records, transform data shapes, or call LLMs for evaluation. | | **Metrics output** | The results the pipeline produces. Metrics take the form of named, typed values (counts, percentages, scores) that appear in the Data section and that you can wire into dashboards. | ### Pipeline lifecycle Pipelines follow a Draft → Validate → Test → Activate lifecycle. The editor toolbar shows the current state (Draft badge, "Validation passed" indicator) and provides buttons for Test, Validate, Save, and Activate. You can iterate on draft pipelines without disrupting live ones, save drafts as you go, validate to catch configuration errors before deployment, and run test executions to verify output before activating. ### Sub-pipelines You can reference one pipeline as a step inside another, enabling reuse and composability. For example, a "Compliance Evaluation" pipeline could call both the built-in "Guardrail Analysis" and a custom "Regulatory Wording Check" as sub-steps. ### Attach to dashboards Once a custom pipeline produces data, its metrics appear alongside built-in metrics in **Agent Performance**, **Quality Monitor**, and **Customer Insights**, giving teams a single pane of glass across both standard and organization-specific evaluation. Start with the built-in pipelines to establish baselines, then create custom pipelines for organization-specific quality dimensions. The validate-before-activate workflow catches configuration errors before pipelines go live. # Custom dashboards Source: https://koreai.mintlify.app/agent-platform/analytics-insights/custom-dashboards The **Custom Dashboards** page lets you build project-shared dashboards from governed SQL queries and visualizations. You create a dashboard, query your data with SQL against the platform tables, and save each visualization as a widget, every dashboard is shared across the project. Navigation: **Insights** → **Custom Dashboards** Date range: Set at the top of the dashboard, for example **Last 7 days**. Widgets that use the `from` and `to` date tokens follow this range, all applicable widgets update. **Filters**: Select **All environments** and **All channels** at the top of the page to narrow the data by deployment environment and channel. Filter selections are reflected in the page URL, so you can bookmark a filtered view or share it with a colleague. When a project has no dashboards, the page shows **No dashboards yet**. Select **New dashboard** to create your first one. Customer Dashboards ### Dashboard actions | Action | Description | | - | - | | **New dashboard** | Creates an empty dashboard that you name and populate with widgets. | | **Import** | Loads a dashboard definition from a file. | | **Export** | Saves the selected dashboard's definition to a file, so you can move it between projects or keep a backup. | | **Refresh** | Re-runs the queries behind the dashboard's widgets and redraws them with the latest data. | | **Edit** | Opens the dashboard for changes, such as adding, arranging, or removing widgets. | | **Time range** | Sets the dashboard date range that date-scoped widgets follow. | ### Build a widget Each visualization on a dashboard is a widget. Select **Add widget** to open the widget editor, which opens as **Untitled widget** and stays open until you save or cancel. | Control | Description | | - | - | | **Query data** | Selects the table and holds the SQL that returns your data. | | **Preview rows** | Shows the rows the query returned, with result statistics. | | **Visualize** | Turns the result set into a chart and configures how it displays. | | **Save widget** | Saves the widget to the dashboard. | | **Cancel** | Discards the widget and closes the editor. | To build a widget: 1. Open a dashboard and select **Add widget**. 2. On **Query data**, select a table from the table selector, for example `abl_platform.platform_events`. 3. Write your SQL. Use the **Table columns** panel to find column names and data roles. 4. Select **Run query**. The result panel below the editor reports the rows returned. 5. Open **Preview rows** to confirm the query returns what you expect. 6. Open **Visualize**, choose a chart type, then map your fields to the axes. 7. Select **Save widget**. #### Query data The SQL editor sits at the center of the tab, with the table selector and **Run query** above it. Queries are scoped by `tenant_id` and `project_id` by default. Use the `from` and `to` date tokens when the widget should follow the dashboard date range. See [Example of Query scoping](#example-of-query-scoping). The right rail supports the editor with three panels. | Panel | Description | | - | - | | **Custom dimensions** | Lists the custom dimensions your project captures, so you can group and filter widgets by business-specific values. Custom metrics and dimensions now cover workflow sessions, from function nodes and agent nodes, also to the agent sessions. | | **Viewer filters** | Lists the fields you can filter this widget on. Use **Find a field** to search, then select **Add filter to this widget**. A viewer filter shows on this widget only. Promote it to apply the filter across the whole dashboard. | | **Table columns** | Lists every column in the selected table, with a data role badge of `id`, `time`, `dimension`, or `metric`. Use **Filter columns** to search long column lists. | #### Result panel After you run a query, the panel below the editor holds the result. Switch between **Preview rows** and **Visualization** without leaving the result, so you can check the data and the chart against each other. The result reports five statistics. | Statistic | Description | | - | - | | **Rows** | Number of rows the query returned. | | **Columns** | Number of columns in the result set. | | **Runtime** | Time the query took to execute, in milliseconds. | | **Row cap** | Maximum rows a single fetch returns, for example `1,000 max`. Table widgets page through results beyond this cap. | | **Range** | Date range the query ran against, for example `Last 7 days`. | Below the statistics, a chip for each column shows the data role the platform assigned it, for example `timestamp: time` and `rows: metric`. These roles determine which columns are available to each axis in **Visualize**. **Preview rows** displays the result as a table, one column per field in the result set. #### Visualize **Visualize** builds the chart from the result set. A status badge reads **Ready** when the current mapping produces a valid chart. Four tabs configure the visualization. | Tab | Settings | | - | - | | **Charts** | Selects the chart type: **Line**, **Area**, **Bar**, **Combo**, or **Table**. Select **More charts** for additional types. **Table** widgets page through large result sets, so high-volume data isn't truncated at the first 1,000 rows. | | **Fields** | Maps result columns to the chart. Set the **X-axis** column, then add one or more **Y-axis series**. Select **Add field** to plot another series, or the delete icon to remove one. | | **Display** | Sets **Value format** and **Secondary Y axis** behavior, for example **Auto (split mixed scales)** when series use different scales. A **Current mapping** card shows the active X-axis and Y-axis series. | | **Axes** | Sets the **X-axis label** and **Y-axis label**. Labels default to the mapped column names. | ### Example of query scoping The platform scopes queries to the current tenant and project by default. Let us consider `abl_platform.platform_events` as the query table. Use the date tokens when a widget should follow the dashboard date range. | Token | Description | | - | - | | `{tenantId:String}` | Scopes the query to the current tenant. | | `{projectId:String}` | Scopes the query to the current project. | | `{from:DateTime64(3)}` | Start of the dashboard date range. | | `{to:DateTime64(3)}` | End of the dashboard date range. | The following query counts events over time and follows the dashboard date range: ```sql theme={null} SELECT timestamp, count() AS rows FROM abl_platform.platform_events WHERE tenant_id = {tenantId:String} AND project_id = {projectId:String} AND timestamp >= {from:DateTime64(3)} AND timestamp <= {to:DateTime64(3)} GROUP BY timestamp ORDER BY timestamp DESC LIMIT 100 ``` # Customer voice Source: https://koreai.mintlify.app/agent-platform/analytics-insights/customer-voice This document covers the customer experience layer in Analytics and Insights: Customer Insights for intent classification, sentiment trajectory, and frustration detection; Feedback for end-user ratings and verbatim comments; and Feedback Insights for the results of an individual feedback survey. ## Customer insights The **Customer Insights** page helps you understand what customers are asking about and how they feel about the experience. It combines intent classification, sentiment scoring, frustration detection, and resolution tracking into a single view to help you identify emerging topics, detect dissatisfaction early, and measure whether the agent resolves the intents it encounters. Navigation: **Project** → **Insights** → **Customer Insights** **Date range selector**: Use the toggle to select **7d**, **30d**, or **90d**. **Filters**: Select **All environments** and **All channels** at the top of the page to narrow the data by deployment environment and channel. Filter selections are reflected in the page URL, so you can bookmark a filtered view or share it with a colleague. Customer Insight ### Customer sentiment summary A banner at the top displays a contextual summary of the analyzed data, for example: "322 analyzed conversations · 144 intent-classified · 322 sentiment-scored · 13 intents detected · 26.7% frustration." A sentiment indicator (for example, "Mixed Sentiment") provides a quick qualitative read. ### KPI metric cards | Metric | Description | | - | - | | **Analyzed Conversations** | Total conversations that pipelines analyzed in the selected period. The sub-label shows the breakdown. | | **Unique Intents** | Number of distinct intents the system identified across all analyzed conversations. | | **Avg Sentiment** | Average sentiment score across all conversations. A score near 0 indicates neutral sentiment; positive values indicate positive sentiment. | | **Frustration Rate** | Percentage of conversations where the system detected user frustration signals (repeated questions, negative language, escalation requests). A warning triangle appears when the rate exceeds the configured threshold. | | **Resolution Rate** | Percentage of conversations reaching successful resolution. A warning triangle appears when the rate drops below the threshold. The sub-label shows the count of resolved conversations the system evaluated. | ### Intent distribution A horizontal bar chart ranks detected intents by volume. Each bar shows the intent name, count, and percentage of total conversations. The chart groups low-volume intents (typically those below a threshold count) into an "Other" category. The footer indicates the total classified intent assignments and how many low-volume intents the chart grouped. ### Sentiment trajectory A side-by-side panel displays conversation counts across three sentiment directions: | Direction | Description | | - | - | | **Improving** | Conversations where sentiment trended positively over the course of the interaction. | | **Stable** | Conversations where sentiment remained consistent throughout. | | **Declining** | Conversations where sentiment deteriorated — candidates for investigation. | The footer indicates the total conversations with sentiment data contributing to the trajectory analysis. ### Trends over time A section below the distribution charts plots intent volumes and sentiment scores as time-series data, enabling you to track whether specific intents are growing or shrinking and whether sentiment is trending in the right direction. *** ## Feedback insights The **Feedback Insights** page reports the results of a single feedback survey. Use it to read the headline score, see how the score moved, find the segments driving it, and open the individual responses behind it. Feedback Insights reads the responses collected against a [feedback template](/agent-platform/feedback-template). Responses reach the page from agents that reference the template and from external systems that submit through the public feedback API. Until you create a template, the survey list offers only the built-in `message_feedback` capture, and the page prompts you to set up a survey before it reports anything. **Navigation**: **Project** → **Insights** → **Feedback Insights** **Date range selector**: Use the toggle to set the reporting window, for example, **Last 7 days**. **Filters**: Select **All environments** and **All channels** at the top of the page to narrow the data by deployment environment and channel. **Export**: Select **Export CSV** to download the current results for offline analysis. ### Survey and score method Two controls at the top of the page define every figure below them. | Control | Description | | - | - | | **Survey** | Selects the survey to report on. The list contains every feedback template in the project, plus the built-in `message_feedback` capture. Every figure on the page belongs to the survey you select. | | **Scored as** | Selects how the page reads the recorded ratings. Changing the score method changes the figure, never the underlying data. | ### Overview The **Overview** tab reports the headline score for the selected survey, the comparison against the prior period, and the count of externally submitted responses. Below the score, the response funnel shows how many of the people who met the survey went on to answer it. | Stage | Description | | - | - | | **Presented** | Surveys rendered in a conversation, whether or not anyone answered. | | **Scored** | Responses where the user submitted a rating. This is the denominator for every score on the page. | | **Commented** | Responses that included comment text. Reads as not applicable when the survey doesn't ask for comments. | | **Abandoned** | Surveys presented and not answered inside the selected window. | The funnel also reports two rates. The response rate needs a presented count, and reads as not available until the survey records one. The comment rate reads as not applicable when the survey doesn't ask for comments. Surveys have no answer window, so a late response can still turn an abandoned survey into a scored answer. #### Read the margin before the number The page styles a change as movement only when the change clears the combined margin of both periods. An index built from two proportions carries roughly twice the margin of a plain percentage at the same sample size. NPS is such an index: it subtracts the share of detractors from the share of promoters. At the same number of responses, the same visible gap on an NPS figure means less than it would on a plain percentage. ### Trend and distribution The **Trend & distribution** tab shows how the score moved across the selected period, and how the responses spread across the rating scale. Use the granularity control on the **Trend** panel to change the interval, for example, from daily to a wider grouping. ### Segments The **Segments** tab breaks the score down by the dimensions the platform records with each response. | Segment | Compares scores across | | - | - | | **Agent** | The agents that presented the survey. | | **Rendered language** | The language the survey rendered in. | | **Version** | The project version in use when the response came in. | Select **Movers first** to order segments by the size of their change. The page orders segments by significance, so a segment with few responses can't headline the list on a dramatic-looking number alone. ### Responses The **Responses** tab lists the individual responses behind the figures on the other tabs. Four controls let you narrow the list. | Filter | Description | | - | - | | **All agents** | Filter by the agent that presented the survey. | | **All ratings** | Filter by a specific rating value. | | **All languages** | Filter by the language the survey rendered in. | | **Search comments** | Match text within submitted comments. | If no responses match, the tab reports that it found no feedback for the selected filters. Widen the date range or clear a filter. # Usage insights Source: https://koreai.mintlify.app/agent-platform/analytics-insights/executive-dashboard-usage This document covers the two pages that answer how the program performs and what it costs. * **At a Glance** reports conversation outcomes, quality, sentiment, and return on investment for a period. * **Billing and Usage** reports what the project consumed and which part of that consumption bills. Both pages report on conversations that evaluation pipelines have already processed. Until a pipeline runs, the metric cards show dashes rather than zeros. ## At a Glance **At a Glance** aggregates outcome, quality, sentiment, and cost metrics into a single view that needs no configuration. Use it for a daily check or a leadership review, and to decide which of the deeper pages to open next. **Navigation**: **Project** -> **Insights** -> **At a Glance** ### Page filters Three controls at the top of the page scope everything below them: an environment filter, a channel filter, and a date range. Narrow the environment filter when production and non-production traffic would otherwise mix. Test conversations distort containment and cost figures more than any other input. When you change a filter, a **Reset filter** control appears with the number of filters you applied. Check it before you conclude that a period was quiet, since a narrowed view looks much the same as a slow week. Dashboard ### Metric cards Six cards summarize the selected period. Each card shows a headline value and a supporting figure that supplies its denominator. Read the two together, because a containment rate means one thing across a thousand conversations and something else across one. | Metric | Description | | - | - | | **Conversations** | Counts total conversations in the period and shows how many of them pipelines evaluated. The evaluated count forms the sample that produces every other card on this page, so a wide gap between the two figures means the rest of the view describes only part of your traffic. | | **Containment Rate** | Shows the share of evaluated conversations the agent resolved without handing off to a person. Most programs treat this as their headline automation measure. The card takes a warning treatment when the rate falls below the expected threshold. | | **Quality Score** | Averages the scores the quality evaluation assigns to the agent's responses. Use it as the counterweight to containment. Containment alone can rise because the agent stopped offering escalation, and quality tells you whether the agent handled the conversations it kept. | | **Avg Sentiment** | Averages sentiment across evaluated conversations. Quality judges what the agent produces, and sentiment reflects how the customer responds to it. The two measures can diverge, and the gap between them usually carries the signal. | | **Period Savings** | Models the cost you avoid when the agent contains a conversation instead of a person handling it. The figure rests on the assumptions you set in ROI cost settings, so treat it as directional and revisit it when your staffing costs change. | | **Escalation Rate** | Shows the share of evaluated conversations the agent handed to a person. This rate isn't the inverse of containment. Conversations the agent handled only in part and didn't escalate count toward neither rate, so both can read low over the same period. | A card shows a dash when the pipeline behind it produces no value for the period. A dash means the measure has no data, not that it scored zero, and it usually indicates that the relevant pipeline isn't running. ### Overview tab The **Overview** tab explains the headline numbers. **Conversation Volume & Containment Rate** plots volume and containment on one timeline. The chart separates the two ways a containment figure moves: the agent resolves a different share of conversations, or the volume changes underneath a stable share. **Outcome Distribution** divides the same conversations into three categories. Each category appears as a segment with its count, its share of the total, and a caption that describes the outcome. A footer states how many conversations the distribution covers. | Outcome | Description | | - | - | | **Resolved** (green) | The agent completed the conversation without involving a person. | | **Contained partial** (amber) | The agent handled the conversation but didn't carry it to a full resolution. These conversations count toward neither the containment rate nor the escalation rate, so both rates can read low over the same period. | | **Escalated** (red) | The agent handed the conversation to a person. | The middle category is the reason to read this section rather than the cards alone. Containment and escalation together imply a binary outcome, and the partial category holds the conversations that fit neither. **What's Driving the Metrics** attributes the period to individual agents, so you can locate the cause of a project-level movement. | Column | Description | | - | - | | **Volume** | Counts the evaluated conversations that belong to this agent. Sort on this column to find the agents that carry enough weight to move the project figures, since a poor score across a handful of conversations rarely explains an aggregate shift. | | **Confidence** | Shows the confidence that accompanies this agent's evaluation results. The column stays blank until the pipeline produces a confidence value. | | **Quality** | Shows this agent's own quality score, so you can see whether the project average describes your agents evenly or conceals a spread. | | **Sentiment** | Averages customer sentiment across this agent's conversations. | | **Trend** | Shows the direction of change from the preceding period. The column stays blank until the project accumulates enough history to compare. | ### Trends tab The **Trends** tab answers questions about direction rather than current state. Three charts cover the period. **Multi-Metric Trends** plots containment, escalation, and quality on one timeline. Reading the three together is what makes the tab useful, because a containment rise that coincides with a quality fall is a different event from one that doesn't. **Sentiment & Quality Over Time** plots average quality and average sentiment. Point to any date to read its values. **Daily Outcome Distribution** shows the outcome mix day by day. Use it to tell whether a change in the period totals comes from a shift in how conversations end or simply from more of them. ### ROI tab The **ROI** tab converts contained volume into money and staffing. Every figure except cost per conversation rests on the assumptions in cost settings, so agree those with your finance team before you circulate anything from this tab. | Metric | Description | | - | - | | **Period Savings** | Shows the modeled human cost you avoided, net of what the AI spent. | | **Period ROI** | Expresses that saving as a return on your AI spend. The card takes a warning treatment when the period produces no positive return, which is normal early in a deployment while volume stays low and the assumptions stay fixed. | | **FTE Equivalent** | Restates contained volume as human staffing capacity and shows the equivalent monetary capacity underneath. Use it in headcount discussions, where an FTE figure lands better than a savings total. | | **Cost / Conversation** | Divides actual AI cost by conversations. This card reports real spend rather than your assumptions, so quote it when someone disputes the assumptions. | **AI Cost vs Estimated Human Cost** plots both costs across the period and states the per-conversation human cost it applies, so anyone who reads the chart can see the assumption behind the comparison. The platform attributes actual model costs to contained conversations. ### Conversations tab The **Conversations** tab lists the individual conversations behind the metrics. Select a conversation to open its session record, which holds the full trace, cost, and message history. Use this tab when a metric looks wrong and you need to read the conversations that produced it. ### ROI cost settings Select **Cost Settings** on the **ROI** tab to set the assumptions that produce the ROI figures, then select **Save**. Nothing here affects what the platform bills you. These values only determine the baseline that the platform compares your actual spend with. | Field | Description | | - | - | | **Human Cost per Conversation (\$)** | Sets the loaded cost of one conversation that a person handles. The platform credits every containment with avoiding this amount, so this field drives the savings figures and the human cost line on the ROI chart more than any other input. | | **Human FTE Monthly Cost (\$)** | Sets the fully loaded monthly cost of one full-time agent. The platform uses it to convert avoided volume into the FTE Equivalent figure. | | **Avg Human Handle Time (minutes)** | Sets the staffed time that one conversation consumes. With the FTE cost, it determines how much contained volume adds up to one person's capacity. | | **Estimated AI Cost per Conversation (\$)** | Sets the fallback cost the platform applies when it can't attribute actual model cost to a conversation. Without this value, unattributed conversations appear to cost nothing and overstate your savings. | *** ## Billing and usage **Billing and Usage** shows what the project consumed, how much of that consumption bills, and the policy that decides the difference. Finance and operations teams use it to reconcile spend and to explain gaps between activity and charges. **Navigation**: **Project** -> **Insights** -> **Billing & Usage** ### Data currency The page reads published billing report rows rather than live traffic, and it states when the platform last published those rows. Check that timestamp before you reconcile a period, because recent activity appears only after the next materialization. A date range control scopes the page. ### Usage and billing units | Metric | Description | | - | - | | **Included Sessions** | Counts the sessions that meet the billing policy and therefore contribute to charges. This number falls below your total session count whenever exclusions apply, and the page itemizes the difference further down. | | **Billing Units** | Counts the chargeable units the platform derives from included sessions using the billing interval and the duration basis. This number maps to your invoice. | | **LLM Calls** | Counts model invocations in the period. This card can show substantial activity while billing units sit at zero, because calls inside excluded sessions still run. Treat a wide gap between the two as a prompt to check your exclusions. | | **Interactive Turns** | Counts user turns across the period. The interaction threshold in the billing policy uses turn count to decide whether a session carries enough interaction to bill. | **Daily Billing Units** plots billing units and included sessions across the period, so you can trace a spike in charges to either more sessions or longer ones. **Channel Breakdown** reports sessions, units, and model calls per channel. It shows which channel drives your spend, and it exposes an excluded channel as activity that produced no units. **Voice minutes** reports voice consumption separately, because voice meters in minutes rather than in the unit model that governs the rest of the page. ### Excluded sessions This section lists the sessions the latest materialization didn't bill, grouped by the reason that excluded each one. Start here when session volume and billed volume disagree. The reason names the policy rule that removed the sessions, which turns an unexplained gap into a policy question. ### Usage by billing scope This section groups sessions by billing scope. When one agent delegates to another, the parent and its children roll up into a single scope, so the platform counts a multi-agent conversation once rather than once per agent. Read this section when your agent architecture makes the session count look higher than the work the customer experienced. ### Resolved billing policy This section shows the billing policy in effect for the tenant and the level that sets each value. The source label matters most: it tells you whether a value comes from your plan or from this project, and therefore whether changing it needs your account team or your own settings. | Setting | Description | | - | - | | **Billing interval (minutes)** | Sets the block of session time that one billing unit covers. It determines how session duration rounds into units, so a long interval and short sessions together decide how much of what you pay for goes unused. | | **Duration basis** | Selects which measure of session duration the platform meters. It decides whether idle time inside a session becomes chargeable. | | **Scope consolidation** | Controls whether the roll-up of delegated agent sessions applies to billing or only to reporting. It determines whether multi-agent fan-out costs more than a single-agent conversation. | | **Excluded channels** | Lists the channels whose sessions never bill. Debug and test channels usually sit here, which explains how development traffic generates model calls without generating charges. | | **Interaction threshold (min user messages)** | Sets the minimum number of user messages a session needs before it bills. It keeps abandoned and no-touch sessions out of your charges. | # Quality insights Source: https://koreai.mintlify.app/agent-platform/analytics-insights/performance-quality This document covers agent evaluation in Analytics and Insights. * **Quality Monitor** reports quality by evaluation dimension across the project. * **Agent Performance** reports the same measures agent by agent. The two pages answer different questions. Quality Monitor tells you which aspect of quality slipped. Agent Performance tells you which agent caused it. Both pages populate only after evaluation pipelines process conversations. A measure with no evaluations shows a dash rather than a zero. ## Quality monitor **Quality Monitor** aggregates the output of the evaluation pipelines into one view, scored by dimension. Use it to decide where quality work is worth doing, rather than to judge how a single conversation went. **Navigation**: **Project** -> **Insights** -> **Quality Monitor** ### Page filters Environment, channel, and date range controls at the top of the page scope everything below them. Filter by environment before you read the scores. Evaluations of test traffic and evaluations of production traffic answer different questions, and an average of both tells you neither. Quality Monitor ### Quality health A banner across the top gives three figures: the number of conversations the pipelines evaluated, a weighted overall score that combines the dimensions, and a count of dimensions in each status. The three statuses are **Critical** (red), **Warning** (amber), and **Healthy** (green). Read the status counts first. They tell you how many dimensions need attention without your having to interpret each card. A dimension with no data also counts toward the total, so a warning count can reflect missing coverage as readily as poor performance. ### Dimension cards Each card reports one evaluation dimension for the period. Higher values are better on every dimension, so a low score always signals a problem rather than a threshold you need to interpret. | Dimension | Description | | - | - | | **Overall Quality** | Combines the scores of the dimensions that hold data. Track it for direction over time. It runs too coarse to act on directly, which is the job of the remaining cards. | | **Faithfulness Score** | Measures how well responses stay grounded in the sources the agent has available. A low score means the agent produces claims it can't support, the failure mode that costs the most trust when customers notice it. | | **Knowledge Coverage** | Measures how often the knowledge base supports the query the agent receives. This dimension scores your content rather than your agent, so a low score tells you to write documentation instead of changing prompts. | | **Safety Score** | Measures the rate at which responses pass the configured guardrails. It moves only when guardrails exist, so a persistent dash usually means you have no guardrails in place rather than that the platform checked nothing. | | **Context Preservation** | Measures how well the agent carries context across turns in one conversation. It degrades first as conversations grow longer, and it usually explains complaints about customers repeating information. | A dash means the pipeline behind that dimension produced no value for the period. It doesn't mean the dimension scored zero. ### Quality trend The trend chart plots each dimension as its own series over the period. Use it for correlation. Dimensions rarely move alone, and a fall in one alongside a rise in another usually points to a single underlying change, such as new traffic the knowledge base doesn't yet cover. ### Dimension details Below the chart, each dimension appears with its score and a **Critical**, **Warning**, or **Healthy** badge. Expand a dimension to see the component measures that produced its score. The expansion separates a dimension that one weak measure pulls down from a dimension that runs weak throughout, and the distinction matters because the first needs a targeted fix and the second usually doesn't. Dimensions with no evaluations in the period carry a no-data badge in place of a status, so you can tell an unmeasured dimension from a failing one. ## Agent performance **Agent Performance** reports the evaluation measures per agent. Open it once Quality Monitor tells you a dimension slipped. It earns its place in multi-agent projects, where one weak agent can pull down a project average that otherwise looks acceptable. **Navigation**: **Project** -> **Insights** -> **Agent Performance** ### Page filters Environment, channel, and date range controls scope the page. Select **Compare previous period** to add a comparison with the period immediately before the one you selected. The comparison turns the scores from absolute values into a direction of travel. Use it before you draw conclusions from a single period, because evaluation scores move considerably on low volume. Agent Performance ### Agent health A banner reports how many agents the pipelines evaluated, how many conversations that covered, and how many agents fall into each status: **Critical** (red) and **Healthy** (green). It tells you whether you face a broad problem or a single agent, before you read any individual score. ### Metric cards Five cards aggregate the measures across all agents in scope. | Metric | Description | | - | - | | **Quality** | Averages the quality score across evaluated conversations on a five point scale. The table below reports the same measure per agent, so compare the two to see whether the average describes your agents evenly or conceals a spread. | | **Hallucination Rate** | Reports the rate at which the pipeline flags responses for unsupported claims. Lower is better here, unlike the other cards, so read this card separately rather than scanning the row for low numbers. | | **Knowledge Gaps** | Counts the conversations where the knowledge base couldn't support the query. It reports a count rather than a rate, so weigh it alongside conversation volume before you treat a rise as a regression. | | **Safety Score** | Reports the rate at which responses pass the configured guardrails. | | **Context Score** | Averages how well agents carry context across turns, on a five point scale. | ### Agent table The table repeats these measures for each agent, alongside the agent's conversation count and an overall status that derives from its scores. The status appears as a **Critical** (red) or **Healthy** (green) badge. Read the table with the conversation count in view. An agent that the pipeline evaluated on a handful of conversations can show the worst scores on the page without being the problem worth fixing. To work through the table: * Search by name to find a specific agent. * Sort on status to bring the agents that need attention to the top. * Switch between **Critical** and **All** using the filter pills to handle the failing agents first. ### Quality trend The trend chart plots quality score and flagged conversation count on separate axes. Read the two series together. Quality that falls while flags stay flat indicates a gradual drift in scoring. Quality that falls as flags rise points to specific conversations going wrong, which you can open and read. # Voice analytics Source: https://koreai.mintlify.app/agent-platform/analytics-insights/voice-analytics This document covers voice channel monitoring in Analytics and Insights: call quality scoring, speech recognition accuracy, end-to-end latency tracking, barge-in rates, and DTMF fallback metrics for voice-enabled agents. Navigation: **Project** → **Insights** → **Voice Analytics** **Date range selector**: Use the toggle to select **Today**, **7d**, or **30d**. **Filters**: Select **All environments** at the top of the page to narrow the data by deployment environment. Filter selections are reflected in the page URL, so you can bookmark a filtered view or share it with a colleague. Channel filtering isn't available on this page. The underlying voice data source doesn't carry a channel dimension, so the channel selector is hidden here even though it appears on other analytics pages. Voice Analytics ## KPI metric cards | Metric | Description | | - | - | | **Total Calls** | Number of voice calls in the selected period. | | **Avg MOS** | Average Mean Opinion Score for call quality on a scale of 1–5. Scores below 3.5 typically indicate noticeable quality issues. | | **ASR Quality** | Automatic Speech Recognition quality score (0–100, higher is better). Measures how accurately the ASR engine transcribes the caller's speech. | | **E2E Latency** | End-to-end latency in milliseconds for the voice processing pipeline. Covers the full round-trip from user speech input through ASR transcription, LLM processing, and TTS output back to the caller. | | **Barge-In Rate** | Percentage of calls where the caller interrupted the agent mid-response. A rising barge-in rate may indicate that responses are too long or latency is too high, prompting callers to cut in. | | **DTMF Fallback** | Percentage of calls falling back to touch-tone (keypad) input, typically when ASR fails to understand the caller. A rising rate may indicate ASR quality issues or unsupported accents/languages. | ## Trend charts | Chart | Description | | - | - | | **Network Quality and Call Volume** | Dual-axis chart plotting MOS scores and call count trends over the selected period. Use this to correlate call quality dips with volume spikes — quality often degrades during peak traffic if capacity-constrained infrastructure cannot keep up. | | **Speech Recognition Quality (ASR)** | ASR quality scores plotted over time. Monitor for sustained degradation, which may indicate noisy caller environments, model drift, or the introduction of new vocabulary that the ASR model does not yet recognize. | Track **E2E Latency** trends after model or pipeline changes. Even small latency increases (50–100ms) can affect caller experience and drive up barge-in rates. # List conversation messages Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-conversation-messages agent-platform/api-specs/messages.yaml get /api/public/analytics/projects/{projectId}/conversation-history Messages are ordered by time, then by message ID to keep the order stable. Different filters combine with AND, and multiple values in the same filter combine with OR. A date-range query with no other filter that would span more than 10,000 sessions is rejected with `400`, so narrow the range or add `sessionIds` for busy projects. Returns the messages exchanged in a project's conversations chronologically. Message content is filtered according to the project's PII policy before it is returned, so sensitive values may be masked or removed. Messages are ordered by time, then by message ID to keep the order stable. Different filters combine with AND, and multiple values in the same filter combine with OR. Give either a date range (`fromDate` and `toDate` together) or a list of `sessionIds`. A date-range query with no other filter may span a long list of sessions, so always use a filter and paginate as needed. A response that span more than 10,000 sessions is rejected with `400`, so narrow the range or add `sessionIds` for busy projects. For pagination, prefer `cursor`: pass the `nextCursor` value from the previous response. `offset` also works, and `skip` is an older name for `offset` that is still accepted. Use only one of the three in a single request. Authenticate with the `x-api-key` header. The key must be bound to the project you are querying and have `analytics:read` permission. # List conversation messages using JWE payload Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-conversation-messages-with-encrypted-payload agent-platform/api-specs/messages.yaml post /api/public/analytics/projects/{projectId}/conversation-history POST form of `GET` on this path. It accepts the same parameters as a flat JSON body (`ConversationHistoryQueryBody`) instead of a query string, and returns the same response. Use it when the request parameters must be encrypted. See "Sending parameters in a POST body" and "Payload encryption" in the API description. - Request encryption off: send `ConversationHistoryQueryBody` as `application/json`. - Request encryption on: send the same object encrypted as an `EncryptedRequest`, preferably as `application/jose+json`. - Response encryption on: every response except `401` is an `EncryptedResponse` with `X-ABL-Encrypted: true`. Retrieve conversation messages while sending the request parameters in the request body instead of the URL query string. It supports the same filters, pagination options, and response format as [the corresponding GET endpoint](/agent-platform/api-reference/analytics-list-conversation-messages). Use the POST endpoint when the request parameters **must be encrypted**. When request encryption is enabled for the Platform Key, send the request parameters as a JSON Web Encryption (JWE) payload. The encrypted payload protects the parameters that would otherwise be exposed in the GET query string. Request encryption is configured on the Platform Key in Studio. The request uses a flattened JWE in JSON serialization and is encrypted using the AES-256 key associated with the Platform Key. If response encryption is also enabled for the Platform Key, the endpoint returns encrypted responses, including error responses, except for `401 Unauthorized` responses. The response is a JWE encrypted for the RSA public key configured on the Platform Key. # List LLM calls Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-llm-calls agent-platform/api-specs/llm-ledger.yaml get /api/public/analytics/projects/{projectId}/llm-ledger `fromDate` and `toDate` are required. `fromDate` is included in the results and `toDate` is not, so a full day is `fromDate=2026-08-04T00:00:00Z&toDate=2026-08-05T00:00:00Z`. All other filters are optional and match exactly. Filters combine with AND, so adding more narrows the results. `source` and `sourceSubtype` also accept older spellings and translate them for you — see those parameters for the details. Use this endpoint to retrieve the LLM call ledger for a project. The response contains one record for each individual LLM call, including details such as the model, provider, token usage, latency, and cost. The endpoint does not aggregate calls by conversation turn. Use the `dataMode` parameter to control the level of detail returned. The default mode returns call-level facts, while `full` additionally includes the exact prompt and response payloads when available. The available filtering criteria are: * Date range — required: `fromDate` and `toDate`. `fromDate` is inclusive, while `toDate` is exclusive. * Source: Filter by the source of the LLM call. * Source subtype: Further filter calls by source subtype. * Model: Filter calls by the LLM model. * Provider: Filter calls by the model provider. * Other query parameters: The endpoint also supports pagination and sorting, but these control how results are returned rather than which records are included. # List LLM calls using JWE payload Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-llm-calls-with-encrypted-payload agent-platform/api-specs/llm-ledger.yaml post /api/public/analytics/projects/{projectId}/llm-ledger POST form of `GET` on this path. It accepts the same parameters as a flat JSON body (`LlmLedgerQueryBody`) instead of a query string, and returns the same response. Use it when the request parameters must be encrypted. See "Sending parameters in a POST body" and "Payload encryption" in the API description. - Request encryption off: send `LlmLedgerQueryBody` as `application/json`. - Request encryption on: send the same object encrypted as an `EncryptedRequest`, preferably as `application/jose+json`. - Response encryption on: every response except `401` is an `EncryptedResponse` with `X-ABL-Encrypted: true`. This endpoint retrieves the LLM call ledger for a project, with the request parameters supplied in the request body rather than the URL query string. It supports the same filters, pagination, sorting, and response formats as [the corresponding GET endpoint](/agent-platform/api-reference/analytics-list-llm-calls). Use the `POST` endpoint when the request parameters must be encrypted. When request encryption is enabled for the Platform Key, send the LlmLedgerQueryBody as an encrypted JWE. When response encryption is enabled, the endpoint also returns the response as an encrypted JWE, including error responses after the API key has been authenticated. The endpoint returns individual LLM calls rather than aggregating calls by conversation turn. You can use `dataMode` to return either call-level details or, with `full`, the stored prompt and response payloads when available. # List session summaries Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-session-summaries agent-platform/api-specs/sessions.yaml get /api/public/analytics/projects/{projectId}/sessions Use `limit` and `offset` to page through the results. The response includes message, trace-event, and token counts per session, but not the messages or trace details themselves. Use the conversation-history endpoint for messages and the traces endpoint for execution detail. Use this endpoint to retrieve session-level analytics for a project. Each result represents one conversation session and includes summary information such as the channel, environment, status, containment outcome, message and trace-event counts, token usage, cost, and session duration. The response does not include the individual messages or trace details; use the conversation-history and traces endpoints to retrieve those details. It provides the following filters: * `fromDate` — Inclusive start timestamp. * `toDate` — Exclusive end timestamp. * `sessionIds` — Filter by specific session IDs; up to 10,000. * `containmentType` — Filter by how the session was resolved, such as `contained_resolved`, `contained_partial`, `escalated`, or `abandoned`. * `environment` — Filter by environment: `dev`, `staging`, `production`, or `working-copy`. * `channelUId` — Filter by the end-user identity recorded on the channel. * `callerNumber` — Filter by caller number or caller identity. * `channel` — Filter by the channel through which the conversation originated. * `traceDimensions[key]` — Filter by custom session dimensions and their values. * `status` — Filter by session state, such as `active`, `idle`, `ended`, `completed`, `escalated`, or `abandoned`. You must provide either `fromDate` and `toDate` together, or `sessionIds`. All other filters are optional. Multiple filters are combined with `AND` logic; multiple values within a filter are combined with `OR` logic. Use `limit` and `offset` to paginate through the results. # List session summaries using JWE payload Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-session-summaries-with-encrypted-payload agent-platform/api-specs/sessions.yaml post /api/public/analytics/projects/{projectId}/sessions POST form of `GET` on this path. It accepts the same parameters as a flat JSON body (`SessionsQueryBody`) instead of a query string, and returns the same response. Use it when the request parameters must be encrypted. See "Sending parameters in a POST body" and "Payload encryption" in the API description. - Request encryption off: send `SessionsQueryBody` as `application/json`. - Request encryption on: send the same object encrypted as an `EncryptedRequest`, preferably as `application/jose+json`. - Response encryption on: every response except `401` is an `EncryptedResponse` with `X-ABL-Encrypted: true`. Use this endpoint to retrieve the same session-level analytics as the [corresponding GET operation](/agent-platform/api-reference/analytics-list-session-summaries), but send the query parameters in a JSON request body instead of the URL query string. It supports the same filters, pagination, validation, permissions, and response format as the GET operation. Use the `POST` endpoint when the request parameters must be encrypted. With request encryption enabled for the Platform Key, send the query object as a JWE encrypted request. With request encryption disabled, send the parameters as a plain JSON object. Response encryption is independent of request encryption; when enabled, responses other than `401` are returned as encrypted JWE payloads. For filter details, see the [GET endpoint help article](/agent-platform/api-reference/analytics-list-session-summaries). # List trace events Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-traces agent-platform/api-specs/traces.yaml get /api/public/analytics/projects/{projectId}/traces `fromDate` and `toDate` are required. `fromDate` is included in the results and `toDate` is not, so a full day is `fromDate=2026-08-05T00:00:00Z&toDate=2026-08-06T00:00:00Z`. To page through results, pass the `nextCursor` value from the previous response back as `cursor`. Keep every other parameter the same — a cursor is tied to the filters, data mode, and sort order it was created with, and changing any of them makes it invalid. To fetch one specific event, filter by `eventId`. There is no separate single-event endpoint. If you request an event name this API does not support, it is ignored rather than rejected, and listed back to you in `meta.unsupportedEventNamesRequested`. Use this endpoint to retrieve the execution events recorded while agents run. The response groups events by trace and can include LLM calls, tool calls, agent decisions, and failures. Each event provides execution metadata and, depending on the selected `dataMode`, either summary information or the complete sanitized request and response content. Use `dataMode=summary` when you need operational information such as the event type, model, token usage, duration, cost, or error status without retrieving the event payloads. Use `dataMode=full` when you need the detailed sanitized request and response content associated with the events. The endpoint requires a `fromDate` and `toDate` range. The start of the range is inclusive and the end is exclusive. You can further narrow the results by event type, event name, session, trace, event, agent, decision type, channel, environment, or error status. Results are paginated using a cursor; because pagination counts events before grouping them into traces, a page can contain fewer traces than the requested event limit. ### Filters * `fromDate` — **Required.** Inclusive start timestamp for the time range. * `toDate` — **Required.** Exclusive end timestamp for the time range. * `eventTypes` — Filter by event category: * `llm` — LLM calls * `tool` — Tool calls * `decision` — Agent decisions * `error` — LLM and tool failures * `eventNames` — Filter by specific event names: * `llm.call.completed` * `llm.call.failed` * `tool.call.completed` * `tool.call.failed` * `tool.call.skipped` * `agent.decision` * `sessionIds` — Filter events belonging to specific sessions; up to 10,000 session IDs. * `eventId` — Return only the event with the specified ID. Use this when you need to retrieve a specific event; there is no separate single-event endpoint. * `traceId` — Return only events belonging to the specified trace. * `agentName` — Filter events produced by a specific agent. The value is matched exactly. * `decisionKind` — Filter agent decision events by decision type, such as `routing`, `handoff`, `escalation`, or `completion`. * `channel` — Filter by the exact channel name recorded for the event. Unlike the Sessions endpoint, this filter does not support channel aliases. * `environment` — Filter by environment: `dev`, `staging`, `production`, or `working-copy`. * `hasError` — Set to `true` to return only events with errors, `false` to return only events without errors, or omit the parameter to return both. The `eventTypes` filter accepts up to four categories. The `eventNames` filter accepts up to 20 names. Unsupported event names are ignored rather than rejected and are reported in `meta.unsupportedEventNamesRequested`. Use `GET` when: * You are retrieving trace data without requiring encrypted request parameters. * You want to inspect or troubleshoot a specific event using `eventId`. * You want to investigate a particular trace using `traceId`. * You are analyzing events for one or more sessions using `sessionIds`. * You need to query and page through trace data using standard URL parameters. Do not use GET when the request parameters themselves must be encrypted. GET query strings are never encrypted. ## POST — List trace events with parameters in the request body Use this endpoint when you need the same trace data and filtering capabilities as the GET endpoint, but want to send the query parameters in the request body instead of the URL. The POST endpoint accepts the same parameters and returns the same response as GET. The POST endpoint is particularly useful when request parameters must be encrypted. When request encryption is enabled for the Platform Key, send the `TracesQueryBody` as an encrypted JWE. When request encryption is disabled, send the same parameters as a plain JSON object. Response encryption is independent of request encryption; when enabled, responses other than `401` are returned as encrypted JWE payloads. ### Filters and parameters The POST endpoint supports the same filtering parameters as GET: * `fromDate` — **Required.** Inclusive start timestamp. * `toDate` — **Required.** Exclusive end timestamp. * `eventTypes` — Filter by event category: `llm`, `tool`, `decision`, or `error`. * `eventNames` — Filter by specific supported event name. * `sessionIds` — Filter by up to 10,000 session IDs. * `eventId` — Retrieve a specific event. * `traceId` — Filter events belonging to a specific trace. * `agentName` — Filter by the exact agent name. * `decisionKind` — Filter agent decision events by decision type. * `channel` — Filter by the exact recorded channel name. * `environment` — Filter by environment. * `hasError` — Filter for events that have or do not have errors. * `dataMode` — Select `summary` or `full` event details. * `sortOrder` — Sort results in ascending or descending order. The POST body uses the GET parameter names as JSON property names. List parameters can be sent as JSON arrays or comma-separated strings. `traceDimensions` is not a parameter in this Traces API; do not use it with this endpoint. Use `POST` method when: * The request parameters must be encrypted. * You want to avoid exposing potentially sensitive filter values in the URL. * You need to send the query as a JSON object rather than URL query parameters. When request encryption is enabled, the plaintext query object is encrypted as a flattened JWE using the configured Platform Key. A plaintext POST body is rejected when request encryption is enabled, while an encrypted body is rejected when request encryption is disabled. Do not add query parameters to a POST request. All query parameters must be supplied in the request body, and the body must be a JSON object. # List trace events using JWE payload Source: https://koreai.mintlify.app/agent-platform/api-reference/analytics-list-traces-with-encrypted-payload agent-platform/api-specs/traces.yaml post /api/public/analytics/projects/{projectId}/traces POST form of `GET` on this path. It accepts the same parameters as a flat JSON body (`TracesQueryBody`) instead of a query string, and returns the same response. Use it when the request parameters must be encrypted. See "Sending parameters in a POST body" and "Payload encryption" in the API description. - Request encryption off: send `TracesQueryBody` as `application/json`. - Request encryption on: send the same object encrypted as an `EncryptedRequest`, preferably as `application/jose+json`. - Response encryption on: every response except `401` is an `EncryptedResponse` with `X-ABL-Encrypted: true`. Use this endpoint when you need the same trace data and filtering capabilities as [the corresponding GET endpoint](/agent-platform/api-reference/analytics-list-traces), but want to send the query parameters in the request body instead of the URL. The `POST` endpoint accepts the same parameters and returns the same response as `GET`. When request encryption is enabled for the Platform Key, send the `TracesQueryBody` as an encrypted JWE. When request encryption is disabled, send the same parameters as a plain JSON object. Response encryption is independent of request encryption; when enabled, responses other than `401` are returned as encrypted JWE payloads. The `POST` endpoint supports the same filtering parameters as `GET`. The `POST` body uses the `GET` parameter names as JSON property names. List parameters can be sent as JSON arrays or comma-separated strings. `traceDimensions` is not a parameter in this Traces API; do not use it with this endpoint. Use `POST` method when: * You must encrypt the request parameters. * You want to avoid exposing potentially sensitive filter values in the URL. * You need to send the query as a JSON object rather than URL query parameters. When request encryption is enabled, the plaintext query object is encrypted as a flattened JWE using the configured Platform Key. A plaintext `POST` body is rejected when request encryption is enabled, while an encrypted body is rejected when request encryption is disabled. Do not add query parameters to a `POST` request. All query parameters must be supplied in the request body. # List survey response Source: https://koreai.mintlify.app/agent-platform/api-reference/feedback-survey-list agent-platform/api-specs/public-feedback-survey.yaml get /api/public/feedback/projects/{projectId} List the project's survey responses Returns a page of responses with an opaque keyset cursor, with the oldest response first. The cursor is keyset (timestamp, response\_id) rather than an offset, so a client polling forward through history never skips a response or sees one twice as new rows arrive. No per-response score is returned: a score is a property of a population, not of one answer. Requires the `feedback-survey:read` permission. # Submit rating for survey Source: https://koreai.mintlify.app/agent-platform/api-reference/feedback-survey-submit-rating agent-platform/api-specs/public-feedback-survey.yaml post /api/public/feedback/projects/{projectId} Submit a rating against a named survey Records one rating for the survey named in the body. The rating is validated against the survey's control range (thumbs 0-1, star 1-5, scale 0-10). The rating is rejected rather than clamped when out of range, so a fabricated value can never reach the score. The response counts toward the survey's headline figure by default, scored by the survey's own method exactly as a runtime-captured response is. It requires the `feedback-survey:write` permission. # Events of outbound calls Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-events agent-platform/api-specs/outbound-calls.yaml get /api/v1/project/{projectSlug}/{env}/outbound-calls/{outboundCallId}/events Live status stream for one outbound call (server-sent events). Subscribes to the call's status as a `text/event-stream`. The first event is always a `snapshot`, carrying the call's current `phase`/`outcome` plus its full `statusHistory` to date. Every subsequent event is a `phase` event describing one applied transition. Every event carries a `version` — use it to detect and discard a `phase` event that describes the same transition already included in the initial `snapshot`. The stream stays open until the call reaches `phase=terminal` (one final event is still delivered) or the client disconnects. There is no guaranteed minimum interval between events; treat gaps as normal, not as evidence of a stall. # Look-up a call by ID Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-id agent-platform/api-specs/outbound-calls.yaml get /api/v1/project/{projectSlug}/{env}/outbound-calls/{outboundCallId} Look-up an outbound call by its ID. # List outbound calls Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-listing agent-platform/api-specs/outbound-calls.yaml get /api/v1/project/{projectSlug}/{env}/outbound-calls List outbound calls. Paginated list of outbound calls for the project/environment, most recent first. Destinations are always masked. Combine filters freely; all supplied filters apply together (AND). # Introduction to outbound dialing API Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-overview These APIs let an agent make voice calls to customers and manage incoming voice calls. The API supports a proactive customer-engagement requirement: * An external event or an app triggers a call. * The voice agent conducts the conversation for answered calls. * The calling app can monitor and act on the call. A few specific use cases are: * Remind user for an upcoming appointment. * Verify user-placed orders. * Send delivery notification or seek confirmation. * Call as a follow-up of a user's contact request. **Prerequisite** to use this API is an outbound-capable KoreVG [voice channel](/agent-platform/channels#voice-channels). Pre-configured channels manage the telephony operations, infrastructure, and provider details. It makes the API simple to use by focusing on outcomes such as answered, busy, not answered, or failed calls. After a call is answered, it is handled over to an Artemis agent as a normal agent conversation. It uses idempotency to prevent unnecessary repeat calls. # Place outbound calls Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-place agent-platform/api-specs/outbound-calls.yaml post /api/v1/project/{projectSlug}/{env}/outbound-calls Initiate an outbound call. Places an outbound call through the given channel connection. Returns immediately once the call has been accepted for submission — this does not wait for the call to ring or connect. Returns `201` on a genuinely new call. Repeating the exact same `idempotencyKey` and body returns `200` with the original call instead of placing a duplicate one. # Search outbound calls Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-search agent-platform/api-specs/outbound-calls.yaml post /api/v1/project/{projectSlug}/{env}/outbound-calls/search Search outbound calls by destination. Look up calls by the real (unmasked) destination number/SIP URI, without ever putting it in a URL. The destination is matched against a server-side blind index — the raw value in your request is never logged or echoed back; results still return the destination masked, like every other endpoint. The `destination` is optional — omit it to combine the other filters (phase, outcome, clientReferenceId, channelConnectionId, time window) as a general filtered search. # Summarize outbound calls Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-summary agent-platform/api-specs/outbound-calls.yaml get /api/v1/project/{projectSlug}/{env}/outbound-calls/summary Aggregate outbound-call KPIs for a time window. Rolled-up counts for the given filters and time window: total calls, how many wer answered, how many are still in flight, how many hit an answering machine, and a breakdown by terminal outcome. # Terminate outbound calls Source: https://koreai.mintlify.app/agent-platform/api-reference/outbound-calls-terminate agent-platform/api-specs/outbound-calls.yaml post /api/v1/project/{projectSlug}/{env}/outbound-calls/{outboundCallId}/terminate Terminate or hang up an in-flight outbound call. Hangs up a call that has not yet reached `phase=terminal`. Drives the call to `phase=terminal`, `outcome=canceled`. Don't use this API on a call that's already terminal. It returns the call's existing terminal state, not an error. # Artemis API overview Source: https://koreai.mintlify.app/agent-platform/api-reference/overview This document covers how to invoke and use the Artemis REST APIs for authentication, request format, error handling, pagination, and more. ## Prerequisites Before calling any API endpoint, ensure the following are in place: * An Artemis account with access to the relevant workspace. * A project created within that workspace. * Generated API credentials (Platform Key). * Confirmed scopes and permissions assigned to the credentials for the endpoints that you intend to use. ## Authentication A platform key is a long-lived bearer credential scoped to a tenant, a project, and a set of scopes. Its format is `abl_<48 hex characters>`, for example `abl_3f7a1c9e42b8dxxxxa1e8c7b4d29f0a3e5c8b1d7f2a4exxxx`. The headers are in the format `X-API-Key: `. ### Create a platform key Navigation: **Project → Settings → API Keys → Platform Keys → New Key**. | Field | Value | | - | - | | Key Name | Example: `acme-search-integration`. | | Scopes | Example: `Execute Chat`, `Execute Workflows`. | | Environments | Choose the environment with which the key is required. | | Expires | 30 days, 90 days, never, custom date. | Copy the generated key. It isn't displayed again. ## Scopes and permissions Scopes determine which resources and actions a given set of credentials can access. Requests made with credentials that lack the required scope for an endpoint fail during authorization, even if authentication succeeds. Every API request must include the API key in the X-API-Key header. Keys are tied to explicit scopes (for example, execute chat) assigned at creation. Note that access is scope-based, not origin-based. | Execution scope | Description | | :- | :- | | Execute Workflows | Execute workflows via Process API | | Invoke Draft Workflows | Invoke draft (unpublished) workflow versions via API key. | | Read Workflows | Read workflow definitions and status | | Execute Chat | Send messages to agents via Chat API | | Execute HTTP Async | Initialize HTTP Async sessions and enqueue asynchronous agent messages | | Read Attachments | Read session attachment metadata, processing status, and download URLs | | Upload Attachments | Upload session attachments and retry attachment processing | | Management scope | Description | | :- | :- | | Read Agents | List and inspect agent configurations. | | Write Agents | Create and update agent configurations. | | Read Channel Connections | Read project channel connection details for service integrations. | | Write Channel Connections | Create, update, and deactivate/delete project channel connections for service integrations. | | Read HTTP Async | Read HTTP Async webhook subscriptions and delivery status. | | Manage HTTP Async | Create, update, and deactivate HTTP Async webhook subscriptions. | | Read Deployments | List deployment status and history. | | Write Deployments | Create and promote deployments. | | Read Solution Workbench | Read published solution contracts, configuration, releases, and analytics. | | Release Solution Workbench Configurations | Validate and release approved solution configuration versions. | | Read Sessions | Read session history and transcripts | | Write Sessions | Attach external provider metadata to sessions (for example, post-transfer contact-center metadata) and trigger transcript sync | | Close Sessions | Close sessions with an explicit disposition | | Read Recordings | Read call recording metadata and media URLs | | Initiate Outbound Calls | Place on-demand outbound voice calls through a configured KoreVG channel | | Read Outbound Calls | Look up and list outbound call attempts and their retained logs | | Create Releases | Build immutable CI/CD releases | | Read Releases | Read CI/CD releases and status | | Download Release Bundle | Download the immutable release bundle (sensitive) | | Git Sync | Pull approved branch/tag/commit into a project | | Git Push | Push Studio changes to a feature/main branch | | Git Checkout | Switch or create the project source branch | | Git Force Override | Override loss-prevention on git sync (high privilege) | | Deploy Release | Deploy a release to an environment | | Promote Release | Promote a release across environments | | Rollback | Roll back an environment to a previous release | | Run Evals | Run eval suites as CI/CD gates | | Read Deployment Health | Read deployment/runtime health checks. | | Read Audit Evidence | Read CI/CD audit evidence packs. | | Manage CI/CD Webhooks | Manage deployment-event webhook subscriptions. | | Bypass Gate | Bypass qualification gate (high privilege, audited). | | Force Lock Release | Force-release a stale deployment lock (high privilege). | | Knowledge base scope | Description | | :- | :- | | Query Knowledge Base | Execute search queries against knowledge bases. | | Read Knowledge Base | Read knowledge base metadata and configurations. | | Ingest Documents | Upload files and ingest data into knowledge bases. | | Write Document Permissions | Set document-level ACL permissions during ingestion. | | Read Field Intelligence | Read Field Intelligence field summaries and details for project knowledge bases. | | Preview Field Intelligence | Preview Field Intelligence changes without saving those. | | Write Field Intelligence | Create and update Field Intelligence configuration. | | Refresh Field Intelligence | Request Field Intelligence projection refresh jobs. | | Import Field Intelligence | Import source-derived Field Intelligence configuration. | | Analytics scope | Description | | :- | :- | | Read Analytics | Read analytics dashboards, metrics, and saved widget data. | | Administration scope | Description | | :- | :- | | Read Workspace | Read workspace settings and usage data. | ## Base URL All API requests target your platform instance base URL of the format `https://example.com/api/public`. For URL of your instance, see the list of [region-specific platform URLs](/agent-platform/getting-started#region-specific-urls-and-allowlist-of-ips). ## Common request parameters | Element | Description | Example | | - | - | - | | X-API-Key header | Carries the platform key used to authenticate the request. | X-API-Key: `abl_xxxxx` | | Content-Type header | Specifies the payload format. | `application/json` | | Request body | Endpoint-specific payload. | See individual endpoint reference. | | projectSlug | Unique identifier for the project | Project > Settings > Advanced > slug. | | agentSlug | Unique identifier for the agent | Agent > Settings > slug | ## Pagination List endpoints use **offset-based pagination**: | Parameter | Type | Default | Description | | - | - | - | - | | limit | integer | 50 | Items per page (maximum 200) | | offset | integer | 0 | Items to skip | Example ```json theme={null} # Fetch page 1 GET /api/projects/proj_abc/sessions?limit=50&offset=0 # Fetch page 2 GET /api/projects/proj_abc/sessions?limit=50&offset=50 ``` Paginated responses include a pagination object. ```json theme={null} { "success": true, "sessions": [...], "pagination": { "total": 142, "limit": 50, "offset": 0 } } ``` ## HTTP error codes | Error code | HTTP code | Potential cause | Fix | | - | - | - | - | | `BAD_REQUEST` | 400 | Invalid or missing fields | Check request body schema | | `VALIDATION_ERROR` | 400 | Field value type mismatch | Review field types and ranges | | `UNAUTHORIZED` | 401 | Not authenticated | Verify key is valid and not expired | | `FORBIDDEN` | 403 | Insufficient permissions | Check credential scope | | `NOT_FOUND` | 404 | Resource missing or unavailable | Verify resource ID and access rights | | `RATE_LIMIT_EXCEEDED` | 429 | Request rate limit hit | Retry after `Retry-After` duration (seconds) | | `QUEUE_FULL` | 429 | Execution queue at capacity | Reduce concurrency, retry after `retryAfterMs` | | `INTERNAL_ERROR` | 500 | Unexpected server error | Retry after a brief delay | | `SERVICE_UNAVAILABLE` | 503 | Backend dependency offline | Retry later | # Get project import status Source: https://koreai.mintlify.app/agent-platform/api-reference/project-export-get agent-platform/api-specs/project-import-export.yaml get /v1/projects/{projectId}/export/{exportId} This endpoint gets the export status of a project. # Start project export Source: https://koreai.mintlify.app/agent-platform/api-reference/project-export-post agent-platform/api-specs/project-import-export.yaml post /v1/projects/{projectId}/export Export a project. This endpoint starts an asynchronous export. The server checks project readiness, discovers agent/tool dependencies, includes required dependency layers, assembles the archive, and generates its manifest and lockfile. # Get project import status Source: https://koreai.mintlify.app/agent-platform/api-reference/project-import-get agent-platform/api-specs/project-import-export.yaml get /v1/projects/{projectId}/import/{importId} This endpoint gets the import status of a project. # Start project import Source: https://koreai.mintlify.app/agent-platform/api-reference/project-import-post agent-platform/api-specs/project-import-export.yaml post /v1/projects/{projectId}/import Import a project. This endpoint starts an asynchronous import. The server runs the Builder-equivalent preview and dependency validation before applying any mutation. Blocking validation errors prevent staging and activation. # Get all recordings for a session Source: https://koreai.mintlify.app/agent-platform/api-reference/recording-fetch-calls-per-session agent-platform/api-specs/call-recordings.yaml get /api/public/recordings/projects/{projectId}/sessions/{sessionId}/recordings Fetch a session's call recordings The Call Recording API provides read-only access to server-side call recordings associated with a conversation session. It returns recording metadata and media URLs for available audio files, supporting automated workflows such as compliance archiving, quality assurance, and analytics. A session can produce multiple recordings, such as when a call is transferred. Each recording can contain multiple files, such as separate caller and agent channels. The API does not return transcripts or other conversation text. Authenticate using a project-bound platform API key (`abl_...`) with the `recordings.read` scope. Send the key in either the `x-api-key` header or the `Authorization: Bearer` header. Studio user JWTs, JWT App assertions, and public `pk_` keys are not accepted. ## Retrieve session recordings Use this endpoint to retrieve all recordings produced by a specific session. The response includes recording metadata and a list of files for each recording. Each file includes its channel, file name, size, recording timestamps, availability, and media URL details. The endpoint requires the project ID and session ID. The optional `disposition` query parameter controls how the media is delivered: * `attachment` (default): downloads the media file. * `inline`: allows the media to play in a browser. ## Response status and availability An HTTP `200` response can include recordings or explain why recording media is not yet available. Check `data.status`, `data.retryable`, and the availability fields on individual recordings and files. | `data.status` | Meaning | | - | - | | `success` | Every file has a media URL. | | `partial` | At least one file has a URL and at least one does not. | | `call_in_progress` | No file has a URL, and the call is still in progress. | | `media_in_progress` | No file has a URL, and media is still being fetched or stored. | | `media_unavailable` | No file is available, and no media is pending. | When `data.recordings` is empty, use `data.unavailableReason` and `data.retryable` to understand why no recordings are available. A file without a URL includes an `unavailableReason` and a `retryable` flag. Retry with backoff while the relevant `retryable` value is `true`. Stop retrying when it is `false`. ## Media URLs Use `urlKind` to determine how to retrieve each file: * `signed`: Send a `GET` request directly to the absolute URL without an authentication header. * `proxied`: Prefix the returned relative URL with the API host and send a `GET` request to it without an authentication header. Use the URL as returned; do not construct it manually. * `external`: Send a `GET` request directly to the customer-owned absolute URL. Media URLs are short-lived where the platform controls their expiry. The default lifetime is 900 seconds, with a maximum of 3600 seconds. The `expiresIn` field indicates the remaining lifetime in seconds when applicable; it is `null` for external URLs. Treat every media URL as a bearer secret. Anyone with access to the URL may be able to retrieve the recording. ## Errors and limits * `400`: Invalid session ID or invalid `disposition`. * `401`: Missing, invalid, expired, or revoked credentials. * `403`: Missing permission, unsupported credential type, or call recording not enabled for the tenant. * `404`: Project or session not found within the API key's authorized scope. * `413`: Recording or stored-file limit exceeded. * `429`: Tenant request rate limit exceeded. * `500`: Unexpected failure. * `503`: Project recording settings could not be resolved; retry later. The API allows up to 64 recordings per session and, by default, up to 32 stored files across those recordings. Tenant-level request rate limits also apply. ## Audit and data handling When at least one media URL is issued, the API records a `recording:public_media_urls_issued` audit event attributed to the calling API key. Responses include `Cache-Control: no-store`, `Pragma: no-cache`, and `Referrer-Policy: no-referrer`. # Stream a recording Source: https://koreai.mintlify.app/agent-platform/api-reference/recording-stream agent-platform/api-specs/call-recordings.yaml get /api/projects/{projectId}/recordings/{recordingId}/media/content/{mediaToken} Stream a recording. The Call Recording API provides read-only access to server-side call recordings associated with a conversation session. It returns recording metadata and media URLs for available audio files, supporting automated workflows such as compliance archiving, quality assurance, and analytics. A session can produce multiple recordings, such as when a call is transferred. Each recording can contain multiple files, such as separate caller and agent channels. The API does not return transcripts or other conversation text. Authenticate when retrieving session recordings using a project-bound platform API key (`abl_...`) with the `recordings.read` scope. Send the key in either the `x-api-key` header or the `Authorization: Bearer` header. Studio user JWTs, JWT App assertions, and public `pk_` keys are not accepted. ## Stream recording media Use this endpoint to stream a recording when the session recordings endpoint returns a file with `urlKind: proxied`. Prefix the returned relative URL with the API host and send a `GET` request to the complete URL. Don't construct the URL on your own. The `mediaToken` embedded in the URL is the credential for this request. **Do not send an `x-api-key` or `Authorization` header.** The token is short-lived and bound to the tenant, project, recording, file index, disposition, and issuing API key. This endpoint supports HTTP range requests for seeking or retrieving part of a file. Send a `Range` header, such as `Range: bytes=0-65535`, to request a byte range. ## Response behavior * `200 OK`: Returns the full media file. * `206 Partial Content`: Returns the requested byte range. * `302 Found`: The file is hosted at an external URL; follow the redirect. ## Errors * `400`: Invalid file index or media disposition. * `401`: The media token is invalid, expired, or does not match the request. * `404`: The recording or requested file is not found, or the stored media object is missing. * `409`: The recording is not yet complete (`RECORDING_NOT_READY`). * `416`: The requested byte range cannot be satisfied. * `504`: Streaming did not finish within five minutes. # Execute a specific tool Source: https://koreai.mintlify.app/agent-platform/api-reference/tools-execute-table-tools-execute agent-platform/api-specs/tool-execution.yaml post /api/v1/project/{projectSlug}/tools/{toolName}/invoke Executes a tool in a project and returns its result. The request supplies the tool's caller-defined input parameters in a JSON body. Execution is synchronous: the request waits for the tool to complete or reach its timeout. Use this endpoint to run an executable tool after identifying it and retrieving its input schema. The tool's output is returned without being reshaped by the platform, except that an Agent Table tool's output is returned as a JSON object. **Permission:** `tool:execute`, granted by the platform API key scope `tools.execute`. ## Path parameters | Parameter | Type | Required | Description | | - | - | - | - | | `projectSlug` | string | Yes | Project slug, not the project ID or UUID. | | `toolName` | string | Yes | Tool name as authored. For MCP tools, use the `alias__toolName` format. | ## Request body Send a JSON object with the following properties: | Property | Type | Required | Description | | - | - | - | - | | `params` | object | Yes | Tool input matching the `input` schema returned by the Describe a tool endpoint. | | `timeout` | integer | No | Per-call timeout in milliseconds. Minimum: `1000`; maximum: `300000`. | | `mode` | string | No | Execution mode. Omit this property or set it to `sync`. Other values return `400 ASYNC_NOT_SUPPORTED`. | The request body is strict. Unknown top-level properties and platform-derived fields are rejected rather than ignored. Platform-derived fields include values such as `tenantId`, `environment`, and `agentSession`. For Agent Table tools without a query template, `params` contains a structured call with `op` and the operands for that operation. The accepted operations depend on the tool's binding. ## Authentication Use one of the supported credentials: * `x-api-key: abl_...` — platform API key. * `Authorization: Bearer ` — platform API key or Studio user JWT. * `auth: ` — JWT App assertion. Public SDK keys (`pk_...`) are not accepted. The JWT App assertion must be sent in the `auth` header, not the `Authorization` header. The credential must have the `tool:execute` permission. A sensitive-column lookup also requires `table:sensitive:reveal`. ## Response A successful response contains `success` and `data`. The `data` object includes the execution `status`, the tool's `output` when provided, and `trace` information when the caller is entitled to it. The response headers identify the tool type, resolved environment, and configuration source: | Header | Description | | - | - | | `X-ABL-Tool-Type` | Type of tool that ran. | | `X-ABL-Environment` | Environment against which the tool definition was resolved. Always `draft` on this API surface. | | `X-ABL-Config-Source` | Indicates whether the configuration came from a pinned version or the working copy. | For Agent Table tools, `output` is a JSON object rather than a serialized JSON string. For other tool types, the output is returned without platform-level reshaping. ## Execution considerations * Only tool types enabled for public API execution can be invoked. Agent Table tools are enabled by default; other supported types require explicit enablement. * The `sandbox` tool type is not executable through this API. * A tool's declared effect can indicate whether it is read-only, writes state, or has side effects. Review this metadata before invoking the tool. * Tool failures are returned using status codes appropriate to the failure. Do not assume every execution failure is a transient server error or safe to retry. ## Errors | HTTP status | Description | | - | - | | `400` | Malformed or invalid request, unknown key, unsupported platform field, unsupported execution mode, or forbidden operation. | | `401` | Missing or invalid credentials, or an unsupported public SDK key. | | `403` | Missing execution permission, insufficient project scope, or a sensitive-column lookup without the required permission. | | `404` | Project or tool not found, tool type disabled, tool not executable, or public execution disabled. Some of these cases intentionally return the same response. | | `409` | Conflict requiring caller action, such as a unique-constraint violation, row-version conflict, or unsupported authentication-profile mode. | | `413` | Request payload exceeds the allowed size or structural limits. | | `415` | Unsupported media type or charset. A JWT App configured to require payload encryption must use the required encrypted format. | | `422` | Tool configuration or execution failure, invalid tool response, or SOAP fault. | | `429` | Tenant request or tool-call rate limit exceeded. | | `500` | Unexpected execution failure. | | `502` | Tool network, HTTP, authentication, or upstream failure. | | `503` | A dependency is unavailable, such as an MCP server or Agent Table service. | | `504` | The tool did not respond within its timeout. | The `429` response uses a rate-limiter-specific response format rather than the standard API error envelope. Check the response headers for rate-limit information and retry guidance. For transient dependency failures (`503`), retry with backoff. Resolve conflicts such as `409` before retrying. Do not automatically retry operations with side effects unless their retry behavior is known to be safe. # List the available project tools Source: https://koreai.mintlify.app/agent-platform/api-reference/tools-execute-table-tools-list agent-platform/api-specs/tool-execution.yaml get /api/v1/project/{projectSlug}/tools Lists the tools available for execution in a project. Each result provides a tool summary, including its name, type, description, declared effect, and execution availability. The endpoint returns only tools whose types are enabled for public API execution. Use this endpoint to discover available tools before retrieving a tool's input schema or invoking it. Results are paginated using a cursor based on the tool name. **Permission:** `tool:read`, granted by the platform API key scope `tools.read`. ## Path parameters | Parameter | Type | Required | Description | | - | - | - | - | | `projectSlug` | string | Yes | Project slug, not the project ID or UUID. | ## Query parameters | Parameter | Type | Required | Description | | - | - | - | - | | `limit` | integer | No | Number of tools to return per page. Defaults to `50`, with a minimum of `1` and a maximum of `200`. The value is clamped to the configured maximum. | | `cursor` | string | No | Opaque cursor returned in `nextCursor` by the previous response. Omit it to retrieve the first page. | | `type` | array of strings | No | Restricts results to specified tool types. Repeat the parameter to specify multiple types. | The `table` tool is supported. The API rejects the other tool types. ## Authentication Use one of the supported credentials: * `x-api-key: abl_...` — platform API key. * `Authorization: Bearer ` — platform API key or Studio user JWT. * `auth: ` — JWT App assertion. Public SDK keys (`pk_...`) are not accepted. The JWT App assertion must be sent in the `auth` header, not the `Authorization` header. ## Response A successful response contains `success` and `data`. The `data` object includes: | Property | Description | | - | - | | `environment` | Environment against which tool definitions were resolved. Always `draft` on this API surface. | | `tools` | Array of tool summaries. | | `nextCursor` | Cursor for the next page, or `null` when no more results are available. | Each tool summary can include `name`, `type`, `description`, `effect`, `requiresAuthProfile`, `operations`, `executable`, and `unsupportedReason`, depending on the tool. The `executable` property indicates whether a tool can be invoked through this API. The `unsupportedReason` property, when present, explains why it cannot be executed. To retrieve the input schema for a tool, use the **Describe a tool** endpoint. ## Errors | HTTP status | Description | | - | - | | `400` | Invalid request, including an invalid tool type filter. | | `401` | Missing or invalid credentials, or an unsupported public SDK key. | | `403` | The credential lacks the required permission or project access. | | `404` | Project or resource not found. | | `429` | Tenant request or tool-call rate limit exceeded. | The `429` response uses a rate-limiter-specific response format rather than the standard API error envelope. Check the response headers for rate-limit information and retry guidance. # Describe a tool and its input schema Source: https://koreai.mintlify.app/agent-platform/api-reference/tools-execute-table-tools-retrieve agent-platform/api-specs/tool-execution.yaml get /api/v1/project/{projectSlug}/tools/{toolName} This API retrieves a tool's summary and its input schema as JSON Schema. The schema describes the parameters that callers may supply when invoking the tool. Use this endpoint after discovering a tool to determine its accepted inputs and construct a valid invocation request. Parameters resolved by the platform at runtime, such as configuration values, authentication-profile values, expression results, lookup results, and other server-side values, are omitted from the published schema. Permission requirement `tool:read`, granted by the platform API key scope `tools.read`. ## Path parameters | Parameter | Type | Required | Description | | - | - | - | - | | `projectSlug` | string | Yes | Project slug, not the project ID or UUID. | | `toolName` | string | Yes | Tool name as authored. For MCP tools, use the `alias__toolName` format. | This endpoint does not define query parameters or a request body. ## Authentication Use one of the supported credentials: * `x-api-key: abl_...` — platform API key. * `Authorization: Bearer ` — platform API key or Studio user JWT. * `auth: ` — JWT App assertion. Public SDK keys (`pk_...`) are not accepted. The JWT App assertion must be sent in the `auth` header, not the `Authorization` header. ## Response A successful response contains `success` and `data`. The `data` object includes the tool's summary properties and an `input` property containing the JSON Schema for caller-supplied parameters. The response can include the following properties: | Property | Description | | - | - | | `name` | Tool name. | | `type` | Tool type. | | `description` | Tool description, when defined. | | `effect` | Declared effect of the tool, when defined. | | `requiresAuthProfile` | Indicates whether the tool resolves an authentication profile server-side. | | `operations` | Operations allowed by the binding for an Agent Table tool. | | `executable` | Indicates whether the tool can be invoked through this API. | | `unsupportedReason` | Reason the tool cannot be executed, when applicable. | | `input` | JSON Schema describing the parameters the caller may supply. | ## Input schema behavior The `input` schema includes only caller-suppliable parameters. Parameters resolved by the platform are omitted rather than published as caller inputs. For an Agent Table tool without a query template, the schema describes the structured call accepted by the executor. This includes `op` and the operands applicable to the selected operation. Only `op` is unconditionally required; the applicability of other operands depends on the selected operation. A tool whose definition cannot be parsed can still be returned. In that case, `executable` is `false`, and `unsupportedReason` is `TOOL_DEFINITION_INVALID`. Use the returned schema to construct the `params` object for the **Execute a tool** endpoint. ## Errors | HTTP status | Description | | - | - | | `401` | Missing or invalid credentials, or an unsupported public SDK key. | | `403` | The credential lacks the required permission or project access. | | `404` | Project or tool not found, or the resource is unavailable. | | `429` | Tenant request or tool-call rate limit exceeded. | The `429` response uses a rate-limiter-specific response format rather than the standard API error envelope. Check the response headers for rate-limit information and retry guidance. # Arch AI Source: https://koreai.mintlify.app/agent-platform/arch-ai Arch™ AI is a multi-agent system that drives every phase of the agent lifecycle — from initial design to continuous production optimization. It is not a copilot or a suggestion engine. Arch AI interviews stakeholders, proposes agent topology, generates ABL™, runs tests, monitors production traces, and surfaces improvement proposals — each phase end-to-end, with human review at the decision points. Arch connects directly to the ABL compiler and the platform runtime. It exchanges compiled IR, traces, and topology data through MCP, so it operates on the same artifacts the platform executes. ## Lifecycle phases Arch operates across six phases. Each phase is driven by a specialized sub-agent. There is no separate manual step to author ABL, run tests, or promote between environments. Agent Lifecycle with Arch Agent Lifecycle with Arch | Phase | What Arch Does | | - | - | | **Design** | Interprets user intent and generates a complete agent blueprint — goals, flows, tools, memory, and guardrails — ready to compile. | | **Build** | Authors ABL, validates cross-references, resolves tool bindings, and confirms deployment readiness before a human reviews. | | **Test** | Runs Persona × Scenario × Evaluator matrices autonomously, surfacing failures, edge cases, and policy violations without manual test authoring. | | **Deploy** | Manages environment promotion, versioning, and rollout. Compiler validation ensures no broken agent reaches production. | | **Observe** | Monitors trace events, session health, and runtime anomalies in real time, surfacing what matters rather than raw logs. | | **Optimize** | Continuously analyzes performance, cost, and quality signals. Proposes ABL patches as pull requests. Engineers review and approve every change. | ## The authoring flow The authoring flow runs in stages, from requirements to a deployed, diffable agent artifact. Human review happens at two points: after Blueprint and before Optimize applies changes. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A[Interview] --> B[Blueprint] B --> C{Human Review} C -- Accept --> D[Build] C -- Request Changes --> B ```
```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR D[Build] --> E[Test] E --> F[Deploy] F --> G[Observe] G --> H[Optimize] H --> D ``` *** ### Design and Blueprint Arch begins by capturing requirements through a structured conversation — accepting a natural-language brief, an SOP document, or a prompt. Stakeholders are the source of truth. Arch produces structured requirements and then proposes a full agent topology: which specialists to create, how handoffs are routed, which tools each agent binds to, and what guardrails apply. You can review and refine the blueprint before any ABL is generated. *Blueprint artifact with agent details:* Blueprint artifact with agent details *Agent topology proposed by Arch:* Agent topology proposed by Arch ### Build The build sub-agent generates the ABL artifact from the approved blueprint. The ABL compiler validates contracts and references on every generation step. Tests are auto-generated. The output is a deployable, diffable, code-reviewable agent — not a sketch or a configuration dialog. *ABL definitions generated by Arch:* ABL definitions generated by Arch ### Observe and Optimize After deployment, Arch's observability sub-agent reads typed trace events, surfaces session health metrics, and flags anomalies — across every agent in the project. When it detects a degradation, a gap in coverage, or a routing inefficiency, it proposes ABL changes as pull requests. The compiler validates the shape of each proposal before engineers review the diff. The optimization loop closes itself: production signal feeds back into ABL improvements, which compile, deploy, and generate new signal. See [Optimize with Arch](/agent-platform/optimize). *Project health monitoring and continuous improvement by Arch:* Arch project health and continuous improvement ## Arch as a multi-agent system Arch is itself built on the same multi-agent architecture it creates. Each specialist sub-agent has a typed role and scoped responsibility. | Specialist | Role | | - | - | | **Multi-agent architect** | Designs agent topology and handoff routing. | | **ABL construct expert** | Writes valid ABL grammar, types, and contracts. | | **Channel and voice expert** | Handles channel-specific authoring requirements for voice, web, and messaging. | | **Integration methodologist** | Designs tool connectors and data source bindings. | | **Testing and eval expert** | Generates regression suites and evaluation matrices. | | **Diagnostician and analyst** | Monitors production traces and drives optimization proposals. | Each specialist operates with the same discipline applied to every customer agent: typed inputs, scoped memory, explicit handoffs, and compiler-validated outputs. ## Accessing Arch Arch is available from three surfaces. All three use the same MCP interface and produce the same compiled output. | Surface | How It Works | | - | - | | **Agent Studio** | The built-in Arch assistant in Studio. Available throughout the authoring, test, and deployment workflow. Accessible from the sidebar of any agent project. | | **External coding tools** | Claude Code, Cursor, and Codex connect to Arch through MCP. Agents can be designed and iterated from a CLI or IDE without opening Studio. | | **Platform runtime** | Arch reads live trace data from the runtime directly. The observability and optimize phases do not require a separate session — Arch works continuously in the background. | # Channels Source: https://koreai.mintlify.app/agent-platform/channels Channels connect your deployed agents to the surfaces where users interact: your own apps, voice calls, messaging apps, webhooks, and other agent frameworks. Artemis provides channel adapters that handle the protocol-specific details, so a single agent definition works across every channel without modification. A channel always binds to a deployment in a specific environment. Configure deployments first, then add channels on top. Navigation: **Project** > **Deployments** > **Channels** | Channels Category | Used by | How It Works | | - | - | - | | [Messaging or Digital](#messaging-or-digital-channels) | Slack, WhatsApp, Teams, and other messaging apps | You register the external provider's credentials. The platform returns a webhook URL that the provider calls when a user sends a message. | | [Voice](#voice-channels) | Realtime LLM Voice, Pipeline Voice, VXML IVR, AudioCodes | A channel connection registers the voice provider's credentials. The platform returns a SIP endpoint or webhook the provider calls to route audio. | | [SDK](#sdk-channels) | Web SDK, API, Mobile SDK | A public API key (`pk_`) or REST API call bootstraps a session, either embedded in your app or called server-to-server. | | [Webhook](#webhook-channel) | Webhooks (HTTP Async) | Agent responses are pushed to a webhook URL on your own server. | | [Protocols](#protocols) | AG-UI (CopilotKit), Agent-to-Agent (A2A) | The agent speaks an open protocol directly, so it plugs into another framework or frontend without a custom adapter. | All five route to the same agent definition. The adapter normalizes inbound and outbound formats, so your agent doesn't need channel-specific logic for basic conversations. *** ## Messaging or digital channels The Platform supports the following messaging or digital channels: | Channel | Description | | - | - | | [Slack](/agent-platform/channels/slack) | Deploy agents as Slack bots for team collaboration | | [LINE](/agent-platform/channels/line) | Connect agents to LINE Official Accounts for chat and media messaging | | [Microsoft Teams](/agent-platform/channels/ms-teams-copilot) | Integrate agents into Teams channels and chats | | [WhatsApp](/agent-platform/channels/whatsapp) | Connect agents to WhatsApp Business for customer messaging | | [Messenger](/agent-platform/channels/messenger) | Connect agents to Facebook Messenger for customer support | | [Twilio SMS](/agent-platform/channels/twilio) | Send and receive SMS messages via Twilio for customer engagement | | [Telegram](/agent-platform/channels/telegram) | Connect agents to Telegram bots for messaging and group conversations | | [Zendesk](/agent-platform/channels/zendesk) | Connect agents to Zendesk Sunshine Conversations for customer support | | [Instagram](/agent-platform/channels/instagram) | Connect agents to Instagram Messaging for direct customer conversations | | [Genesys](/agent-platform/channels/genesys) | Connect agents to Genesys Cloud as a Bot Connector for contact center automation | | [Genesys Open Messaging](/agent-platform/channels/genesys-open-messaging) | Connect Genesys Cloud Open Messaging conversations through a project-scoped webhook adapter | | Email | Process inbound emails and send agent responses via email | | [AI for Work](/agent-platform/channels/ai-for-work) | Kore.ai's Employee Experience (EX) platform — bidirectional agent messaging integration | | [Kore Agent Assist](/agent-platform/channels/kore-agent-assist) | Kore.ai Agent Assist — real-time agent guidance. The platform generates the API key; the external endpoint contract is unchanged. | Each setup page covers credential registration, webhook configuration, channel-specific features (such as Slack threading and slash commands, or WhatsApp interactive messages), and troubleshooting. *** ## Voice channels The Platform supports the following voice channels: | Channel | Description | | - | - | | [Realtime LLM Voice](/agent-platform/channels/voice#set-up-realtime-llm-voice-channel) | Live voice conversations using realtime LLM models (OpenAI Realtime, Gemini Live) via Kore.ai Voice Gateway | | [Pipeline Voice](/agent-platform/channels/voice#set-up-pipeline-voice-channel) | Traditional STT → LLM → TTS voice pipeline via Kore.ai Voice Gateway, with configurable ASR/TTS models | | VXML IVR | VoiceXML 2.1 gateway for traditional IVR systems, with DTMF and prompt-based navigation | | [Genesys Audio Connector](/agent-platform/channels/genesys-audio-connector) | Configure Genesys Audio Connector for KoreVG-owned AudioHook execution | | AudioCodes | Connect voice calls via AudioCodes VoiceAI Connect | Each voice setup page covers caller identity fields (`session.anonymousId` and related), voice-specific `VOICE:` response blocks, SSML control, and troubleshooting. *** ## SDK channels The Platform supports the following SDK channels to embed an agent in your own application or call it server-to-server: | Channel | Description | | - | - | | [Web SDK](/agent-platform/channels/web-sdk-channel) | Embed a chat widget in your website with a single script tag | | API | Programmatic access via REST API for server-to-server integration | | Mobile SDK | Native iOS and Android SDKs for in-app agent experiences | *** ## Webhook channel The Platform supports the following webhook channel to receive asynchronous events: | Channel | Description | | - | - | | [Webhooks (HTTP Async)](/agent-platform/channels/webhook) | Receive agent responses via webhook callbacks to your server | *** ## Protocols The Platform supports the following open protocols to connect agents with other agent frameworks and frontends: | Protocol | Description | | - | - | | [AG-UI (CopilotKit)](/agent-platform/channels/ag-ui-copilotkit) | Server-sent events protocol for React/Next.js frontend agent UIs, with streaming support | | [Agent-to-Agent (A2A)](/agent-platform/channels/agent-to-agent) | Google A2A protocol for inter-agent communication with task lifecycle management | *** **Related articles:** * [Deploy Agents](/agent-platform/deployment) # Set up AG-UI (CopilotKit) Source: https://koreai.mintlify.app/agent-platform/channels/ag-ui-copilotkit [Back to protocols](/agent-platform/channels#protocols) Supports bi-directional protocol connections for React frontend agent UIs built with CopilotKit. To configure an AG-UI (CopilotKit) channel, go to **Deployments** > **Channels** > **Protocols** > **AG-UI (CopilotKit)** > **+ New** and complete the following steps in order. On the **New AG-UI (CopilotKit) Connection** page, provide the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection. Up to 255 characters. | | **Frontend App Name** | The name of the frontend application connecting to this channel, for example `copilot-dashboard`. Up to 255 characters. | | **Environment** | The deployment the connection binds to. Select an environment to follow its active deployment automatically, or leave the default to use the working copy. | | **Provider Verification Strength** | Controls how strictly the platform trusts the provider's identity assertions for session continuity and linking. **Weak** is the default. Choose **Strong** only for channels/providers whose identity assertions you trust for stronger continuity and linking decisions. | | **Initial Status** | **Active** connections can be used immediately. **Inactive** connections stay saved but disabled until you activate them. | Click **Create**. ### Manage an existing AG-UI (CopilotKit) channel Open a channel from **Deployments** > **Channels** > **AG-UI (CopilotKit)** shows three tabs: **Overview**, **Configuration**, and **Deployment**. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | for example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | for example: `dev` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique channel identifier (UUID) | **Setup Instructions** shows the **Frontend App Name** configured for this connection, with a copy icon to copy it into your integration code. **General** | Field | Description | | - | - | | **Display Name** | The connection's label. Editable at any time. | | **Provider Verification Strength** | Same setting described in [Set up AG-UI (CopilotKit)](#set-up-ag-ui-copilotkit) above. | **AG-UI (CopilotKit) Settings** Protocol channels are configured through the SDK integration — no additional settings are needed here. Click **Save Changes** to apply changes. The **Deployment** tab shows these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown (for example: `Development`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and when it was deployed. | Click **Save Changes** after switching the environment/version. # Set up Agent-to-Agent (A2A) Source: https://koreai.mintlify.app/agent-platform/channels/agent-to-agent [Back to protocols](/agent-platform/channels#protocols) Supports the Google A2A protocol for inter-agent communication with task lifecycle management. To configure an Agent-to-Agent (A2A) channel, go to **Deployments** > **Channels** > **Agent-to-Agent (A2A)** > **+ New** and complete the following steps in order. On the **New Agent-to-Agent (A2A) Connection** page, provide the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection. Up to 255 characters. | | **Agent Endpoint Name** | A system name for the A2A endpoint, for example `booking-agent-a2a`. Up to 255 characters. | | **Environment** | The deployment the connection binds to. Select an environment to follow its active deployment automatically, or leave the default to use the working copy. | | **Provider Verification Strength** | Controls how strictly the platform trusts the provider's identity assertions for session continuity and linking. **Weak** is the default. Choose **Strong** only for channels/providers whose identity assertions you trust for stronger continuity and linking decisions. | | **Initial Status** | **Active** connections can be used immediately. **Inactive** connections stay saved but disabled until you activate them. | Click **Create**. ### Manage an existing Agent-to-Agent (A2A) channel Open a channel from **Deployments** > **Channels** > **Agent-to-Agent (A2A)** shows three tabs: **Overview**, **Configuration**, and **Deployment**. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | for example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | for example: `dev` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique channel identifier (UUID) | **Setup Instructions** shows the **Agent Endpoint Name** configured for this connection, with a copy icon to copy it into your integration code. **General** | Field | Description | | - | - | | **Display Name** | The connection's label. Editable at any time. | | **Provider Verification Strength** | Same setting described in [Set up Agent-to-Agent (A2A)](#set-up-agent-to-agent-a2a) above. | **Agent-to-Agent (A2A) Settings** Protocol channels are configured through the SDK integration — no additional settings are needed here. Click **Save Changes** to apply changes. The **Deployment** tab shows these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown (for example: `Development`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and when it was deployed. | Click **Save Changes** after switching the environment/version. # Set up AI for Work Source: https://koreai.mintlify.app/agent-platform/channels/ai-for-work [Back to channels](/agent-platform/channels) Connect your agent to Kore.ai AI for Work, the Employee Experience (EX) platform, as a bidirectional messaging integration. Employees reach the agent through a published Autonomous Agent in AI for Work, and AI for Work reaches the agent through an endpoint URL and connection secret that you generate in Artemis. Artemis labels this channel **AIforWork**. The product it connects to is documented as AI for Work. This article uses **AIforWork** for the on-screen label and AI for Work for the product. ## Before you begin You need administrator access to the AI for Work Admin Console and permission to create and publish Autonomous Agents in the target workspace. You also need a project in Artemis with at least one deployed version in the environment you plan to bind the connection to. Create the connection in Artemis first. The endpoint URL and connection secret it generates are the inputs to the AI for Work side of the setup. ## Create the channel connection in Artemis Open Artemis, select the required project, and go to **Deployments** -> **Channels** -> **AIforWork** -> **+ New**. On the **New AIforWork Connection** dialog, provide the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection, for example `AIforWork Production`. Up to 255 characters. | | **Environment** | Select an environment to follow its active deployment. If no environment has an active deployment yet, leave the default and bind the connection later from the **Deployment** tab. | | **Provider Verification Strength** | **Weak (default)** or **Strong**. Choose **Strong** only for channels or providers whose identity assertions you trust for stronger continuity and linking decisions. | | **Initial Status** | **Active** connections can be used immediately. **Inactive** connections stay saved but disabled until you activate them. Set this to **Active**. | Select **Create**. The **AIforWork Connection Created** dialog shows the **Endpoint URL** and the **Connection Secret**. Copy both values and store the secret securely, then select **Done**. The connection secret appears only once and can't be retrieved afterward. If you lose it, open the connection and select **Rotate** on the **Overview** tab to issue a new one. Rotating the secret invalidates the previous value, so update the AI for Work configuration with the new secret. AI for Work uses the endpoint URL and secret to sign its requests with HMAC. ## Link the connection in AI for Work 1. Open the AI for Work Admin Console, switch to the target workspace, and go to **AI Agents** -> **Autonomous Agents**. 2. Select **Create agent**, then create the agent from scratch. 3. Enter the agent name and the purpose of the app. AI for Work uses the purpose for query routing and training. 1. In **Configure agentic app**, select **Add using credentials**. 2. On the **Link using credentials** dialog, enter the **Endpoint URL** and the **Connection Secret** you copied from Artemis. 3. Select **Run and Save**. Select **Browse and Link Projects** on the same dialog to pick the app directly from an Agent Platform workspace instead of entering credentials. Use the credentials method when the agent is in a workspace you can't browse from AI for Work, or when you want the connection pinned to a specific Artemis channel connection. Select **Async** as the mode and test the connection. A successful test reports that the async connection succeeded. Async mode suits agents that take longer than the 60-second sync timeout. When you use async mode, set the **Callback Base URL** on the Artemis connection's **Configuration** tab so the agent can return responses and proactive notifications to AI for Work. Select **Continue**, then select **Publish**. Choose whether to publish to everyone in the account or to limited users. For the full set of publishing options, see the [AI for Work custom agents documentation](https://docs.kore.ai/ai-for-work/custom-agents#publishing-your-agent). ## Key values reference | Value | Where it comes from | Where it's used | | - | - | - | | **Endpoint URL** | The **AIforWork Connection Created** dialog in Artemis, or the connection's **Overview** tab | The **Endpoint URL** field on the AI for Work **Link using credentials** dialog | | **Connection Secret** | The **AIforWork Connection Created** dialog in Artemis; shown only at creation time | The **Connection Secret** field on the AI for Work **Link using credentials** dialog | | **Callback Base URL** | Your AI for Work instance | The **Callback Base URL** field on the Artemis connection's **Configuration** tab | ## Pass custom metadata AI for Work can pass custom metadata to the connected Artemis app. In **Configure agentic app**, open **\ Meta Data** and add the metadata you want to send. Inside Artemis, the expression that reads the metadata differs by surface. The following examples read an `email` key. | Surface | Expression | | - | - | | Agent | `{{_metadata.email}}` | | Workflow tool | `{{vars._session._metadata.email}}` | | HTTP tool | `{{session._metadata.email}}` | ## Manage the channel connection Opening a connection from **Deployments** -> **Channels** -> **AIforWork** shows three tabs: **Overview**, **Configuration**, and **Deployment**. The header shows the connection name, the bound environment, the status, and the channel type, along with **Pause** and delete actions. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | For example: *Active* | | **Created** | Timestamp the connection was created | | **Updated** | Timestamp of the last change | | **Environment** | For example: `dev` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique connection identifier (UUID) | Under **Setup Instructions**, the tab shows the **Endpoint URL** to enter in AI for Work and the masked **Connection Secret**. Select **Rotate** to issue a new secret. The full secret is shown only at creation time and can't be retrieved. Under **General**, configure the following. | Field | Description | | - | - | | **Display Name** | The label for the connection. | | **Provider Verification Strength** | **Weak (default)** or **Strong**. Choose **Strong** only for channels or providers whose identity assertions you trust for stronger continuity and linking decisions. | Under **AIforWork Settings**, configure the following. | Field | Description | | - | - | | **Callback Base URL** | The base URL where ABL sends async responses and proactive notifications. Required when AI for Work uses the async response mode. | #### Authorization message The **Authorization Message** panel controls the message shown when a tool needs the user to authorize. Edit the **Message template** directly, or use the **Insert** buttons to add the most common variables at the cursor. The default template asks the user to authorize the pending verifications and then lists each one as a numbered profile name with its authorization link. Expand **Show variables** to see the full set of supported variables. | Scope | Variables | Notes | | - | - | - | | Message scope | `{{total}}`, `{{completed}}`, `{{remaining}}` | Counts for the session's pending authorizations. Use these anywhere in the template. | | Item scope | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Valid only inside the repeat block. | | Repeat block | `{{#authorizations}}`, `{{/authorizations}}` | Wraps the item scope variables and repeats the enclosed text once per pending authorization. | Expand **Show preview** to see the template rendered with sample values. At runtime the platform fills these from the live session's pending authorizations. Select **Save Changes** to apply any changes. The **Deployment** tab shows these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown (for example: `Development`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and when it was deployed. | Select **Save Changes** after switching the environment. # Set up Genesys Source: https://koreai.mintlify.app/agent-platform/channels/genesys [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital) Connect your agent to Genesys Cloud as a Bot Connector so an Architect message flow can hand a conversation to it for intent and slot processing. You install the Bot Connector integration in Genesys, bind its Stream ID and shared secret to Artemis, then call the integration from an Architect flow. This channel works differently from every other messaging channel. Genesys sends one authenticated POST carrying the user's turn and holds that request open while your agent runs, then reads the reply out of the same HTTP response. There's no callback and no later send API. The setup requires: 1. [Genesys Cloud setup](#genesys-cloud-setup): install and name the Bot Connector integration, and note its Stream ID. 2. [Artemis channel configuration](#artemis-channel-configuration) using the Stream ID and a shared secret you generate, then copy the generated Bot Connector URL. 3. [Bot Connector configuration in Genesys](#bot-connector-configuration-in-genesys): set that URL and the same secret on the integration, activate it, and call it from an Architect flow. After you complete the configuration, an Architect flow reaching the **Call Bot Connector** action sends the turn to Artemis, the bound deployment answers inside the same request, and Architect branches on the intent your agent returns. **Important considerations** * **The response travels in the same request**: Genesys waits for your agent. Provider timeout and agent execution time have to be compatible, and this channel stays non-streaming by design. * **Client Secret isn't a Genesys OAuth secret**: for Bot Connector, it's a pre-shared bearer token you invent and configure identically on both sides. Substituting an OAuth integration secret fails unless Genesys sends that same value on the webhook request. * **Bot Connector needs a subscription change**: Genesys requires you to contact Genesys Cloud Sales before you can install the integration. * **Architect intents must already exist**: Artemis returns intent names that Genesys matches to branches in your flow. It doesn't create or validate them, so a name that doesn't exist in Architect produces a branch that never fires. ## Prerequisites * A Genesys Cloud organization with the Bot Connector integration available on your subscription, in the region that owns the Architect flow. * Admin access in Genesys Cloud, with permission to install integrations and edit Architect flows. * An Architect message flow where the conversation should reach the agent, or permission to create one. * Artemis project access with permission to open **Deployments** -> **Channels**, and a deployed agent in the environment you intend to bind. *** ## How Artemis handles Bot Connector requests Genesys posts to a URL ending in the Stream ID, so Artemis resolves the connection from that path segment and the conversation from the payload. | Piece | Who owns it | What it does | | - | - | - | | **Stream ID** | Genesys | Routes inbound requests. Artemis reads the final path segment and matches it to an active connection. It's a routing identifier rather than a secret. | | **Client Secret** | You | Authenticates the request. Artemis compares the token Genesys sends with the stored secret using a timing-safe check. | | Genesys conversation ID | Genesys | Identifies each end-user session inside the connection. Artemis keys the session to it, so reusing it continues the conversation and changing it starts a new one. | | **Webhook URL** | Artemis | Receives the Bot Connector POST. It ends with the encoded Stream ID, and the generic Genesys route alone can't select a connection. | Because the exchange is synchronous, the HTTP response carries everything Genesys needs: the reply messages, the bot state, the intent, and whether the task ended. Nothing arrives later. *** ## Genesys Cloud setup Do this work in Genesys Cloud before you open Artemis. Genesys documents the same screens in [Configure the Genesys Bot Connector integration](https://help.genesys.cloud/articles/configure-the-genesys-bot-connector-integration/). Sign in to the Genesys Cloud organization and regional domain that own the Architect flow you intend to change. Genesys organizations are region-specific, so a development organization's integration pointed at production Artemis produces a setup that looks correct but serves the wrong traffic. Confirm the organization before you install anything. 1. Select **Admin**, then under **Integrations**, select **Integrations**. On newer navigation, select **Menu** -> **IT and Integrations** -> **Integrations**. 2. Search for the **Genesys Bot Connector** tile and click **Install**. 3. On the **Details** tab, give the integration a name that identifies the agent and the environment, and add notes if your team uses them. Pick a distinct name. Architect actions reference integrations by name, so duplicates make it easy to point a flow at the wrong one later. Don't install the **Genesys Digital Bot Connector** tile by mistake. It's a separate integration with its own Architect action, and Artemis connects through Bot Connector. Keep this integration's lifecycle separate from Open Messaging and Audio Connector integrations too. Genesys allows up to five Bot Connector integrations per organization, which is the practical ceiling on how many Artemis connections one organization can carry. Copy the Stream ID Genesys shows for this integration, exactly as it appears. This becomes the routing identifier in Artemis and the last segment of the webhook URL. Copy it without surrounding whitespace, because a trailing space produces a 404 that looks like a missing connection. Don't substitute the Genesys organization ID, the integration ID, an OAuth client ID, a conversation ID, or the Architect flow ID. None of them route. Create a high-entropy random string. You invent this value rather than Genesys issuing it, and you'll enter the identical string in two places: the Artemis connection, and the Bot Connector credentials in Genesys. Use a different secret per environment, and store it in your secret manager. This token is the only thing standing between the public URL and your agent. *** ## Artemis channel configuration Create the connection before you configure the integration in Genesys. Artemis resolves the Stream ID and decrypts the secret before it authenticates a request, so an inactive or missing connection returns 404 even when the URL is right. 1. Open Artemis and select the project. 2. Go to **Deployments** -> **Channels** -> **Genesys** -> **+ New**. 3. Configure the following fields: | Artemis connection field | Source / Notes | | - | - | | Display Name | The label operators see in the connections list. Name it for the Genesys organization and the environment it serves, because a project can hold several Genesys connections. Routing ignores this value. | | Stream ID | The routing key. Artemis writes it into the webhook path and resolves the connection from it. Enter the exact value from the Bot Connector integration, with no leading or trailing whitespace. | | Client Secret | The shared bearer token you generated. Artemis compares the token on each request with this encrypted value using a timing-safe check. | | Environment | The published environment whose active deployment answers the Architect flow. Leave the working copy default only while you develop. | | Provider Verification Strength | How far Artemis trusts Genesys's identity assertion when it links a caller to an existing session. **Weak** suits most deployments. Choose **Strong** only when you trust those assertions enough to base continuity and account-linking decisions on them. | | Initial Status | Whether the connection handles requests. Set it to **Active**, because an inactive connection returns 404 rather than an authentication error, which sends you looking in the wrong place. | 4. Click **Create**. 5. Open the connection and copy the **Webhook URL** from the **Overview** tab.
The URL ends with the URL-encoded Stream ID. Copy the whole thing, because the generic Genesys route can't identify which connection a request belongs to. ### Validation checklist | Validation item | Expected result | | :- | :- | | Confirm the subscription | The Genesys Bot Connector tile appears in your organization's integration list. | | Install the integration | Bot Connector integration installed and named distinctly, in the region that owns the flow. | | Copy the Stream ID | Stream ID copied exactly, with no surrounding whitespace, and distinct from organization, integration, and flow IDs. | | Create the Artemis connection | Connection saved as Active with the Stream ID and the shared secret. | | Copy the webhook URL | Bot Connector URL copied from the **Overview** tab, ending with the encoded Stream ID. | | Configure the integration | URL and matching secret saved on the integration, and its status set to active. | | Map the Architect outcomes | Intent names in Artemis match branches that already exist in the Architect flow. | | Wire the Architect action | **Call Bot Connector** action added to the flow, pointing at this integration, with its failure path connected. | | Run a test interaction | Test turn reaches the agent and Architect branches on the returned intent. See [Verify the connection](#verify-the-connection). | *** ## Bot Connector configuration in Genesys Return to the integration you installed and finish it with the URL and secret from Artemis, then call it from a flow. Open the integration's **Configuration** tab and set the Bot Connector URL to the **Webhook URL** from the Artemis connection. Preserve the encoded Stream ID at the end. Genesys uses this URL to relay each utterance, so a truncated path reaches Artemis and resolves to nothing. Open the integration's **Credentials** tab and configure the same Client Secret you entered in Artemis as the bearer token. Artemis prefers the token on an `Authorization: Bearer` header. It also accepts `x-channel-secret`, `x-ingress-secret`, or `x-webhook-secret`. It never accepts a token in the query string, so don't append one to the URL. Set the integration status to active. An inactive integration doesn't appear in Architect at all, so this step has to come before you try to add the action. 1. Open the Architect message flow that should reach the agent. 2. From the toolbox, drag a **Call Bot Connector** action into the flow. 3. Set the action's bot integration to the integration you just configured, and set the name and version fields the action asks for. 4. Connect the action's failure path to real handling rather than leaving it unconnected. Genesys documents the action's inputs, timeouts, and error types in [Call Bot Connector action](https://help.genesys.cloud/articles/call-bot-connector-action/). The failure path matters: Architect takes it on execution errors and connectivity problems, and it also takes a safety path after four consecutive turns return an unchanged intent. Run Architect's validation and test the flow on a non-production interaction. Publish only after you've confirmed the URL, the secret, the response timing, and the failure branches. A published flow with an unreachable bot fails in front of customers. *** ## Map Architect outcomes Artemis returns an intent name to Genesys at the end of each turn, and Architect branches on it. Configure the mapping on the connection's **Configuration** tab, under **Architect outcome mapping**. Turn on **Enable terminal intent mapping**, then set the intent name Artemis returns for each outcome. | Outcome | Artemis returns it when | | - | - | | Continue conversation | The conversation should stay with this agent for another turn. | | Escalate to an agent | The conversation should transfer to a human. | | Close the session | The conversation should end. | Each outcome takes a **Value source** of either a literal intent name you type, or a project variable that supplies it. Use a project variable when the same agent serves several Genesys flows with different intent vocabularies, and a literal name otherwise. These names have to match intents and branches that already exist in your Architect flow. Artemis neither creates nor validates them, so a typo produces a branch Architect never takes rather than an error you can see. Agree the names with whoever owns the flow before you enter them here. *** ## Verify the connection Test each failure mode as well as the happy path, because Architect's branching depends on Artemis returning the right status for each one. 1. Send a text turn with a unique message and a stable Genesys conversation ID.
Confirm an HTTP 200 and one text reply in the response. 2. Send a second turn reusing the same conversation ID, and confirm it resumes the same session.
A different conversation ID should open a separate session. 3. Return an authored quick reply, send its encoded payload back as the button response, and confirm the session resumes with the action value you authored. 4. Send a request with the wrong secret and confirm it returns 401. 5. Send a request to an unknown or inactive Stream ID and confirm it returns 404. 6. Send malformed input and confirm it returns 400. 7. Confirm the Architect flow handles a 503 busy response and an execution failure without looping. *** ## Request and response contract Genesys carries the whole turn in one exchange, so both halves have required fields. | Direction | Required content | | - | - | | Request | A non-empty Genesys conversation ID and an input message type. A text turn also needs the message text; a structured quick reply needs the button response payload. | | Response | The reply messages, the bot state, the intent name, and whether the task ended. Genesys reads all of these from the same HTTP response. | Keep the conversation ID stable across every turn in one Genesys interaction. Replacing it with the Stream ID or an ephemeral request ID resets the conversation context, which looks like the agent forgetting rather than like a configuration error. Optional session metadata crosses a trust boundary, so Artemis validates it before the agent runs. Send it only in the JSON object shape Artemis expects. *** ## Secure the connection The Stream ID appears in a public URL and isn't a secret. HTTPS and the shared token protect the endpoint, so never put the token in the URL or in logs. Use a unique, high-entropy Client Secret for each environment, stored only in the Genesys integration's secure credential configuration and in encrypted Artemis credentials. Rotating it means changing both sides together, because Artemis rejects a request the moment the values diverge. Prefer the `Authorization: Bearer` header. If your ingress adds or rewrites headers, make sure it preserves the one Genesys sends and strips any untrusted duplicate before the request reaches Artemis. Two competing authorization headers produce a 401 that looks like a wrong secret. Use separate Genesys integrations, Stream IDs, secrets, Architect flows or versions, and connections for development and production. *** ## Manage the channel connection Opening a connection shows four tabs. The delete control sits beside the connection name. **Connection Summary** reports the connection's state and identity. | Field | Description | | - | - | | **Status** | Whether the connection currently handles requests. | | **Created** and **Updated** | Date and time of creation, and of the last change. | | **Environment** | The environment whose deployment answers requests. | | **Source Type** | Reads `Channel Connection`. | | **Source ID** | Identifies the connection uniquely. Quote it when you raise a support ticket. | **Setup Instructions** shows the **Webhook URL** to configure in Genesys and the **Stream ID** it ends with, each with a copy control. Compare the Stream ID here with the integration's whenever requests return 404. Artemis encrypts the secret after you save it, so the field reads as saved rather than showing its value. Leave it empty to keep the current value, or enter a new one to replace it. | Field | Description | | - | - | | **Use Auth Profile** | Draws the credential from a saved auth profile instead of the field below. Prefer it when the same secret serves more than one connection, since a rotation then touches one profile. | | **Client Secret** | The shared bearer token. Change it here and in the Genesys integration as one coordinated step, because the two have to match on every request. | Click **Save Credentials** to apply your changes. Under **General**, rename the connection with **Display Name**, and revisit **Provider Verification Strength** if your trust posture for this organization changes. Under **Genesys Settings**, **Stream ID** appears read-only. The webhook URL is built from it, so changing it would invalidate the URL configured in Genesys. Create a new connection to serve a different integration. **Authorization Message** controls what the customer sees when a tool needs them to authorize something before the conversation continues. Write the template with these variables. | Scope | Variables | Use | | - | - | - | | Message | `{{total}}`, `{{completed}}`, `{{remaining}}` | Summarize progress across every pending authorization, typically in a lead-in line above the list. | | Repeat block | `{{#authorizations}}` and `{{/authorizations}}` | Wrap the section Artemis repeats once per pending authorization. | | Item | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Identify and link each authorization inside the repeat block. | **Architect outcome mapping** sets the intent names Artemis returns to Genesys. See [Map Architect outcomes](#map-architect-outcomes). Click **Save Changes** to apply your changes. Choose which deployed version of the agent answers requests on this connection. Under **Agent Version**, set **Environment** to the environment you want the connection to follow. **Active Deployment** then shows the version currently serving traffic and when someone deployed it. Check this pair when the flow reaches the agent but the agent behaves like an older build. The connection follows whatever the environment currently has deployed, not the version you last edited. Click **Save Changes** after you switch. *** ## Troubleshoot connection issues | Symptom | Likely cause | Recommended action | | - | - | - | | The webhook returns 404 | The Stream ID doesn't match, or the connection isn't active | Confirm the URL ends with the exact Stream ID, check the saved identifier for copied whitespace, and confirm the connection is Active in the intended project. | | The webhook returns 401 | The token doesn't match, or headers conflict | Send the exact Client Secret through `Authorization: Bearer` or a supported secret header, remove duplicate authorization headers at your ingress, and confirm nobody rotated the secret on one side only. | | The webhook returns 400 | The request is missing a required field | Include a non-empty conversation ID and an input message type. Text turns also need the message text, and structured actions need the button response payload. | | The webhook returns 503 busy | Concurrent turns for the same session couldn't acquire the lock | Serialize turns within a conversation and retry according to the Architect failure policy. | | Genesys times out before a response | Agent execution outlasts the provider timeout | Compare the configured timeout with actual execution duration, investigate model and tool latency, and keep this channel synchronous and non-streaming. | | Text works but quick replies don't | The response lacks structured entries, or the payload doesn't return | Confirm the response carries quick reply entries and that the selected encoded payload comes back as the button response payload. | | Conversation context resets between turns | The conversation ID changes each turn | Preserve the same Genesys conversation ID for every turn in one interaction, and don't replace it with the Stream ID or a per-request identifier. | | Architect never takes the expected branch | The intent name doesn't exist in the flow | Compare the names in **Architect outcome mapping** with the intents and branches in Architect. Artemis doesn't create or validate them. | | The integration doesn't appear in Architect | The integration isn't active | Set the integration's status to active in Genesys Cloud, then reopen the flow. | *** ## Genesys reference Use these Genesys pages when you need detail beyond what this article covers. | Resource | Use it for | | - | - | | [About Genesys Bot Connector](https://help.genesys.cloud/articles/about-genesys-bot-connector/) | What the connector supports, including quick replies, cards, and carousels. | | [Configure the Genesys Bot Connector integration](https://help.genesys.cloud/articles/configure-the-genesys-bot-connector-integration/) | Installing the integration, its tabs, and activating it. | | [Call Bot Connector action](https://help.genesys.cloud/articles/call-bot-connector-action/) | Action inputs, response timeouts, session variables, error types, and failure paths. | | [About Genesys Digital Bot Connector](https://help.genesys.cloud/articles/about-genesys-digital-bot-connector/) | Telling the two connectors apart, if you're unsure which one your flow uses. | **Related articles:** * [Set up Zendesk](/agent-platform/channels/zendesk) * [Set up WhatsApp](/agent-platform/channels/whatsapp) # Genesys Audio Connector Source: https://koreai.mintlify.app/agent-platform/channels/genesys-audio-connector [Back to voice channels](/agent-platform/channels#voice-channels) Create a project-owned Genesys Audio Connector connection, choose a Voice mode, configure that mode's speech settings, wire the connection into Genesys Architect, and validate live audio before enabling traffic. **Voice mode can't be changed after the connection is created.** Choose **Pipeline** for independent speech-to-text, LLM, and text-to-speech providers, or **Realtime** for a single speech-to-speech (S2S) model with lower latency. If you need the other architecture later, create a new connection. ### Where setup happens Setup moves back and forth between Genesys Cloud and Studio. Here's where each step happens: | Step | Where | | - | - | | [Prepare Genesys Cloud and authentication](#prepare-genesys-cloud-and-authentication) | Genesys Cloud | | [Create the connection in Studio](#create-the-connection-in-studio) | Studio | | [Configure the connection](#configure-the-connection) | Studio | | [Wire the Genesys Architect flow](#wire-the-genesys-architect-flow) | Genesys Cloud | | [Run live validation before enabling traffic](#run-live-validation-before-enabling-traffic) | Studio + Genesys Cloud | ## Prepare Genesys Cloud and authentication Sign in to the regional Genesys Cloud organization that owns the voice deployment. Studio uses this region to select the matching Genesys API host. In Genesys Cloud Admin, identify the voice deployment associated with the intended Architect flow and copy its Voice Deployment ID. Generate a project-specific Audio Connector API key for this connection. Configure the matching value in Genesys when you wire the Architect flow. Use separate API keys, voice deployments, and connections for development and production. Never share one connection's credentials across environments. ## Create the connection in Studio To configure a Genesys Audio Connector channel, go to **Deployments** -> **Channels** -> **VOICE** -> **Genesys Audio Connector** -> **+ New** and complete the following steps in order. Give the connection a label, for example `Genesys Audio Connector Production`. Enter the exact Genesys Organization ID and select the region that owns it, for example `US East (N. Virginia)`. A mismatched region can make valid credentials appear unavailable. Paste the voice deployment ID associated with the intended Architect flow. Select **Pipeline** or **Realtime**. This choice is permanent for the connection - see the note above. Start in **Diagnostics only** so the Testing tab can collect evidence without treating the setup as production-ready. Switch to **Enabled** only after validation passes. Enter the project-specific Audio Connector API key. **Webhook Signature Secret** is optional at creation - set it when Genesys is configured to sign outbound requests. Set the maximum duration for a single voice session, in seconds (default: `1800`). | Field | Description | | - | - | | **Environment** | Select an environment to follow its active deployment, or leave the default to use the working copy. | | **Provider Verification Strength** | **Weak** is the default. Choose **Strong** only for channels or providers whose identity assertions you trust for stronger continuity and linking decisions. | | **Initial Status** | **Active** connections can be used immediately. **Inactive** connections stay saved but disabled until you activate them. | Select **Create**. The next step - configuring speech settings for your chosen Voice mode - happens after creation, on the connection's **Configuration** tab. ### Connection fields reference | Field | Description | | - | - | | **Display Name** | A label for the connection. | | **Genesys Organization ID** | Your Genesys Cloud organization identifier. | | **Allowed Region** | The Genesys region this connection is scoped to. | | **Voice Deployment ID** | The voice deployment associated with the intended Architect flow. | | **Voice mode** | **Pipeline** (independent STT/LLM/TTS providers) or **Realtime** (single S2S model). Permanent after creation. | | **Activation Mode** | **Diagnostics only** (default, safe - setup and validation, no production traffic), **Enabled** (processes live calls), or **Disabled**. | | **API Key** | A project-specific Audio Connector API key. | | **Webhook Signature Secret** | Optional at creation. Configure the identical value in Genesys when signed HTTP signaling is enabled. Referred to as **HTTP Message Signature Secret** later, on the Credentials tab - same value, different label. | | **Max Session Seconds** | Maximum duration for a single voice session, in seconds. Default: `1800`. | ## Configure the connection After creation, open the connection and go to its **Configuration** tab to set up speech behavior for the Voice mode you chose. | Field | Description | | - | - | | **Provider** | The speech-to-text credential set to use, for example `Deepgram Speech Credentials`. | | **Language** | The recognition language, for example `Multilingual`. | | **STT Model** | The recognition model, for example `Nova 3 (latest, most accurate)`. | | **Minimum STT confidence** | Reject transcripts below this confidence (0–1) and treat them as no-input. `0` disables the gate. Provider-relative - calibrate per channel. | | **Custom vocabulary** | Domain-specific terms to bias recognition toward. Select **Add phrase** and set a **Phrase** (for example `ACME, policy number`) and optional **Boost**. Deepgram Nova-3 and Flux use keyterm prompting and ignore boost; older Deepgram models use keyword intensifiers. | Optionally enable **A different voice per language** to give each language its own voice instead of one voice for every caller. Then set: | Field | Description | | - | - | | **Provider** | The text-to-speech credential set to use, for example `ElevenLabs (TTS) Credentials`. | | **Language** | The synthesis language, for example `Arabic`. | | **Voice** | The voice used for synthesized speech. | | **TTS Model** | The synthesis model, for example `Eleven Multilingual v2`. | If you selected an ElevenLabs voice, an **ElevenLabs Voice Settings** panel lets you override **Speed**, **Stability**, **Similarity boost**, **Style exaggeration**, and **Speaker boost** for this connection, plus a **Preview Voice** field to hear the configured voice before saving. | Field | Description | | - | - | | **Barge-in** | Allow the caller to interrupt the agent while it is speaking. | | **Speech Timeout (ms)** | The time the agent waits for caller speech before it times out, for example `3000`. | | **Welcome Message** | The opening prompt the agent speaks, such as `Hello! How can I help you today?`. | | **Noise isolation** | Apply server-side noise reduction to inbound call audio to filter out background voices and ambient noise. | | Field | Description | | - | - | | **Transfer Queue Header Key** | SIP transfer header name that carries the queue, for example `X-Queue`. Must match what Genesys expects. Required for escalate-to-human. | | **Transfer Queue Name** | The Genesys queue used when the agent transfers a caller to a human, for example `Kore Inbound`. Must match a queue configured in Genesys. | Select **Save Changes**. Under **Realtime LLM Voice Settings**, select an **S2S Provider**. Only the panel for the selected provider applies to this connection. **Azure OpenAI Realtime, Grok Realtime, Google Gemini Live, and OpenAI Realtime** each require a **Realtime model connection** - a project-level model pinned to this channel. Amazon Nova Sonic does not use this field; it authenticates via Voice Service credentials instead. To make a model available here: Configure the model under [Voice Services](/agent-platform/administration/ai-configuration#voice-services), then add it to the [model catalog](/agent-platform/administration/ai-configuration#model-catalog) as an LLM provider. Go to **Settings** -> **Model** and add the model you configured at the workspace level. Once added, the model becomes selectable in the **Realtime model connection** dropdown below. | Field | Description | | - | - | | **Realtime model connection** | The project-pinned realtime model this provider uses. Shows `No project model selected` until a workspace admin adds one following the steps above; once selected, shows the model name and provider (for example `GPT Realtime 2 · openai`). | | Field | Description | | - | - | | **Voice** | The synthesized voice, for example `Tiffany (default)`. | | **Model** | The Amazon Bedrock model ARN, for example `arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-2-sonic-v1:0`. | | **Maximum tokens** | The maximum response length, in tokens. Default: `1024`. | | **Temperature** | Response creativity. Default: `0.70`. | | **Top P** | Nucleus sampling threshold. Default: `0.90`. | | **Endpointing sensitivity** | How quickly the model treats the caller as finished speaking - `Low`, `Medium`, or `High`. `Medium` is the general-conversation default; `High` responds sooner but may interrupt callers; `Low` allows more pause before responding. | | Field | Description | | - | - | | **Voice** | The synthesized voice, for example `Marin (recommended)`. | | **Temperature** | Response creativity, from `0.6` (Focused) to `1.2` (Creative). Default: `0.8`. | Under **Turn Detection** - tunes how Azure OpenAI detects speech starts and decides when the caller has finished speaking: | Field | Description | | - | - | | **Speech detection sensitivity** | Lower values pick up quieter speech; higher values reduce background-noise triggers. Default: `0.5`. | | **Silence Duration (ms)** | Quiet time before Azure OpenAI treats the caller turn as complete. Default: `700`. | | **Prefix Padding (ms)** | Audio kept before detected speech so the beginning of the caller utterance is preserved. Default: `300`. | | Field | Description | | - | - | | **Model** | The Grok Realtime model, for example `grok-voice-latest (recommended)`. | | **Voice** | The synthesized voice, for example `Ara (default)`. | | **Temperature** | Response creativity, from `0.0` (Focused) to `2.0` (Creative). Default: `1`. | | **Threshold** | Turn-detection sensitivity, from `0.0` (Sensitive) to `1.0` (Conservative). Default: `0.5`. | | **Silence Duration (ms)** | Quiet time before the caller turn is treated as complete. Default: `500`. | | **Prefix Padding (ms)** | Audio kept before detected speech so the start of the caller's utterance is preserved. Default: `300`. | The xAI Grok Realtime API provides low-latency voice conversations with function calling support. | Field | Description | | - | - | | **Model** | The Gemini Live model, for example `Gemini 3.1 Flash Live (Latest)`. | | **Voice** | The synthesized voice, for example `Puck - Upbeat`. | | **Pending tool fillers** | Allow Gemini Live to acknowledge long-running project tools while the original tool call remains pending. | | **Temperature** | Response creativity, from `0.0` (Deterministic) to `2.0` (Creative). Default: `1`. | Under **Voice Activity Detection** - controls when Gemini starts listening to a caller turn and how long it waits before closing that turn: | Field | Description | | - | - | | **Start Sensitivity** | Higher detects speech sooner but may trigger on noise; lower waits for a stronger speech signal. Default: `Default`. | | **End Sensitivity** | Higher ends turns sooner for lower latency; lower allows longer pauses before Gemini responds. Default: `Default`. | | **Silence Duration (ms)** | Milliseconds of non-speech before Gemini closes the caller turn. Increase to allow pauses; decrease for faster responses. Default: `100`. | | **Prefix Padding (ms)** | Milliseconds of detected speech required before Gemini commits a speech start. Lower values catch shorter utterances but can increase false starts. Default: `20`. | Google Gemini Live provides multimodal voice conversations with low latency. | Field | Description | | - | - | | **Model** | The OpenAI Realtime model, for example `gpt-realtime-1.5 (recommended)`. | | **Voice** | The synthesized voice, for example `Marin (recommended)`. | Under **Turn Detection** - tunes how OpenAI detects speech starts and decides when the caller has finished speaking: | Field | Description | | - | - | | **Speech detection sensitivity** | Lower values pick up quieter speech; higher values reduce background-noise triggers. Default: `0.5`. | | **Silence Duration (ms)** | Quiet time before OpenAI treats the caller turn as complete. Default: `700`. | | **Prefix Padding (ms)** | Audio kept before detected speech so the beginning of the caller utterance is preserved. Default: `300`. | The OpenAI Realtime API provides low-latency voice conversations with function calling support. The following fields apply regardless of which S2S provider you select: | Field | Description | | - | - | | **Noise isolation** | Apply server-side noise reduction to inbound call audio to filter out background voices and ambient noise. | | **Transfer Queue Header Key** | SIP transfer header name that carries the queue, for example `X-Queue`. Must match what Genesys expects. Required for escalate-to-human. | | **Transfer Queue Name** | The Genesys queue used when the agent transfers a caller to a human, for example `Kore Inbound`. Must match a queue configured in Genesys. | Select **Save Changes**. ## Wire the Genesys Architect flow Open the connection's **Testing** tab and copy the **AudioHook Callback URL** - a `wss://` URL with an embedded connection ID, for example `wss://korevg-np.audiosocket.kore.ai/audiosocket/genesysconnect/`. Preserve the full URL and connection ID; do not substitute a generic Runtime WebSocket. In the intended Architect flow, add or update the Audio Connector action and paste the callback and API key into the corresponding integration fields. Keep the configured PCMU framing, sample rate, channel count, and media direction aligned with the platform contract. Transcoding or buffering at an intermediate proxy adds latency and can corrupt audio. Capture normal completion, caller hangup, gateway error, and fallback outcomes so Architect can continue or exit the flow deterministically. ## Run live validation before enabling traffic Use a dedicated Architect test flow and test number. Confirm session creation, greeting playback, caller speech, agent response, barge-in, and clean hangup. Confirm PCMU framing, bidirectional audio, queue behavior, packet loss, and disconnect variables under normal speech, silence, interruption, and rapid turn-taking. Record audio jitter p95, first audio p95, STT first partial p95, TTS first audio p95, and representative end-to-end turn latency. Investigate regressions before promotion. On the **Testing** tab, record AudioHook and KoreVG session IDs, Architect flow name and version, Runtime trace IDs, sandbox evidence, dashboard and alert references, concurrency, and fallback results, then select **Save Evidence**. Mark validation **Passed**, retain the evidence, then change **Activation Mode** to **Enabled**. Keep a tested rollback or fallback path available. ### Promotion gate checklist Before switching a connection from **Diagnostics only** to **Enabled**, confirm all four areas below have passing evidence: | Gate | What to confirm | | - | - | | **1. Architect sandbox call** | Greeting, caller speech, agent audio, barge-in, and hangup all work. AudioHook and KoreVG session IDs are captured. | | **2. Media contract** | PCMU framing, bidirectional audio, and backpressure hold up under packet loss and disconnect scenarios. | | **3. Latency and capacity evidence** | Audio jitter p95, first audio p95, STT first partial p95, TTS first audio p95, and concurrency are all recorded. | | **4. Promote Diagnostics only -> Enabled** | Passed evidence, trace IDs, flow version, dashboards, alerts, and a tested rollback or fallback path are all in place. | ## Manage the channel connection Opening a connection from **Deployments** -> **Channels** -> **VOICE** -> **Genesys Audio Connector** shows six tabs: **Overview**, **Credentials**, **Configuration**, **Deployment**, **Testing**, and **Activity**. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | For example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | For example: `Working Copy (draft)` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique channel identifier (UUID) | Under **Setup Instructions**, the tab shows the **Genesys Organization ID** and the **SIP URI** for reference. Select the copy icon next to either value to copy it. Credentials are saved and encrypted after creation; fields show placeholder text and accept new values only if you want to update them. | Field | Description | | - | - | | **Use Auth Profile** | Toggle on to authenticate through a saved Auth Profile instead of an API key. | | **API Key** | Shown as *(saved - enter to update)*; leave blank to keep the current value. | | **HTTP Message Signature Secret** | Shown as *(saved - enter to update)*; leave blank to keep the current value. This is the same secret set as **Webhook Signature Secret** at creation. | Select **Save Credentials** to apply any changes. Shows **General** (Display Name, Provider Verification Strength) plus the Pipeline or Realtime speech settings described in [Configure the connection](#configure-the-connection) above, editable at any time. Select **Save Changes** to apply. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown that selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and date, or `Working Copy (draft)` if nothing has been deployed to an environment yet. | If the connection is running on the working copy, a banner reads: *"This channel is running on the Working Copy (draft). Working copy is intended for testing. For a stable production setup, deploy your agent to an environment and bind this channel to that environment."* Select **Save Changes** after switching the environment or version. The **Audio Connector Health** panel shows: | Field | Description | | - | - | | **AudioHook Target** | The execution target for this connection, for example `KoreVG (SAVG audiosocket)`. | | **Last Success** / **Last Failure** | Timestamps of the most recent diagnostic outcomes, or a prompt such as *Live Architect validation required*. | | **Provider URL** | The underlying provider endpoint the connection routes to. | | **AudioHook Callback URL** | The full `wss://` URL to configure in the Architect Audio Connector action. | The **Live Validation Evidence** panel records the results of the checks in [Run live validation before enabling traffic](#run-live-validation-before-enabling-traffic). | Field | Description | | - | - | | **Validated At** | Timestamp of the last passing validation. | | **Trace IDs** | Number or list of Runtime traces captured during validation. | | **Certification Status** | For example: `Not started`. | | **Runbook Reference** / **Dashboard Reference** / **Alert Reference** | Links or IDs for the operator runbook, dashboard, and alert configuration used during validation. | | **Runtime Trace IDs** / **Sandbox Evidence References** | Free-text fields for trace IDs and sandbox evidence links. | | **Validation Summary** | Free-text summary of the validation outcome. | | **Audio Runtime Target** | The runtime target validated, for example `KoreVG (SAVG audiosocket)`. | | **Architect Flow Name** / **Architect Flow Version** | The Architect flow and version used during validation. | | **AudioHook Session ID** / **KoreVG Session ID** | Session identifiers captured during the validation call. | | **PCMU framing verified** / **Backpressure verified** / **Disconnect variables verified** / **Fallback path verified** | Checkboxes confirming each part of the media contract. | | **Audio Jitter P95 (ms)** / **First Audio P95 (ms)** / **STT First Partial P95 (ms)** / **TTS First Audio P95 (ms)** | Recorded latency metrics from validation. | | **Max Concurrent Sessions** / **Packet Loss (%)** | Recorded capacity and reliability metrics from validation. | Select **Save Evidence** to record your results. ## Security, capacity, and reliability boundaries Keep the API key and HTTP Message Signature Secret in Genesys and encrypted Studio credentials. Use distinct values per environment and rotate exposed credentials. **Audio** - Don't log raw audio or sensitive transcript content. Use scrubbed trace identifiers and aggregate voice phase metrics for operational debugging. **Capacity** - Validate the expected concurrent-session ceiling and queue pressure. KoreVG has bounded per-session queues and environment-level capacity controls. **Fallback** - Define what Architect does when the AudioHook can't connect, media stalls, STT or TTS fails, or the agent exceeds its response budget. **Observability** - Monitor connection failures, audio jitter, packet loss, STT, LLM, and TTS phase latency, session termination reasons, and fallback activation. # Genesys Open Messaging Source: https://koreai.mintlify.app/agent-platform/channels/genesys-open-messaging [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital-channels) Deploy your agent through Genesys Cloud Open Messaging. Connect your Genesys Open Messaging integration to the platform by configuring the required provider details, authentication, and signed webhook. **Webhook Connection Token and Webhook Signature Secret are different.** The connection token identifies the connection through the webhook URL, while the signature secret verifies the authenticity of incoming requests. ## Create or select an Open Messaging integration in Genesys Cloud Sign in to the Genesys Cloud organization that owns the Open Messaging integration. Select the correct Genesys region because the platform uses it to connect to the corresponding Genesys Platform API host. In Genesys Cloud, create or select the required Open Messaging integration and copy its **Integration ID**. Do not use a Bot Connector Stream ID, Audio Connector integration ID, organization ID, or OAuth client ID. Create an active Auth Profile for the Genesys connector using OAuth 2.0 Client Credentials or Bearer authentication. The platform uses this profile for outbound Genesys Platform API calls. Ensure that the Genesys OAuth client has the permissions required to send Open Messaging inbound messages, structured responses, and receipts. Use separate clients, integrations, and Auth Profiles for development and production environments. ## Integrate the Genesys Open Messaging connection Open Artemis, select the required project, and go to **Deployments** -> **Channels** -> **MESSAGING** -> **Genesys Open Messaging** -> **+ New**. Enter the required provider and authentication details. | Field | Description | | - | - | | **Display Name** | A label for the connection, for example `Genesys Open Messaging Production`. Up to 255 characters. | | **Genesys Region** | The region that hosts your Genesys Cloud organization. | | **Organization ID** | The identifier for your Genesys Cloud organization. | | **Integration ID** | The Open Messaging Integration ID. | | **Activation Mode** | Select **Diagnostics only** initially to validate the connection before processing live traffic. | | **Delivery Mode** | Keep **External bridge** unless a different delivery mode is explicitly required. External bridge is the standard bot-facing channel — it's what makes the connection available to the Genesys Open Messaging agent-transfer handoff. | | **Genesys Auth Profile** | An active Auth Profile configured for the Genesys integration. | The Integration ID is used as the external identifier for outbound provider calls. The selected Auth Profile must be active and configured for the Genesys integration. | Field | Description | | - | - | | **Environment** | Select an environment to follow its active deployment, or leave the default to use the working copy. | | **Provider Verification Strength** | **Weak** is the default. Choose **Strong** only for channels or providers whose identity assertions you trust for stronger continuity and linking decisions. | | **Initial Status** | **Active** connections can be used immediately. **Inactive** connections stay saved but disabled until you activate them. | When you create the connection, the platform generates the following values. | Value | Description | | - | - | | **Webhook Connection Token** | Identifies the connection and is included in the connection-specific webhook URL. | | **Webhook Signature Secret** | Verifies incoming webhook requests using HMAC-SHA256. | Copy the generated webhook endpoint and signature secret before closing the confirmation screen. These values are required when configuring the Genesys Open Messaging integration. ## Configure the webhook in Genesys Cloud In your Genesys Open Messaging integration, enter the complete public HTTPS webhook URL generated for the connection. The URL must include the connection token. The generic webhook path cannot identify the required connection. Enter the Webhook Signature Secret generated by the Artemis as the signature secret for the Genesys integration. Do not use the Genesys OAuth client secret or the Webhook Connection Token as the signature secret. Incoming requests must be signed using **HMAC-SHA256** over the exact raw request body. Runtime validates the signature before processing the request. Do not parse and reserialize the request body before signature validation because changing the request bytes can cause a valid signature to fail. ## Test the connection Once the channel is created, select it, then go to the **Testing** tab. Open the connection's testing or diagnostics area and run the signature validation. This confirms that the configured signature secret and supported signature format are correct. Run the inbound message diagnostic and confirm that Runtime resolves the connection and processes the incoming message correctly. Run the receipt diagnostic to confirm that the Auth Profile can authenticate and communicate with the configured Genesys receipt endpoint. After all diagnostics pass, change **Activation Mode** to **Enabled**. Test a live message, a quick reply, and a follow-up message in the same Genesys conversation. ## Manage the channel connection Opening a connection from **Deployments** -> **Channels** -> **MESSAGING** -> **Genesys Open Messaging** shows six tabs: **Overview**, **Credentials**, **Configuration**, **Deployment**, **Testing**, and **Activity**. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | For example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | For example: `staging` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique channel identifier (UUID) | | **Integration ID** | The bound Open Messaging Integration ID, shown for reference. | Under **Setup Instructions**, the tab shows the connection-specific webhook endpoint to paste into the Genesys Open Messaging integration. Credentials are saved and encrypted after creation; fields show placeholder text and accept new values only if you want to update them. | Field | Description | | - | - | | **Use Auth Profile** | Toggle on to authenticate through a saved Auth Profile. | | **Genesys Auth Profile** | Picker to select or change the bound Auth Profile. | | **Webhook Signature Secret** | Shown as *(saved — enter to update)*; leave blank to keep the current value. | The **Webhook Connection Token** isn't editable here — it's embedded in the webhook URL and can't be changed after creation. To rotate it, create a new connection. Select **Save** to apply any changes. | Field | Description | | - | - | | **Display Name** | The connection's label. | | **Provider Verification Strength** | **Weak** or **Strong** — see [Complete common connection settings](#integrate-the-genesys-open-messaging-connection). | | **Integration ID** | The bound Open Messaging Integration ID. | Select **Save Changes** to apply any changes. | Field | Description | | - | - | | **Environment** | Selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and date, or a **Working Copy** warning if nothing has been deployed to this environment yet. | Select **Save Changes** after switching the environment or version. The **Diagnostics** panel runs the checks described in [Test the connection](#test-the-connection). | Field | Description | | - | - | | **Last Success** / **Last Failure** | Timestamps of the most recent diagnostic outcomes. | | **Endpoint** | The webhook endpoint under test. | | **Signature Secret** | The secret used to sign the test request. | | **Customer ID** | The test conversation's customer identifier. | | **Message** | The test message body. | | **Originating Message ID** | The test message's originating ID. | | **Run Signature** | Runs the webhook signature validation. | | **Run Inbound** | Runs the inbound message diagnostic. | | **Run Receipt** | Runs the delivery or read receipt diagnostic. | The **Validation Evidence** panel records the results and supporting evidence from those runs. | Field | Description | | - | - | | **Validated At** | Timestamp of the last passing validation. | | **Trace Count** | Number of Runtime traces captured during validation. | | **Status** | Overall validation status. | | **Operator Runbook Reference** | Link or ID for the operator runbook used during validation. | | **Operator Dashboard Reference** | Link or ID for the operator's monitoring dashboard. | | **Operator Alert Reference** | Link or ID for the operator's alert configuration. | | **Runtime Trace IDs** | Trace IDs captured during validation runs. | | **Sandbox Evidence References** | Links or IDs for sandbox test evidence. | | **Summary** | Free-text summary of the validation outcome. | | **Max Concurrent Sessions** | Peak concurrent sessions observed during validation. | | **Packet Loss Percent** | Observed packet loss during validation, if applicable. | | **Delivery Mode** | The delivery mode validated (for example, External bridge). | | **Provider Conversation ID** | The Genesys conversation ID used during validation. | | **Signature Diagnostic ID** / **Inbound Diagnostic ID** / **Receipt Diagnostic ID** | IDs referencing each diagnostic run. | | **Automated Provisioning Verified** | Whether automated provisioning was confirmed during validation. | | **Chat Round Trip P95 ms** | The 95th-percentile round-trip latency observed during validation. | # Set up Instagram Source: https://koreai.mintlify.app/agent-platform/channels/instagram [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital) Connect an Instagram professional account to your agent so people reach it through Instagram direct messages. Instagram messaging runs on the Messenger Platform. You prepare a Meta app and a Facebook Page alongside the Instagram account, then register a signed webhook that Meta verifies before it sends anything. The setup requires: 1. [Instagram and Meta setup](#instagram-and-meta-setup): Confirm the account is professional, link it to a Facebook Page, add the Instagram product to a Meta app, and collect the account ID, Page Access Token, and App Secret. 2. [Artemis channel configuration](#artemis-channel-configuration) using those values, then copy the generated webhook URL. 3. [Webhook configuration in Meta](#webhook-configuration-in-meta): Verify the callback and subscribe the professional account to the message events. After the configuration is complete, a direct message to the professional account opens a session in Artemis, the bound deployment answers it, and the reply returns to the same Instagram conversation. **Important considerations** * **Instagram messaging runs through a Facebook Page**: The Page issues the access token that authorizes outbound replies, so the Instagram account, the Page, the Meta app, the App Secret, and the webhook subscription all have to belong to one linked set. Values from two apps produce failures that look unrelated to each other. * **Three Meta identifiers compete for one field**: Artemis routes on the Instagram Account ID. The Facebook Page ID, the Meta App ID, and the Instagram-scoped ID that identifies a customer all appear nearby and none of them route. * **Create the connection before the webhook**: Meta's verification challenge carries the Verify Token but no account ID, so Artemis finds the connection by the stored token. An unsaved connection fails verification. * **Development mode limits who can test**: Only assigned app roles and eligible testers can exercise the integration until you complete permission review and switch the app to Live. ## Prerequisites * An Instagram **Business** or **Creator** account. A personal account can't support this integration, and converting it is the first step below. * A Facebook Page linked to that Instagram account, on which your account can perform the messaging and moderate tasks. Meta requires both tasks of whoever generates the Page Access Token. * A Meta developer account with permission to create a business app, add the Instagram product, and reveal the App Secret. * Artemis project access with permission to open **Deployments** -> **Channels**, and a deployed agent in the environment you intend to bind. *** ## How Artemis routes Instagram events Meta sends every event for the account to one generic URL, so Artemis identifies the connection from the payload rather than the path. It reads the Instagram account ID out of the signed event and matches it to a connection. | Piece | Who owns it | What it does | | - | - | - | | **Instagram Account ID** | Meta | Routes inbound events. Artemis reads the account ID from each signed webhook and matches it to this connection. | | **Page Access Token** | Meta | Authorizes outbound replies. Artemis sends to the account's messages endpoint using it as a bearer token, so the token has to belong to the Page linked to this account and carry the Instagram messaging permission. | | **App Secret** | Meta | Verifies the `X-Hub-Signature-256` header on every inbound POST. Artemis computes the HMAC over the unchanged request body and rejects anything that doesn't match. | | **Verify Token** | You | Answers Meta's one-time verification challenge when you save the callback URL. It does nothing after that. | | **Webhook URL** | Artemis | Receives events. The route carries no account ID, because the account ID travels in the payload. | Artemis scopes each conversation to the account and the sender together, so one person messaging two of your professional accounts holds two separate sessions. *** ## Instagram and Meta setup Do this work in Instagram and Meta before you open Artemis. Meta covers the same ground in its [Instagram messaging get started guide](https://developers.facebook.com/docs/messenger-platform/instagram/get-started). 1. Open the Instagram app and go to your profile. 2. Open the menu, then **Settings and privacy**. 3. Find the account type setting and switch the account to **Business** or **Creator**. Instagram messaging works only on a professional account, and the [Instagram Platform overview](https://developers.facebook.com/docs/instagram-platform/overview/) sets out what each account type supports. A personal account can't support this integration at all, so confirm this before you spend time in the App Dashboard. 1. In Instagram, open the professional account settings and find the linked accounts or connected Page setting. 2. Connect the Facebook Page that will issue the Page Access Token. 3. Confirm the link from the Page side as well, so both surfaces agree. The Page is what makes messaging work. Meta routes Instagram conversations through the linked Page, which is why the token you use later belongs to the Page rather than to Instagram. 1. Sign in to [Meta for Developers](https://developers.facebook.com/) and open [My Apps](https://developers.facebook.com/apps/). 2. Create an app, choosing the business messaging use case, or open the app that will own this connection. 3. Add the Instagram product and configure Instagram messaging for the linked Page and account. Keep development and production on separate apps and accounts, because app mode and webhook subscriptions belong to the app. 1. Grant the app, its testers, and any system users the Instagram messaging and Page permissions they need. 2. Complete any permission review Meta requires for your current app mode. 3. Generate a Page Access Token for the linked Page and copy it. Meta requires the `instagram_manage_messages` permission on the token, and requires that whoever requests it can perform the messaging and moderate tasks on the Page. A token generated without both produces a clean-looking connection that fails on the first reply. Token lifetime follows the user token behind it. A short-lived user token yields a Page token good for about an hour. A long-lived user token yields one with no expiry, so use the long-lived path for anything beyond a first test. Artemis routes on the numeric professional account ID, which is neither the `@username` nor the Page ID. Meta surfaces it in the Instagram messaging setup area of the App Dashboard alongside the connected account. You can also query the linked Page for its connected Instagram professional account in the Graph API Explorer, which returns the same numeric ID. Don't substitute the `@username`, the Facebook Page ID, the Meta App ID, a profile URL, or the Instagram-scoped ID. That last one, the IGSID, appears in every inbound event and identifies the customer: Meta issues each person a different scoped ID for each account they message. 1. In the left menu of the App Dashboard, go to **App settings** -> **Basic**. 2. Click **Show** beside **App secret**. 3. Re-enter your Facebook password when Meta asks, then copy the value. Take it from the same Meta app that owns the Instagram webhook. An App Secret from a neighboring app produces a signature failure on every event, which is a confusing failure to trace because nothing else looks wrong. Invent a long random string. Meta doesn't issue this one, so you create it and enter the same value twice: once in Artemis, once in the Meta webhook form. Use a different value per environment, and treat it as a secret rather than a label. It's your shared challenge secret, distinct from both the Page Access Token and the App Secret. While the app sits in development mode, only accounts holding an app role or an eligible Instagram tester role can exercise the integration. Before ordinary people can message the account, complete permission review and switch the app to Live using the control at the top of the App Dashboard. A successful test with a role-holding account proves nothing about public access. *** ## Artemis channel configuration Create the connection before you configure the callback in Meta. Meta's verification request carries the Verify Token but no account ID, so Artemis resolves the challenge from the stored token and needs the connection to exist and be active. 1. Open Artemis and select the project. 2. Go to **Deployments** -> **Channels** -> **Instagram** -> **+ New**. 3. Configure the following fields: | Artemis connection field | Source / Notes | | - | - | | Display Name | The label operators see in the connections list. Name it for the account and the environment it serves, because a project can hold several Instagram connections. Routing ignores this value. | | Instagram Account ID | The routing key. Enter the numeric professional account ID. From the Instagram messaging area of the Meta App Dashboard, or from the linked Page in the Graph API Explorer. | | Page Access Token | Authorizes outbound replies. Use the token generated for the Facebook Page linked to this Instagram account, carrying the Instagram messaging permission. | | App Secret | Verifies the signature Meta puts on each inbound event. From **App settings** -> **Basic** in the same Meta app that owns the webhook. | | Verify Token | The string you invented. Enter the identical value in Meta when you register the callback. | | Environment | The published environment whose active deployment answers incoming messages. Leave the working copy default only while you develop. | | Provider Verification Strength | How far Artemis trusts Meta's identity assertion when it links a person to an existing session. **Weak** suits most deployments. Choose **Strong** only when you trust those assertions enough to base continuity and account-linking decisions on them. | | Initial Status | Whether the connection starts handling messages. Set it to **Active**, because Meta's verification challenge resolves only when the connection is already active. | 4. Click **Create**. 5. Open the connection and copy the **Webhook URL** from the **Overview** tab.
Copy it exactly. The route carries no account identifier, and adding one breaks the callback rather than refining it. ### Validation checklist | Validation item | Expected result | | :- | :- | | Convert the Instagram account | Account shows as Business or Creator and is eligible for Instagram messaging. | | Link the Facebook Page | Instagram account and Page show as linked from both sides, with messaging and moderate tasks available to you. | | Prepare the Meta app | Instagram product added, permissions assigned, and a non-expired Page Access Token generated for the linked Page. | | Collect the routing identifier | Numeric Instagram Account ID copied, distinct from the `@username`, Page ID, and App ID. | | Create the Artemis connection | Connection saved as Active with the account ID, Page Access Token, App Secret, and Verify Token. | | Register the callback | Meta reports the webhook verified, with `messages` and `messaging_postbacks` subscribed and the intended account connected. | | Bind a deployment | The connection's **Deployment** tab shows a runnable active deployment. | | Send a real message | Test direct message reaches the agent and the reply returns once. See [Verify the connection](#verify-the-connection). | *** ## Webhook configuration in Meta Registration has two halves, and completing only the first is the most common way a setup fails silently. Meta verifies the URL whether or not you subscribe to anything. 1. In the same Meta app, open the Instagram webhook configuration. 2. Paste the Artemis **Webhook URL** exactly as shown. 3. Enter the same **Verify Token** you saved in Artemis. 4. Click the control that verifies and saves the callback. Meta sends a subscribe challenge and expects your Verify Token echoed back. Artemis returns the challenge only when the token matches an active Instagram connection. A failure here points at the token, the connection status, or public reachability over HTTPS rather than at the URL itself. Don't append an account ID, a connection ID, a query string, or a trailing segment. Subscribe the Instagram webhook to `messages` and `messaging_postbacks`, then save the subscription. The `messages` field carries text, attachments, and quick-reply payloads, so it's required even for an agent that only handles text. `messaging_postbacks` carries the action callbacks behind buttons and template elements. Confirm the app connects to the same Instagram account, and that the Page subscription includes it. Artemis resolves each event by the account ID in the signed payload, so an app subscribed to a different account delivers events that no connection claims. Meta signs every POST with `X-Hub-Signature-256`, and Artemis computes the HMAC over the exact raw bytes. Any proxy or middleware in front of Artemis has to pass the body through untouched and keep the header. Parsing and reserializing the JSON changes the digest even when the content stays identical, which produces a signature failure that looks like a credential problem. *** ## Verify the connection Artemis doesn't synthesize Meta events, so a real Instagram conversation is the only end-to-end proof. 1. From an account holding an app or Instagram tester role, send the professional account a short direct message you can search for later. 2. Confirm Meta reports a successful delivery and that Artemis opens one session.
Artemis ignores echo events, so your own outbound messages shouldn't create extra turns. 3. Check that a single reply reaches the original sender in the same conversation.
A send failure here usually means the Page Access Token is wrong or expired, the account linkage broke, the messaging permission is missing, or the conversation has fallen outside the messaging window. 4. Trigger an authored quick reply and a template action, and confirm each resumes the same session rather than starting a new one. 5. Send an image, video, audio file, and document, and confirm the agent handles the downloaded attachment.
Artemis downloads signed Meta CDN URLs promptly because they expire, so a delay in your own processing isn't the same as a delivery failure. 6. Once Meta approves the app and you switch it to Live, repeat the test from an account with no role. This is the only check that proves ordinary people can reach the agent. *** ## Instagram channel behavior and limits Meta enforces these boundaries, and Artemis applies its own download policy on inbound media. Design your agent's responses to fit them. | Area | Limit | | - | - | | Quick replies | Buttons and selects become quick replies, capped at 13 options with visible titles truncated to 20 characters. | | Carousels | Generic-template rich content supports at most 10 elements, each carrying at most 3 buttons. | | Unsupported inputs | Artemis ignores share, story mention, like-heart, reel, sticker, and fallback-only attachments rather than turning them into blank agent turns. | | Media downloads | Image, video, audio, and file downloads accept only recognized Meta and Instagram CDN hosts, time out after 30 seconds, and cap at 100 MB streaming. | | Messaging window | Meta can reject outbound delivery outside the Instagram messaging window, which runs 24 hours from the person's message. A human agent tag extends that to 7 days for approved use cases. | | Rate limits | Meta allows roughly 100 calls per second per account for text messages, and roughly 10 per second for audio or video. | *** ## Secure the connection Instagram authentication runs in one direction only, which is easy to misread. Meta signs what it sends you; your Verify Token does nothing after setup. Every POST requires `X-Hub-Signature-256` generated with the matching App Secret, and Artemis rejects a missing or invalid signature before it queues anything. Signature validation covers the exact raw bytes, so preserve the request body, its content encoding, the header, and the external scheme through your ingress. Keep the three secrets distinct in your own operational notes. The Verify Token protects only the one-time challenge, and you rotate it in both Artemis and Meta together. The App Secret authenticates inbound traffic. The Page Access Token authorizes outbound messages, so encrypt it, redact it from logs, grant least privilege, monitor its expiry, and never expose it to browser code. Use separate Meta apps and accounts for development and production, or at minimum isolated credentials, tokens, webhook subscriptions, and connections. *** ## Manage the channel connection Opening a connection shows four tabs. The delete control sits beside the connection name. **Connection Summary** reports the connection's state and identity. | Field | Description | | - | - | | **Status** | Whether the connection currently handles messages. | | **Created** and **Updated** | Date and time of creation, and of the last change. | | **Environment** | The environment whose deployment answers messages. | | **Source Type** | Reads `Channel Connection`. | | **Source ID** | Identifies the connection uniquely. Quote it when you raise a support ticket. | **Setup Instructions** shows the **Webhook URL** to register in Meta and the **Instagram Account ID** this connection matches events to, each with a copy control. Check the account ID here first when messages arrive and no session appears. Artemis encrypts credentials after you save them, so each field reads as saved rather than showing its value. Leave a field empty to keep the current value, or enter a new one to replace it. | Field | Description | | - | - | | **Use Auth Profile** | Draws the credentials from a saved auth profile instead of the fields below. Prefer it when the same credentials serve more than one connection, since a rotation then touches one profile. | | **Page Access Token** | Replace it here when you generate a new token in Meta. The account ID stays the same, so you don't need a new connection. | | **App Secret** | Replace it here if you move the Instagram product to a different Meta app. | | **Verify Token** | Changing this breaks Meta's webhook until you enter the matching value in Meta as well. | Click **Save Credentials** to apply your changes. Under **General**, rename the connection with **Display Name**, and revisit **Provider Verification Strength** if your trust posture for this account changes. Under **Instagram Settings**, **Instagram Account ID** appears read-only. Artemis routes inbound events by this value, so changing it would orphan the connection. Create a new connection to serve a different account. **Authorization Message** controls what the person sees when a tool needs them to authorize something before the conversation continues. Write the template with these variables. | Scope | Variables | Use | | - | - | - | | Message | `{{total}}`, `{{completed}}`, `{{remaining}}` | Summarize progress across every pending authorization, typically in a lead-in line above the list. | | Repeat block | `{{#authorizations}}` and `{{/authorizations}}` | Wrap the section Artemis repeats once per pending authorization. | | Item | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Identify and link each authorization inside the repeat block. | Item variables resolve only inside the repeat block. Use the preview to check the wording, and remember it fills from live pending authorizations at runtime. Click **Save Changes** to apply your changes. Choose which deployed version of the agent answers messages on this connection. Under **Agent Version**, set **Environment** to the environment you want the connection to follow. **Active Deployment** then shows the version currently serving traffic and when someone deployed it. Check this pair when the channel connects but the agent behaves like an older build. The connection follows whatever the environment currently has deployed, not the version you last edited. Click **Save Changes** after you switch the environment. *** ## Troubleshoot connection issues | Symptom | Likely cause | Recommended action | | - | - | - | | Meta can't verify the callback | The connection doesn't exist yet, the tokens differ, or the endpoint isn't publicly reachable | Create the connection and set it to Active first, use the exact saved Verify Token in both systems, expose the runtime over public HTTPS, and keep the generic Instagram webhook path. | | Inbound requests return 401 | The App Secret belongs to a different app, or ingress altered the signed request | Confirm the App Secret comes from the app that owns the webhook, preserve `X-Hub-Signature-256`, and stop proxies from parsing and reserializing the JSON body. | | Artemis reports no connection found | The account ID in the payload doesn't match the saved value | Compare the account ID in the signed payload with the **Instagram Account ID** saved in Artemis, and confirm the connection is Active. A Page ID or an IGSID in this field produces exactly this result. | | Verification succeeded but no messages arrive | The subscription is missing, or the sender isn't eligible in the current app mode | Subscribe `messages` and `messaging_postbacks`, connect the intended professional account, inspect Meta's delivery log, and confirm the sender holds a role while the app is in Development. | | Inbound works but outbound fails | The Page Access Token is wrong, expired, or under-permissioned, or the window closed | Verify the token, the account linkage, the Instagram messaging permission, the token expiry, the messaging window, and reachability of the Graph API. | | Text works but actions don't | `messaging_postbacks` isn't subscribed, or the payload exceeds channel limits | Subscribe the field, then keep authored quick replies and template payloads within the limits above. | | Text works but attachments fail | The media type, host, size, or signed URL falls outside policy | Use image, video, audio, or file, confirm the signed URL hasn't expired, confirm the host is a recognized Meta CDN, keep the download within 100 MB, and confirm your attachment policy accepts it. | | Meta rejects replies after a delay | The conversation fell outside the messaging window | Reproduce inside an active conversation, then review Meta's current Instagram messaging window and approved use cases. | *** ## Meta reference Use these Meta pages when you need detail beyond what this article covers. | Resource | Use it for | | - | - | | [Instagram Platform overview](https://developers.facebook.com/docs/instagram-platform/overview/) | Account eligibility, how the pieces fit together, and rate limits. | | [Instagram messaging get started](https://developers.facebook.com/docs/messenger-platform/instagram/get-started) | Meta's own walkthrough of app setup, permissions, and Page token generation. | | [Instagram messaging overview](https://developers.facebook.com/docs/instagram-messaging/overview) | Conversation entry points, supported message types, and the inbox model. | | [Send a message](https://developers.facebook.com/docs/messenger-platform/instagram/features/send-message) | Outbound message formats, media limits, and the messaging window rules. | | [My Apps](https://developers.facebook.com/apps/) | Returning to an app you created, or checking which app owns a subscription. | | [Meta Business Suite](https://business.facebook.com/) | Page roles, linked accounts, and business verification. | **Related articles:** * [Set up Messenger](/agent-platform/channels/messenger) * [Set up WhatsApp](/agent-platform/channels/whatsapp) * [Set up LINE](/agent-platform/channels/line) # Kore Agent Assist Source: https://koreai.mintlify.app/agent-platform/channels/kore-agent-assist The Kore Agent Assist channel connects an Artemis project to Kore Agent AI, so your deployed agents give contact center agents real-time guidance. Artemis creates the connection, generates its API key, binds it to an environment, and controls traffic through **Pause** and **Resume**. Kore Agent AI keeps calling the existing Agent Assist API, so the external endpoint contract doesn't change. The setup requires: 1. [Create the connection in Artemis](#create-the-connection-in-artemis) 2. [Configure Kore Agent AI](#configure-kore-agent-ai) 3. [Verify the connection](#verify-the-connection) After setup, Kore Agent AI routes each Agent Assist session to the agent version active in the environment you select. **Important considerations** * **The API key appears only once.** Artemis shows the full key only in the creation dialog. Afterward, only the key prefix stays visible. Copy the key before you select **Done**. * **Pause and Resume control traffic.** The connection has no separate on or off setting. A paused or inactive connection returns `404` to external requests. * **Use one connection per environment.** Keep development, staging, and production on separate connections, each with its own key. * **The key has a narrow scope.** It covers message sending for the owning project only and doesn't grant platform administration access. * **Output supports text and markdown.** Responses can also carry normalized renderables, actions, and activity events. The channel doesn't accept attachments or deliver channel-native rich media. ## Prerequisites Confirm the following before you start. * An Artemis project with an agent version deployed to the target environment. The connection's **Deployment** tab lists it as the **Active Deployment**. * Access to **Deployments** in the Artemis project. * Administrator access to Kore Agent AI, including the **Configuration** section. * A secure location, such as a secrets manager, to store the API key. *** ## Create the connection in Artemis Create the connection first. Artemis generates the four values that Kore Agent AI needs. 1. In your project, go to **Deployments** -> **Channels**. 2. Select **Kore Agent Assist**. 3. Select **Create Connection**. If the project already has connections, select **New**. 1. In **Display Name**, enter a name that identifies the Agent Assist experience and its environment. The name supports up to 255 characters. 2. In **Environment**, select the environment whose active deployment supplies the entry agent, welcome message, model, and runtime behavior. 3. In **Initial Status**, select **Active** to accept traffic immediately, or select **Inactive** to save the connection and activate it later. 4. Select **Create**. The **Kore Agent Assist Connection Created** dialog shows four values. 1. Copy **Domain URL**, **Environment**, **App ID**, and **API Key**. 2. Store the API key in your secrets manager. 3. Select **Done**. After you close the dialog, Artemis can't show the full API key again. If you lose the key, rotate it from the **Overview** tab. ### Connection values Each value maps to a field of the same name in Kore Agent AI. Copy each one exactly, including case and punctuation. | Value | Source / Notes | | - | - | | **Domain URL** | The Artemis host that Kore Agent AI calls. Agent AI appends the Agent Assist API route, so don't paste an individual endpoint path. | | **Environment** | The environment identifier. It can differ from the label in the **Environment** list, so use the value from the dialog. | | **App ID** | The project ID for the connection. Don't substitute the display name, an agent name, or the **Source ID** from the **Overview** tab. | | **API Key** | A generated secret with the `abl_` prefix. Agent AI sends it in the `x-api-key` header. Keep it out of browser code, transcripts, client logs, and source control. | *** ## Configure Kore Agent AI Add the connection as an Agentic Copilot app in Kore Agent AI. For the complete Agent AI procedure, see [Linked Services](https://docs.kore.ai/agentassist/settings/linked-services/). 1. Sign in to [Kore Agent AI](https://agentassist.kore.ai/koreagentassist/). 2. Go to **Configuration** -> **System Setup** -> **Linked Services**. 3. Select **Agentic Configurations**. 4. Select **+ Add Agentic Copilot App**. Under **App Information**, enter the values from the Artemis connection. 1. In **App Name**, enter a name that agents and administrators recognize. The name supports up to 50 characters. 2. In **Domain URL**, paste the Artemis **Domain URL**. 3. In **Environment**, paste the Artemis **Environment** value. 4. In **App ID**, paste the Artemis **App ID**. 5. In **API Key**, paste the full API key, not the prefix. Select **Test Connection & Save**. Agent AI checks the values and saves the app when the connection succeeds. Next, link the app to your Agent AI experience so agents receive guidance at runtime. For details, see [Agentic Copilot](https://docs.kore.ai/agentassist/uncategorized/agentic-copilot/). ### Validation checklist Review these items before you route live conversations to the connection. * The Artemis connection shows **Active** status. * The selected environment has an **Active Deployment**. * Agent AI holds the exact **Domain URL**, **Environment**, and **App ID** values. * Agent AI holds the current, full API key. * **Test Connection & Save** succeeds in Agent AI. * Every action on the Artemis **Testing** tab returns a successful result. *** ## Verify the connection The **Testing** tab calls the live Agent Assist API with the connection's saved values. Run it before you add the values to Agent AI, and again after any key rotation. 1. Open the connection, and select the **Testing** tab. 2. Under **Connection**, confirm the **Domain URL**, **Environment**, and **App ID**. 3. In **API Key**, paste the full key you stored at creation. The test actions stay unavailable until you paste a key. Artemis generates a **sessionReference** for the test session. Run the actions in order so every call uses the same session. 1. Select **Test connection** to check authentication and deployment readiness. 2. Select **Connect (welcome)** to open the session and receive the agent's welcome message. 3. In **Message**, enter a test message, and select **Send message**. 4. Turn on **Stream response (SSE)**, and send another message to check streaming delivery. 5. Select **Terminate** to end the session. **Results** lists the request, status, duration, and response for each action. To start over with a new session reference, select **New session**. ### Test actions Each test action calls one Agent Assist API endpoint. | Action | Endpoint | What it confirms | | - | - | - | | **Test connection** | `/test` | The key authenticates, the App ID and environment resolve, and the deployment, entry agent, and model are ready. The call doesn't create a conversation or call the model. | | **Connect (welcome)** | `/sessions` | The API creates the session and returns the authored welcome message. | | **Send message** | `/runs/execute` | The agent processes a turn and returns a response. With **Stream response (SSE)** on, the response arrives as a Server-Sent Events stream. | | **Terminate** | `/sessions/terminate` | The API ends the session and returns a termination response. | ### Verify pause and resume Confirm that the connection status controls all external traffic. 1. In the connection header, select **Pause**. 2. On the **Testing** tab, select **Test connection**, and confirm the request returns `404`. 3. Select **Resume**, and run **Test connection** again. *** ## Manage the connection Open a connection from **Deployments** -> **Channels** -> **Kore Agent Assist**. The header shows the display name, environment, and status, plus the **Pause** and delete controls. | Tab | Use it to | | - | - | | **Overview** | Review the **Connection Summary**, copy the Agent AI values, and rotate the API key. | | **Configuration** | Change the **Display Name**, and then select **Save Changes**. | | **Deployment** | Change the environment that handles incoming messages, and review the **Active Deployment** version and deployment time. | | **Testing** | Run test actions through the live API. | ### Connection summary The **Overview** tab lists the connection's metadata under **Connection Summary**. | Field | Description | | - | - | | **Status** | Whether the connection accepts external traffic. | | **Created** | When you created the connection. | | **Updated** | When the connection last changed. | | **Environment** | The environment identifier the connection follows. | | **Source Type** | The record type. For this channel, the value is **Channel Connection**. | | **Source ID** | The internal ID of the connection record. Don't use it as the App ID. | ### Change the environment Changing the environment routes incoming messages to that environment's active deployment. 1. Select the **Deployment** tab. 2. In **Environment**, select the environment. 3. Select **Save Changes**. 4. On the **Overview** tab, check the **Environment** value, and update Agent AI if it differs. ### Rotate the API key Rotate the key when you lose it or suspect exposure. 1. On the **Overview** tab, select **Rotate** next to **API Key**. 2. Copy the new key. Artemis shows it only once. 3. In Agent AI, replace the **API Key** value, and select **Test Connection & Save**. Artemis revokes the previous key when it saves the replacement. Agent AI requests fail until you update the key. ### Pause, resume, or delete Use the header controls to change the connection's availability. * Select **Pause** to stop external traffic. Requests return `404` until you select **Resume**. * Select the delete icon to remove the connection. Before you delete it, remove or update the matching app in Agent AI. *** ## API reference Kore Agent AI calls these endpoints on your behalf. Use this section to build a custom integration or to diagnose failed requests. ### Base path and authentication All endpoints share one base path built from the connection values. ```text theme={null} {Domain URL}/api/v2/apps/{App ID}/environments/{Environment} ``` Every request sends `Content-Type: application/json` and the full API key in the `x-api-key` header. Don't send an `Authorization: Bearer` header, a user token, an SDK public key, or the key prefix. The API checks that the key's tenant and project match the resolved App ID and environment, and returns `404` on any mismatch. ### Session lifecycle Each Agent Assist conversation uses the same session identity across all calls. | Step | Endpoint | Details | | - | - | - | | Start | `POST /sessions` | Send `sessionIdentity` before the first turn. Set `metadata.isSendWelcomeMessage` to `true` to receive the welcome message. | | Send turns | `POST /runs/execute` | Send `sessionIdentity` and `input[]`. Reuse the same `sessionReference` for later turns. | | End | `POST /sessions/terminate` | Send the same session identity when the conversation ends. The API returns a termination response even if the session no longer exists. | | Check readiness | `/test` | Checks authentication and deployment readiness without creating a conversation or calling the model. | ### Response modes `/runs/execute` supports three response modes. The request fields decide which mode applies. | Mode | How to request it | Behavior | | - | - | - | | Synchronous (default) | Leave streaming off, and omit `callbackUrl`. | Returns one completed response on the same HTTP connection. | | Streaming (SSE) | Set `stream.enable` to `true`, and accept `text/event-stream`. | Streams the response with heartbeats, then sends the final session and run details. Streaming takes precedence over `isAsync`. | | Async push | Set `isAsync` to `true`, and include `callbackUrl`. | Validates the callback, returns `202` with a processing state, and later delivers the completed or error response to the callback. | If you set `isAsync` to `true` without a `callbackUrl`, the API falls back to the synchronous response. For async push, use an approved HTTPS destination, and avoid private and link-local addresses. When callback signing is on, verify the `X-ABL-Signature` header, which signs `timestamp.rawBody`. ### Payload rules The API normalizes and restricts request content before agent execution. | Element | Rule | | - | - | | `sessionIdentity` | Accepts `sessionReference`, `sessionId`, `sessionIdentity`, and `userReference` entries. Include at least one stable `sessionReference`, `sessionId`, or `userReference`, and keep it unchanged across calls. | | `input[]` | Requires at least one item and supports `text`, `object`, and `tool_input` types. Agent execution needs a `text` item. Each content value allows up to 16,000 characters. | | `metadata.aa_uamsgs` | Carries Agent Assist conversation history. The API keeps up to 50 messages. | | Other `metadata` | The API accepts custom keys but strips reserved transport, credential, tenant, project, session, and authorization keys. | | Output | Text and markdown, plus optional normalized renderables, actions, and activity events. No attachments or channel-native rich media. | *** ## Troubleshoot connection issues Match the symptom to its likely cause, and apply the recommended action. | Symptom | Likely cause | Recommended action | | - | - | - | | `401 Unauthorized` | The request carries a missing, partial, or revoked key. | Paste the full current `abl_` key, and confirm every request sends `x-api-key`. | | `404` app not found | The App ID or Environment doesn't match, the key belongs to another project, or the connection is paused or inactive. | Copy the values again from the **Overview** tab, and confirm the connection shows **Active**. | | **Test Connection & Save** fails in Agent AI | One or more pasted values differ from the Artemis connection. | Recopy all four values with the copy icons on the **Overview** tab. | | `/test` reports a deployment resolution failure | The environment has no active deployment, or its entry agent or model configuration isn't valid. | Deploy an agent version to the environment, and check the entry agent and model settings. | | Streaming returns JSON or stalls | The request doesn't set `stream.enable`, doesn't accept `text/event-stream`, or a proxy buffers the stream. | Set `stream.enable` to `true`, accept `text/event-stream`, turn off proxy buffering, and let heartbeats pass for the full request timeout. | | Async returns an invalid callback error or never delivers | The callback URL isn't an approved HTTPS destination, or signature checks fail. | Use an approved HTTPS URL outside private and link-local ranges, and verify `X-ABL-Signature` when signing is on. | | Welcome and follow-up turns land in different sessions | The session identity changes between calls. | Reuse the same `sessionReference`, and keep identity values and their order unchanged between `/sessions` and `/runs/execute`. | | Test actions stay unavailable | The **Testing** tab has no API key. | Paste the full key, or rotate it from the **Overview** tab and paste the new one. | ## Related topics Use these pages to continue channel and deployment setup. * [Channels](/agent-platform/channels) * [Deployment](/agent-platform/deployment) * [Linked Services in Kore Agent AI](https://docs.kore.ai/agentassist/settings/linked-services/) # Set up LINE Source: https://koreai.mintlify.app/agent-platform/channels/line [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital) Connect a LINE Official Account to your agent so people reach it in LINE for chat and media messaging. You create the Official Account, enable the Messaging API on it, collect two credentials and one identifier, then register a webhook and verify it before any real traffic arrives. The setup requires: 1. [LINE setup](#line-setup): Create the Official Account, enable the Messaging API, and collect the Channel Secret, Channel Access Token, and Destination ID. 2. [Artemis channel configuration](#artemis-channel-configuration) using those three values, then copy the generated webhook URL. 3. [Webhook configuration in LINE](#webhook-configuration-in-line): Register the webhook URL, verify it, and switch delivery on. After the configuration is complete, LINE posts every event for the channel to Artemis, which resolves the connection from the payload and hands the message to the deployed agent. **Important considerations** * **Two consoles, two halves of the job**: LINE splits the work between LINE Official Account Manager and the LINE Developers Console. Sign in to both with the same account. The Developers Console asks first-time users to register a developer profile with a name and email, and an account that skips that step can't see the channel it just created. * **Official Account Manager creates the channel, not the Developers Console**: LINE removed the ability to create Messaging API channels directly in the Developers Console in September 2024. Enabling the Messaging API on the Official Account creates the channel. * **Provider selection is permanent**: You can't change or remove the provider afterwards. A person also receives a different user ID under each provider, so moving an account later would break every stored user reference. * **The Destination ID is not shown in either console**: It comes from an API call. Three identifiers that are shown look plausible and none of them route. See [How Artemis routes LINE events](#how-artemis-routes-line-events). | Console | What you do there | | - | - | | [LINE Official Account Manager](https://manager.line.biz/) | Create the Official Account, enable the Messaging API, and turn off LINE's automatic replies. | | [LINE Developers Console](https://developers.line.biz/console/) | Copy the Channel Secret and access token, set the webhook URL, and find the friend-add QR code. | ## Prerequisites * A LINE Business ID, registered with an existing personal LINE account or with an email address. See [LINE setup](#line-setup). * The admin role on the Official Account and on the Messaging API channel. Several steps hide their controls from lower roles rather than reporting a permission error. * The admin role on the provider you intend to use, if you plan to select an existing one. A provider only appears in the selection list when your account holds that role on it. * Artemis project access with permission to open **Deployments** -> **Channels**, and a deployed agent in the environment you intend to bind. *** ## How Artemis routes LINE events LINE sends every event for a channel to one URL, so Artemis identifies the connection from the payload rather than the path. Each webhook body carries a `destination` field holding the bot's user ID, and Artemis resolves the connection from that value before it verifies the signature. | Piece | Who owns it | What it does | | - | - | - | | **Destination ID** | LINE | Routes inbound events. It has to be the bot's user ID, which begins with `U`. | | **Channel Secret** | LINE | Verifies the `X-Line-Signature` header. Artemis computes HMAC-SHA256 over the unchanged request body and compares the result. | | **Channel Access Token** | LINE | Authorizes outbound calls: replies, push messages, bot info lookups, and the loading indicator. | | **Webhook URL** | Artemis | Receives events. The route carries no Destination ID, because the destination travels in the signed body. | Three LINE identifiers look plausible in the Destination ID field and none of them route. The **Channel ID** on the Basic settings tab is numeric. The **Bot basic ID** on the Messaging API tab starts with `@`. **Your user ID**, also on the Basic settings tab, is your own personal LINE account, not the bot's. The correct value comes from an API call, covered in [LINE setup](#line-setup). *** ## LINE setup Complete all of this in LINE before you open Artemis. Start at the beginning even if someone has already created an Official Account, because enabling the Messaging API is what creates the channel you'll configure. LINE accounts for business sit behind a Business ID, which you can register with an existing personal LINE account or with an email address. 1. Go to [account.line.biz](https://account.line.biz/signup?redirectUri=https://entry.line.biz/form/entry/unverified) and register a Business ID, if your organization doesn't already have one. 2. Use an account your team can keep. This identity carries the admin role for everything that follows. 1. Complete LINE's [Official Account entry form](https://entry.line.biz/form/entry/unverified) with your business details. Submitting it creates the account. 2. Open [LINE Official Account Manager](https://manager.line.biz/) and confirm the new account appears in your list before you continue. 1. In Official Account Manager, select the account. 2. Go to **Settings** -> **Messaging API** and enable the Messaging API. This is what creates the channel that Artemis connects to. You can't do this anywhere else. LINE removed the ability to create Messaging API channels directly in the Developers Console in September 2024. Starting from the developer side finds nothing to create. LINE asks you to pick a provider, which is the organization that owns the channel. Two constraints make this a decision rather than a formality. You can't change or remove the provider afterwards. A person also receives a different user ID under each provider, so moving an account later would break every stored user reference. An existing provider only appears in the list when your account holds the admin role on it. A provider that seems to be missing is usually a permissions problem, not an absent one. Pick the provider that owns your related channels, and avoid putting unrelated services under one provider for convenience. A new channel arrives with greeting messages and auto-reply messages enabled, which means a person gets one LINE-managed response and one agent response to the same message. Reach the settings from either console: 1. In Official Account Manager, open **Settings** -> **Response settings**. 2. Or in the Developers Console, open the channel's **Messaging API** tab, where **Greeting messages** and **Auto-reply messages** each carry an **Edit** link that opens Official Account Manager at the right place. LINE recommends disabling both for a first Messaging API bot, and explains why under [settings on LINE Official Account Manager](https://developers.line.biz/en/docs/messaging-api/building-bot/#line-manager-settings). Disable both when the agent should own every reply. Keeping a greeting message is reasonable if you author it deliberately, but leaving the defaults on reads to users as a broken bot rather than as a settings problem. All three credential values live in the Developers Console, spread across two tabs of the same channel. 1. Sign in at [developers.line.biz/console](https://developers.line.biz/console/). 2. Select your provider, then select the Messaging API channel. 3. Open the **Basic settings** tab and copy **Channel secret**. You need the admin role on the channel to see it. Take care not to grab the **Channel ID** or **Your user ID** from the same screen. Both sit nearby, both look like credentials, and neither works. Open the **Messaging API** tab. LINE offers four token types, and this screen issues only one of them. | Token type | Validity | Limit per channel | | - | - | - | | User-specified expiration (v2.1) | Up to 30 days | 30 | | Stateless | 15 minutes | No limit | | Short-lived | 30 days | 30 | | Long-lived | Until you revoke it | 1 | The long-lived token is the one with an **Issue** control on this tab. The other three come from API calls rather than the console, so choosing them means building issuance into your own tooling. LINE recommends the v2.1 token, and that recommendation costs you a rotation schedule. Pick the long-lived token if you'd rather not build that, and accept that a single indefinite credential carries more risk. Whichever you choose, copy the value straight into your secret manager. Reissuing a long-lived token normally kills the previous one immediately. LINE lets you extend the outgoing token's validity by up to 24 hours when you reissue, which gives you a window to update Artemis without dropping messages. Use it. The console never shows this value, so you call for it. 1. Use LINE's [Get bot info](https://developers.line.biz/en/reference/messaging-api/#get-bot-info) endpoint with the channel access token you just issued. ```bash theme={null} curl -v -X GET https://api.line.me/v2/bot/info \ -H "Authorization: Bearer {channel access token}" ``` 2. Copy the `userId` from the response. The value begins with `U` and matches the `destination` field LINE puts in every webhook for this channel. Run this from a secure terminal rather than a shared one, since the command carries your token. While you're on the **Messaging API** tab, find the QR code or add-friend link. You'll need it to test, and it's easier to grab now than to come back for. *** ## Artemis channel configuration Create the connection before you verify the webhook in LINE. LINE's verification request has to resolve to an active connection and authenticate with that connection's Channel Secret, so an unsaved connection fails verification. 1. Open Artemis and select the project. 2. Go to **Deployments** -> **Channels** -> **LINE** -> **+ New**. The **New LINE Connection** dialog opens. 3. Configure the following fields: | Artemis connection field | Source / Notes | What it controls | | - | - | - | | Display Name | You choose it | The label operators see in the connections list. Name it for the Official Account and the environment it serves, because a project can hold several LINE connections. Routing ignores this value. | | Destination ID | The `userId` from Get bot info. See [LINE setup](#line-setup). | The routing key. It begins with `U`. | | Channel Access Token | Developers Console > channel > **Messaging API** tab | The credential for every outbound call Artemis makes to LINE. | | Channel Secret | Developers Console > channel > **Basic settings** tab | The key Artemis uses to verify the signature on each inbound event. | | Environment | Your published environments | The environment whose active deployment answers incoming messages. Leave the working copy default only while you develop. | | Provider Verification Strength | Defaults to **Weak** | How far Artemis trusts LINE's identity assertion when it links a person to an existing session. **Weak** suits most deployments. Choose **Strong** only when you trust those assertions enough to base continuity and account-linking decisions on them. | | Initial Status | Defaults to **Active** | Whether the connection starts handling messages. Set it to **Active**, because LINE's verification resolves only when the connection is already active. | 4. Click **Create**. 5. Open the connection and copy the **Webhook URL** from the **Overview** tab. You need it for the next section. *** ## Webhook configuration in LINE LINE separates saving the URL from switching delivery on, so a verified webhook can still deliver nothing. Complete all three parts in the Developers Console, on the channel's **Messaging API** tab. 1. Find **Webhook settings**. 2. Click **Edit** under **Webhook URL**. 3. Paste the Artemis endpoint exactly as shown and click **Update**. LINE accepts one endpoint per channel. Don't append the Destination ID, the Channel ID, a project ID, or any connection identifier, because Artemis routes from the signed body rather than the path. LINE also requires HTTPS with a certificate from a widely trusted authority. Self-signed certificates fail, and so does a server missing its intermediate certificates, which is a common cause of a verification failure that looks like a LINE problem. Click **Verify**. LINE posts an event-free payload to the endpoint, and Artemis responds once it has resolved the connection and verified the signature. LINE reports **Success** when this works. Treat anything else as a stop. A timeout, a non-200 response, a TLS error, and a signature failure each point somewhere different, and continuing past one means debugging later with live users watching. Turn on **Use webhook**. This is the step people miss. Saving a verified URL without this toggle leaves the channel looking correctly configured while message events never reach Artemis. Enable redelivery only when your operations policy requires retries after a non-2xx response. LINE can deliver the same webhook more than once when redelivery is on, and it can change event order during redelivery. Your agent logic has to tolerate both, so rely on event IDs rather than arrival order before you turn this on. The channel's **Security** tab accepts a list of IP addresses, individually or in CIDR notation, that may call the LINE Platform with a long-lived token. This narrows the blast radius of an indefinite credential considerably, so it's worth doing whenever you pick that token type. LINE covers the setting under [restrict who can call the API](https://developers.line.biz/en/docs/messaging-api/building-bot/#configure-security-settings). It doesn't apply to the other three. ### Validation checklist | Validation item | Expected result | | :- | :- | | Complete LINE setup | Official Account created, Messaging API enabled, and greeting and auto-reply messages disabled. | | Collect credentials | Channel Secret, Channel Access Token, and the `U`-prefixed Destination ID copied from the matching channel. | | Create the Artemis connection | Connection saved as **Active** with the Destination ID, both credentials, and the intended environment. | | Register the webhook URL | LINE reports **Success** when you click **Verify**. | | Enable delivery | **Use webhook** shows as turned on. | | Send a live message | A real LINE conversation produces exactly one agent reply. See [Verify the connection](#verify-the-connection). | ## Verify the connection Artemis has no synthetic test for this channel, so a real conversation is the only end-to-end proof. 1. Add the Official Account as a friend by scanning the QR code or using the add-friend link on the **Messaging API** tab. Test from an ordinary LINE account rather than an identity that exists only as a console administrator, because the two behave differently. 2. Send a short message you can search for later, and confirm the agent replies in the same chat. 3. Count the replies. Two responses to one message means greeting messages or auto-reply messages are still enabled, not that the agent ran twice. 4. Send each input type your agent supports, and confirm its authored behavior for each. Receiving a media event isn't the same as handling it, so test the authored path rather than only the delivery. 5. Test groups only if you need them. Enable **Allow bot to join group chats** in Official Account Manager, invite the bot, then message or mention it. Confirm sessions stay scoped to the LINE source, so a group conversation and a one-to-one conversation with the same person don't merge. *** ## Secure the connection Both LINE credentials are secrets, and they do opposite jobs. The Channel Secret authenticates what comes in; the Channel Access Token authorizes what goes out. Artemis computes HMAC-SHA256 over the unchanged raw request body and compares it with the `X-Line-Signature` header. Any proxy or middleware in front of Artemis has to leave the signed body untouched, because a single changed byte fails verification as surely as a wrong secret does. Rotation invalidates immediately on both credentials. Reissuing a Channel Secret kills the old secret, and reissuing a long-lived access token kills the previous token unless you use the 24-hour extension. Coordinate the Artemis update with the provider-side change rather than doing them hours apart. Revoke any token you suspect someone has copied, because a compromised token can broadcast to every friend of the Official Account. Keep both values out of browser code, screenshots, tickets, and source control, and update Artemis the moment LINE reissues or revokes either one. Use distinct providers or channels for development and production, or at minimum distinct credentials and connections. Pointing two environments at one webhook mixes test traffic into real conversation history. *** ## Manage the channel connection Opening a connection shows four tabs. The delete control sits beside the connection name. **Connection Summary** reports the connection's state and identity. | Field | Description | | - | - | | **Status** | Whether the connection currently handles messages. | | **Created** and **Updated** | Date and time of creation, and of the last change. | | **Environment** | The environment whose deployment answers messages. | | **Source Type** | Reads `Channel Connection`. | | **Source ID** | Identifies the connection uniquely. Quote it when you raise a support ticket. | **Setup Instructions** shows the **Webhook URL** to register in the Developers Console and the **Destination ID** this connection matches events to, each with a copy control. Check the Destination ID here first when messages arrive and no session appears. Artemis encrypts credentials after you save them, so each field reads as saved rather than showing its value. Leave a field empty to keep the current value, or enter a new one to replace it. | Field | Description | | - | - | | **Use Auth Profile** | Draws the credentials from a saved auth profile instead of the fields below. Prefer it when the same credentials serve more than one connection, since a rotation then touches one profile. | | **Channel Access Token** | Replace it here whenever you reissue the token in the Developers Console, and do it promptly, because the old token stops working at once. | | **Channel Secret** | Replace it here if another administrator reissues the secret. Until you do, every inbound event fails signature verification. | Click **Save Credentials** to apply your changes. Under **General**, rename the connection with **Display Name**, and revisit **Provider Verification Strength** if your trust posture for this Official Account changes. Under **LINE Settings**, **Destination ID** appears read-only. Artemis routes inbound events by this value, so changing it would orphan the connection. Create a new connection to serve a different Official Account. **Authorization Message** controls what the person sees when a tool needs them to authorize something before the conversation continues. Write the template with these variables. | Scope | Variables | Use | | - | - | - | | Message | `{{total}}`, `{{completed}}`, `{{remaining}}` | Summarize progress across every pending authorization, typically in a lead-in line above the list. | | Repeat block | `{{#authorizations}}` and `{{/authorizations}}` | Wrap the section Artemis repeats once per pending authorization. | | Item | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Identify and link each authorization inside the repeat block. | Item variables resolve only inside the repeat block. Use the preview to check the wording, and remember it fills from live pending authorizations at runtime. Click **Save Changes** to apply your changes. Choose which deployed version of the agent answers messages on this connection. Under **Agent Version**, set **Environment** to the environment you want the connection to follow. **Active Deployment** then shows the version currently serving traffic and when someone deployed it. Check this pair when the channel connects but the agent behaves like an older build. The connection follows whatever the environment currently has deployed, not the version you last edited. Click **Save Changes** after you switch the environment. *** ## Troubleshoot connection issues | Symptom | Likely cause | Recommended action | | - | - | - | | Verify doesn't report success | The connection isn't active, the endpoint isn't reachable, or the certificate chain is incomplete | Confirm the connection exists and is active, that the Artemis endpoint is reachable over public HTTPS with a certificate from a trusted authority and a complete chain, that the URL carries no extra path segment, and that LINE receives a 200 response. | | Artemis reports no connection found | The Destination ID holds the wrong identifier | Replace it with the `U`-prefixed `userId` from Get bot info. The Channel ID, the Bot basic ID, and personal user IDs don't route. | | Signature verification fails | The Channel Secret doesn't match, or a proxy rewrote the body | Copy the Channel Secret from the matching channel's **Basic settings**, check whether another administrator reissued it, and confirm no proxy or middleware rewrites the raw body. | | Verification succeeded but no messages arrive | **Use webhook** is off | Turn on **Use webhook**. A saved and verified URL delivers nothing until you do. | | Messages arrive but the agent doesn't reply | The access token expired, someone revoked it, or no deployment can run | Confirm the Channel Access Token is current, the connection is active, and the bound environment has a runnable deployment. A v2.1 token past its 30-day ceiling produces exactly this symptom. | | People receive two replies | LINE's own automatic replies are still enabled | Disable greeting messages and auto-reply messages, or author them so they don't overlap with the agent. | | Direct messages work but groups don't | The bot can't join group chats, or it joined before you changed the setting | Enable **Allow bot to join group chats**, reinvite the bot if you changed the setting after inviting it, and confirm the test event comes from the expected group or room. | | Text works but media doesn't | The event type, the token, the size limit, or the agent flow blocks it | Confirm Artemis supports the event type, the token can download message content, attachment limits allow the file, and the agent flow handles attachments. | | The provider you want isn't in the list | Your account lacks the admin role on it | Ask a provider admin to grant the role rather than creating a second provider. | *** ## LINE reference Use these LINE pages and consoles when you need detail beyond what this article covers. | Resource | Use it for | | - | - | | [LINE Official Account Manager](https://manager.line.biz/) | Creating the Official Account, enabling the Messaging API, response settings, group chat permission, and admin roles. | | [LINE Developers Console](https://developers.line.biz/console/) | Channel credentials, webhook settings, the security tab, and the friend-add QR code. | | [Get started with the Messaging API](https://developers.line.biz/en/docs/messaging-api/getting-started/) | LINE's own walkthrough of Business ID registration, account creation, and provider selection. | | [Build a bot](https://developers.line.biz/en/docs/messaging-api/building-bot/) | Tokens, the webhook URL, verification, and the certificate requirements. | | [Channel access tokens](https://developers.line.biz/en/docs/basics/channel-access-token/) | How the four token types differ and which suits your rotation policy. | | [Messaging API reference](https://developers.line.biz/en/reference/messaging-api/) | Endpoint detail, including [Get bot info](https://developers.line.biz/en/reference/messaging-api/#get-bot-info) and webhook event objects. | **Related articles:** * [Channels](/agent-platform/channels) * [Set up Messenger](/agent-platform/channels/messenger) * [Set up WhatsApp](/agent-platform/channels/whatsapp) * [Set up Telegram](/agent-platform/channels/telegram) # Set up Messenger Source: https://koreai.mintlify.app/agent-platform/channels/messenger [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital) Connect a Facebook Page to your agent so people reach it through Messenger. You prepare one Meta app and one Page, hand Artemis four values from them, then register a signed webhook that Meta verifies before it sends anything. The setup requires: 1. [Meta setup](#meta-setup): create a business app, connect the Page, and collect the Page ID, Page Access Token, and App Secret. 2. [Artemis channel configuration](#artemis-channel-configuration) using those values plus a Verify Token you invent, then copy the generated webhook URL. 3. [Webhook configuration in Meta](#webhook-configuration-in-meta): register the callback URL, complete Meta's challenge, and subscribe the Page to the message events. After you complete the configuration, Meta posts signed Page events to Artemis, which matches the Page ID in the payload to this connection and hands the message to the deployed agent. **Important considerations** * **Every value must come from one app and one Page**: The Page ID, Page Access Token, App Secret, Verify Token, and webhook subscription all have to belong to the same Meta app and Page setup. Mixing values from two apps produces failures that look unrelated to each other. * **One Page routes to one connection**: Reusing a Page means deactivating the existing connection first, or the second one has nothing to route to. * **Verification and subscription are separate steps**: Meta verifies the callback URL whether or not you subscribe to anything, so a verified webhook with no subscription looks correct on screen and delivers nothing. * **Development mode blocks the public**: Until Meta approves the app and you switch it to Live, Page tokens reach only accounts holding an app or Page role. A successful test with a role-holding account proves nothing about public access. The Meta work spans three screens in the App Dashboard at [Meta for Developers](https://developers.facebook.com/). | Meta screen | What you do there | | - | - | | Messenger settings | Connect the Page, generate the Page Access Token, and configure the webhook. | | **App settings** -> **Basic** | Reveal and copy the App Secret. | | **Roles** tab | Add the testers who can reach your Page before app review. | ## Prerequisites * A Meta developer account, and a Facebook Page that customers will message. * Enough access on the Page to perform the messaging and moderate tasks. Meta requires this of whoever generates the Page Access Token, so an editor or viewer role fails even when the app side looks correct. * An HTTPS-reachable Artemis runtime, since Meta calls the callback URL during verification. * Artemis project access with permission to open **Deployments** -> **Channels**, and a deployed agent in the environment you intend to bind. *** ## How Artemis routes Messenger events Meta sends every Page event to one generic URL, so Artemis identifies the connection from the payload rather than the path. It reads the Page ID out of the signed event and matches it to a connection. | Piece | Who owns it | What it does | | - | - | - | | **Facebook Page ID** | Meta | Routes inbound events. Artemis reads the Page ID from each signed webhook and matches it to this connection. | | **Page Access Token** | Meta | Authorizes outbound replies through the Messenger Send API. It scopes to one Page, so a token for a different Page fails to send. | | **App Secret** | Meta | Verifies the `X-Hub-Signature-256` header on every inbound POST. Artemis computes the HMAC over the unchanged request body and rejects anything that doesn't match. | | **Verify Token** | You | Answers Meta's one-time verification challenge when you save the callback URL. It does nothing after that. | | **Webhook URL** | Artemis | Receives events. The route ends at the generic Messenger path and carries no Page ID, because the Page ID travels in the payload. | *** ## Meta setup Complete all of this in Meta before you open Artemis. Meta covers the same ground in its [Messenger Platform quick start](https://developers.facebook.com/docs/messenger-platform/getting-started/quick-start/). 1. Sign in to [Meta for Developers](https://developers.facebook.com/) and open [My Apps](https://developers.facebook.com/apps/). 2. Create an app, or open the one that will own this connection. 3. When Meta asks what the app does, choose the business messaging use case, or choose **Other** and then **Business** as the app type if Meta shows you that older flow. Only a business app exposes the Messenger product, so choosing the wrong type means creating a second app rather than converting the first. Use separate apps or Pages for development and production, because app mode and Page subscriptions belong to the app. 1. On the App Dashboard, find **Messenger** in the products list. 2. Click **Set up**. The left menu then carries a Messenger section holding the settings screen you'll return to several times. Meta renames this screen between app types, so you may see **Messenger API Settings**, or **Messenger** followed by **Settings**. Whichever label appears, the screen you want holds three areas together: a Page connection, a token generation panel, and webhook controls. If the screen you're looking at has only one of those, you're on the wrong one. 1. In the token generation area, add or select the Page customers will message. 2. Authorize the app on that Page when Meta asks. Your account needs enough access on both sides. Meta requires that whoever generates the Page token can perform the messaging and moderate tasks on the Page, so an editor or viewer role fails here even when everything on the app side looks right. Sort the Page role out before you continue, because the failure it produces later looks like a token problem. 1. Select the Page in the **Page** dropdown. A token appears in the **Page Access Token** field. 2. Click the field to copy the token. Two behaviors here catch people out. Meta doesn't store the token in this screen, so you can't come back and read it; you generate a fresh one instead. And each time you select the Page from the dropdown, Meta generates a new token, while every token it generated earlier keeps working. That means an accidental extra generation doesn't break anything, but it does leave live credentials you didn't intend to create, so treat each generation as deliberate. This token is Page-scoped rather than user-scoped. A user token, or a token generated for a different Page, authenticates successfully and then fails to deliver replies. Artemis routes on the Page's numeric ID, which is a different value from everything else Meta shows you about the Page. 1. Open the Page in Facebook and look under its **About** information, where Facebook lists the Page ID among the page's transparency details. 2. Or open the Page's settings in [Meta Business Suite](https://business.facebook.com/), which also shows it. Don't substitute the Meta App ID, the Business ID, the Page username from the Page's web address, or the page-scoped user ID. That last one, the PSID, appears in every inbound event and identifies the customer, not the Page: Meta issues each person a different PSID for each Page they message. The App Secret sits outside the Messenger section, so the left menu is the only route to it. 1. Go to **App settings** -> **Basic**. 2. Click **Show** beside **App secret**. 3. Re-enter your Facebook password when Meta asks, then copy the value. Artemis uses this secret to verify that an inbound event genuinely came from Meta. Take it from the same app that owns the Messenger product, since an App Secret from a neighboring app produces a signature failure on every single message. Invent a high-entropy secret string. Meta doesn't issue this one, so you create it and enter the same value twice: once in Artemis, once in the Meta webhook configuration. Artemis lets you save a connection without it, but Meta's verification can't succeed until a matching non-empty value exists in both places. While the app stays in development mode, Page tokens reach only Facebook accounts holding an administrator, developer, or tester role on the app. 1. Grant those roles from the **Roles** tab of your app settings, which Meta describes under [standard access](https://developers.facebook.com/docs/messenger-platform/getting-started/quick-start/). 2. Have each person accept the invitation before you test with them. 3. Before ordinary customers can message the Page, submit the app for review on the messaging permissions and complete business verification. Then switch the app to Live mode using the control at the top of the App Dashboard. A successful test with a role-holding account proves nothing about public access. Plan to repeat it with an account holding no role once the app goes Live. *** ## Artemis channel configuration Create the connection before you configure the callback in Meta. Meta sends its verification request the moment you save the webhook, and Artemis can only answer that challenge from a stored Verify Token. 1. Open Artemis and select the project. 2. Go to **Deployments** -> **Channels** -> **Messenger** -> **+ New**. The **New Messenger Connection** dialog opens. 3. Configure the following fields: | Artemis connection field | Source / Notes | What it controls | | - | - | - | | Display Name | You choose it | The label operators see in the connections list. Name it for the Page and the environment it serves, because a project can hold several Messenger connections. Routing ignores this value. | | Facebook Page ID | The Page's **About** information, or Meta Business Suite. See [Meta setup](#meta-setup). | The routing key. It's numeric, and it isn't the App ID, Business ID, Page username, or PSID. | | Page Access Token | Messenger settings > token generation panel | The credential for outbound replies. Generate it for this exact Page. | | App Secret | **App settings** -> **Basic** in the same Meta app | The key Artemis uses to verify the signature on every inbound event. | | Verify Token | You invent it | Answers Meta's one-time verification challenge. Enter the identical value in Meta when you register the callback. | | Environment | Your published environments | The environment whose active deployment answers incoming messages. Leave the working copy default only while you develop. | | Provider Verification Strength | Defaults to **Weak** | How far Artemis trusts Meta's identity assertion when it links a person to an existing session. **Weak** suits most deployments. Choose **Strong** only when you trust those assertions enough to base continuity and account-linking decisions on them. | | Initial Status | Defaults to **Active** | Whether the connection starts handling messages. Set it to **Active**, because Meta's verification challenge resolves only when the connection is already active. | 4. Click **Create**. 5. Open the connection and copy the **Webhook URL** from the **Overview** tab. You need it for the next section. *** ## Webhook configuration in Meta Registration has two halves, and completing only the first is the most common way a setup fails silently. Meta verifies the URL whether or not you subscribe to anything. 1. Return to the Messenger settings screen in the same Meta app. 2. Find the webhooks area and add a callback for the Page object. Configure the URL here rather than under another Meta product. A callback registered under a different product verifies successfully and delivers nothing to this connection, which is a slow failure to trace because both screens report success. 1. Paste the Artemis **Webhook URL** exactly as shown. 2. Enter the same **Verify Token** you saved in Artemis. Don't append a Page ID, a connection ID, a query string, or a trailing segment. Artemis takes the Page ID from the signed payload, so anything added to the path breaks the route rather than refining it. Save the callback. Meta immediately calls the URL with a subscribe challenge and expects your Verify Token echoed back. Artemis answers only when the token matches an active connection, so a failure here points at the token, the connection status, or public reachability over HTTPS rather than at the URL itself. 1. In the webhook fields list, click **Manage**. 2. Subscribe the Page webhook to `messages` and `messaging_postbacks`. The `messages` field carries text, attachments, and quick-reply payloads, so it's required even for an agent that only handles text. Subscribe `messaging_postbacks` as well if your agent sends buttons, since a button tap arrives as a separate event type carrying the authored payload rather than as a message. 1. Confirm this app and webhook list the intended Page as subscribed. 2. Save the app changes. If Meta asks for permissions review or Page task access for your current app mode, complete that before you test with anyone outside your app roles. ### Validation checklist | Validation item | Expected result | | :- | :- | | Complete Meta setup | Business app created, Messenger product added, and the Page connected with a generated Page Access Token. | | Collect credentials | Numeric Page ID, Page Access Token, and App Secret copied from the same app and Page. | | Create the Artemis connection | Connection saved as **Active** with all four values and the intended environment. | | Register the callback URL | Meta accepts the callback and completes the verification challenge. | | Subscribe the events | This app and webhook list the Page as subscribed to `messages` and `messaging_postbacks`. | | Send a live message | A real Messenger conversation produces exactly one agent reply. See [Verify the connection](#verify-the-connection). | | Retest after Live mode | An account with no app or Page role reaches the agent. | ## Verify the connection Artemis doesn't synthesize Meta events, so a real Messenger conversation is the only end-to-end proof. 1. While the app sits in development mode, use a Facebook account holding an app or Page role. Open the connected Page in Messenger and send a short message you can search for later. Confirm Meta reports a successful webhook delivery and that Artemis opens one session. 2. Check that a single outbound reply appears in the same conversation. A send failure at this point usually means the token belongs to the wrong Page, the messaging permission is missing, someone revoked the token, or the app mode still restricts delivery. 3. Trigger a quick reply and a button postback, and confirm each returns to the same session. Artemis normalizes both into action events, so the authored action ID and value still have to exist in the agent. 4. Send each message type your Page will use. Artemis downloads image, video, audio, and file attachments before the agent processes them. Location, sticker, and fallback attachments arrive as events rather than as files, so an agent that expects a downloadable attachment won't find one. 5. Once Meta approves the app and you switch it to Live mode, run the same test from an account with no app or Page role. This is the only check that proves ordinary customers can reach the agent. *** ## Messenger limits Meta enforces these boundaries on outbound content, and Artemis applies them before it calls the Send API. Design your agent's responses to fit them. | Area | Limit | | - | - | | Buttons | Button templates carry up to 3 buttons, with titles capped at 20 characters. | | Quick replies | Up to 13 choices, with titles capped at 20 characters. | | Carousels | Generic-template carousels carry up to 10 cards and up to 3 buttons per card. | | Postback payloads | Up to 1,000 characters, which is the authored value your agent branches on. | | Messaging window | Meta's standard messaging window lets you reply freely for 24 hours after a person messages the Page. Outside it, you need an approved message tag, which the [Send API reference](https://developers.facebook.com/docs/messenger-platform/reference/send-api/) lists. A valid Page Access Token doesn't bypass this. | | Media | Remote media has to stay reachable from Artemis and within attachment size, content-type, and network-safety policies. | *** ## Secure the connection Messenger authentication runs in one direction only, which is easy to misread. Meta signs what it sends you; your Verify Token does nothing after setup. Every Messenger POST carries `X-Hub-Signature-256`. Artemis computes HMAC-SHA256 over the raw request body using your App Secret and rejects a missing or mismatched signature. Any proxy or middleware in front of Artemis has to preserve the exact request bytes, because reformatting the JSON changes the digest even when the content stays identical. Keep the three secrets distinct in your own operational notes. The Verify Token protects only the initial challenge and is a string you invented. The App Secret authenticates inbound traffic. The Page Access Token authorizes outbound sends, and you can rotate it without touching the Page ID, so a token rotation never requires a new connection. Artemis rejects Messenger events older than five minutes as replay protection. Replaying a captured payload during debugging therefore fails by design, and a live message is the only way to retest. Use separate Meta apps or Pages, credentials, callbacks, and connections for development and production. *** ## Manage the channel connection Opening a connection shows four tabs. The delete control sits beside the connection name. **Connection Summary** reports the connection's state and identity. | Field | Description | | - | - | | **Status** | Whether the connection currently handles messages. | | **Created** and **Updated** | Date and time of creation, and of the last change. | | **Environment** | The environment whose deployment answers messages. | | **Source Type** | Reads `Channel Connection`. | | **Source ID** | Identifies the connection uniquely. Quote it when you raise a support ticket. | **Setup Instructions** shows the **Webhook URL** to register in Meta and the **Facebook Page ID** this connection matches events to, each with a copy control. Check the Page ID here first when messages arrive and no session appears. Artemis encrypts credentials after you save them, so each field reads as saved rather than showing its value. Leave a field empty to keep the current value, or enter a new one to replace it. | Field | Description | | - | - | | **Use Auth Profile** | Draws the credentials from a saved auth profile instead of the fields below. Prefer it when the same credentials serve more than one connection, since a rotation then touches one profile. | | **Page Access Token** | Replace it here when you generate a new token in Meta. The Page ID stays the same, so you don't need a new connection. | | **App Secret** | Replace it here if you move the Messenger product to a different Meta app. | | **Verify Token** | Changing this breaks Meta's webhook until you enter the matching value in Meta as well. | Click **Save Credentials** to apply your changes. Under **General**, rename the connection with **Display Name**, and revisit **Provider Verification Strength** if your trust posture for this Page changes. Under **Messenger Settings**, **Facebook Page ID** appears read-only. Artemis routes inbound events by this value, so changing it would orphan the connection. Create a new connection to serve a different Page. **Authorization Message** controls what the person sees when a tool needs them to authorize something before the conversation continues. Write the template with these variables. | Scope | Variables | Use | | - | - | - | | Message | `{{total}}`, `{{completed}}`, `{{remaining}}` | Summarize progress across every pending authorization, typically in a lead-in line above the list. | | Repeat block | `{{#authorizations}}` and `{{/authorizations}}` | Wrap the section Artemis repeats once per pending authorization. | | Item | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Identify and link each authorization inside the repeat block. | Item variables resolve only inside the repeat block. Use the preview to check the wording, and remember it fills from live pending authorizations at runtime. Click **Save Changes** to apply your changes. Choose which deployed version of the agent answers messages on this connection. Under **Agent Version**, set **Environment** to the environment you want the connection to follow. **Active Deployment** then shows the version currently serving traffic and when someone deployed it. Check this pair when the channel connects but the agent behaves like an older build. The connection follows whatever the environment currently has deployed, not the version you last edited. Click **Save Changes** after you switch the environment. *** ## Troubleshoot connection issues | Symptom | Likely cause | Recommended action | | - | - | - | | Callback verification fails | The connection doesn't exist yet, the Verify Tokens don't match, or the endpoint isn't publicly reachable | Create the connection before you save the callback, enter the same non-empty Verify Token in both systems, use the URL exactly as Artemis shows it, and confirm the endpoint is reachable over public HTTPS. | | Inbound requests return 401 | The App Secret came from a different Meta app, or a proxy altered the signed request | Copy the App Secret from the app that sends the webhook, and confirm every proxy preserves the original request body and the `X-Hub-Signature-256` header. | | Artemis reports no connection found | The Facebook Page ID field holds the wrong identifier | Compare the Page ID in the webhook payload with the numeric value saved in Artemis, and confirm the connection is active. A PSID or an App ID in this field produces exactly this result. | | Verification succeeded but no messages arrive | The webhook has no field subscription, or the Page isn't subscribed to the app | Subscribe the `messages` and `messaging_postbacks` fields, confirm the app lists the intended Page as subscribed, and check the app mode, tester roles, and outstanding permissions. | | Messages arrive but replies fail | The token is for the wrong Page, the Page role is insufficient, or no deployment can run | Generate a fresh Page Access Token for the same Page, confirm your account can perform the messaging and moderate tasks on the Page, and confirm the bound environment has a runnable deployment. | | Everything works for your team and nobody else | The app is still in development mode | Complete app review and business verification, switch to Live, then retest from an account with no role. | | Text works but buttons fail | The content exceeds a Messenger limit, or the authored action is gone | Keep button and quick-reply counts and label lengths within the limits above, then confirm the authored action still exists in the agent. | | Text works but attachments fail | The media is unreachable, disallowed, or oversized | Confirm the attachment URL is reachable, that policy allows its host, content type, and size, and that the agent handles the downloaded file. | | Artemis ignores replayed events | Replay protection rejects events older than five minutes | Expected behavior. Send a new live message instead of replaying a captured payload. | *** ## Meta reference Use these Meta pages when you need detail beyond what this article covers. | Resource | Use it for | | - | - | | [Messenger Platform quick start](https://developers.facebook.com/docs/messenger-platform/getting-started/quick-start/) | Meta's own walkthrough of app setup, Page token generation, and the first webhook. | | [Send API reference](https://developers.facebook.com/docs/messenger-platform/reference/send-api/) | Outbound message types, message tags, and the messaging window rules. | | [My Apps](https://developers.facebook.com/apps/) | Returning to an app you created, or checking which app owns a Page subscription. | | [Meta Business Suite](https://business.facebook.com/) | Page settings, Page roles, and business verification. | **Related articles:** * [Channels](/agent-platform/channels) * [Set up WhatsApp](/agent-platform/channels/whatsapp) * [Set up LINE](/agent-platform/channels/line) * [Set up Telegram](/agent-platform/channels/telegram) # Set up Microsoft Teams and Copilot Source: https://koreai.mintlify.app/agent-platform/channels/ms-teams-copilot [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital-channels) Integrate your agent into Microsoft Teams channels, chats, and Microsoft 365 Copilot through an Azure Bot registration and a Teams application. ## Before you begin Complete the following tasks in Microsoft before you create the channel connection in Artemis. You need administrator access to the [Azure Portal](https://portal.azure.com/), the [Microsoft Teams Developer Portal](https://dev.teams.microsoft.com/), and the [Teams Admin Center](https://admin.teams.microsoft.com/). 1. Sign in to the [Azure Portal](https://portal.azure.com/) and search for **Azure Bot** in Marketplace. 2. Select **Create** and complete the bot registration. 3. When the deployment completes, select **Go to resource** under **Next steps**. 1. In the Azure Bot resource, go to **Settings** -> **Configuration**. 2. Select the **Manage password** link beside the **Microsoft App ID**. This opens the app registration's **Certificates & secrets** page. 3. Select **+ New client secret** and create the secret. 4. Copy the secret value immediately and store it securely. 5. Go to the app registration's **Overview** page and copy the **Application (client) ID**. This is your Microsoft App ID, also called the Enterprise App ID later in this article. 6. Copy the **Directory (tenant) ID** from the same page. When you copy the client secret, use the **Value**, not the **Secret ID**. The Secret ID won't authenticate. The Value appears only once. If you navigate away before copying it, create a new client secret. 1. In the app registration, go to **Authentication (Preview)** -> **Supported accounts**. 2. Select **Multiple Entra ID tenants**, choose **Allow all tenants**, and save. 1. Return to the Azure Bot resource and go to **Settings** -> **Channels**. 2. Enable **Microsoft Teams** and **Microsoft 365**. 1. Sign in to the [Microsoft Teams Developer Portal](https://dev.teams.microsoft.com/). 2. Create a new application or import an existing manifest. 3. Go to **Configure** -> **Basic information**, enter the **Short name** and **Long name**, and set the **Application (client) ID** to the Microsoft App ID you copied in step 2. 1. Go to **Configure** -> **App features** and select **Bot**. 2. Enter the Enterprise App ID as the bot ID. 3. Add the project commands, utterances, and descriptions. 4. Select the **Personal**, **Team**, and **Group Chat** scopes. 1. Go to **App package editor** -> **Actions** -> **Edit** to open `manifest.json`. 2. Update the Web App ID (`webApplicationInfo.id`) and the Copilot/Agents ID (`copilotAgents.customEngineAgents.id`) to the Enterprise App ID in both locations. 3. To support file attachments, set `bots[0].supportsFiles` to `true`. 4. Select **Update** and confirm. Attachment ingestion currently supports personal chat scope only. 1. In the Teams Developer Portal, go to **Publish** -> **Publish to org**, select **Get started**, and publish the application. 2. Open the [Teams Admin Center](https://admin.teams.microsoft.com/) and go to **Teams apps** -> **Manage apps**. 3. Search for the application, open it, and publish it. ## Create the channel connection in Artemis Open Artemis, select the required project, and go to **Deployments** -> **Channels** -> **MESSAGING** -> **Microsoft Teams** -> **+ New**. On the **New Microsoft Teams Connection** dialog, provide the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection, for example `Microsoft Teams Production`. Up to 255 characters. | | **Bot App ID** | The Microsoft App ID of the bot configured in your Teams application. Up to 255 characters. | | **App ID** | The Microsoft App ID from the Azure Bot registration, also called the Enterprise App ID. | | **Client Secret** | The client secret value you created in the Azure app registration. | | **Azure Tenant ID** | The Directory (tenant) ID from the app registration's Overview page in the Azure Portal. | | **Environment** | Select an environment to follow its active deployment. If no environment has an active deployment yet, leave the default to use the working copy and bind the connection later from the **Deployment** tab. | | **Initial Status** | **Active** connections can be used immediately. **Inactive** connections stay saved but disabled until you activate them. Set this to **Active**. | **Bot App ID** and **App ID** both take a Microsoft App ID. The app ID of your Teams app must match the app ID in the Azure Bot registration and the values you enter here. A mismatch is the most common cause of a Teams integration that won't connect. Select **Create**. 1. Open the connection you created and copy the **Webhook URL** from the **Overview** tab. 2. In the [Azure Portal](https://portal.azure.com/), open the Azure Bot resource and go to **Configuration**. 3. Paste the Webhook URL into the **Messaging endpoint** field. 4. Enable the **Streaming Endpoint** and select **Apply**. 1. Open [Teams](https://teams.cloud.microsoft/) and go to **Apps**. 2. Search for the published application, open it, and select **Add**. To test in Copilot, select **Open with Copilot**. 3. Trigger the configured queries and verify the Artemis response. ## Key values reference | Value | Where it comes from | Where it's used | | - | - | - | | **Microsoft App ID (Enterprise App ID)** | The Azure Bot registration's Application (client) ID | The **Bot App ID** and **App ID** fields in Artemis, the bot ID in the Teams application, and the app package IDs in the Teams Developer Portal | | **Client Secret** | Created under **Certificates & secrets**; copy the value immediately after creation | The **Client Secret** field in Artemis | | **Azure Tenant ID** | The Directory (tenant) ID in the Azure Portal (admin account) | The **Azure Tenant ID** field in Artemis | | **Webhook URL** | The Artemis Microsoft Teams connection's **Overview** tab | The Azure Bot **Messaging endpoint** | ## Manage the channel connection Opening a channel from **Deployments** -> **Channels** -> **MESSAGING** -> **Microsoft Teams** shows four tabs: **Overview**, **Credentials**, **Configuration**, and **Deployment**. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | For example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | For example: `staging` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique channel identifier (UUID) | Under **Setup Instructions**, the tab shows the **Webhook URL** to set as the bot's messaging endpoint in Azure, the **Bot App ID** for reference, and the **Require verified Teams identity** toggle. #### Require verified Teams identity Turn this on to require Microsoft-resolved Teams profile identity before the connection starts or resumes a session. Leave it off for baseline Teams conversations. When the toggle is on, Runtime resolves the sender's Microsoft profile email or UPN through the Bot Connector member endpoint before it allows the turn. If the identity can't be resolved, the turn doesn't proceed. This confirms that the person on the other end is the Teams user their profile claims they are, which matters for agents that act on a user's behalf or return information scoped to that user. The toggle relies on the credentials already configured for this connection or its selected auth profile, and on the bot staying installed and reachable in Teams. It doesn't use Microsoft Graph consent, OBO settings, or Graph scopes. The **Stronger identity verification setup** panel below the toggle lists the specific requirements to complete when you enable it. Credentials are saved and encrypted after creation; the fields show placeholder text and accept new values only if you want to update them. | Field | Description | | - | - | | **Use Auth Profile** | Toggle on to authenticate through a saved Auth Profile instead of entering credentials manually. When enabled, select the profile from the dropdown in place of the fields below. | | **App ID** | Your Microsoft App ID. Shown as *(saved — enter to update)*; leave blank to keep the current value. | | **Client Secret** | The client secret from your Azure Bot registration. Shown as *(saved — enter to update)*; leave blank to keep the current value. | | **Azure Tenant ID** | The Directory (tenant) ID from the Azure Overview page. Shown as *(saved — enter to update)*; leave blank to keep the current value. | Select **Save Credentials** to apply any changes. Under **General**, update the **Display Name**. Under **Microsoft Teams Settings**, configure the following. | Field | Description | | - | - | | **Bot App ID** | The Microsoft App ID of the bot, shown for reference. You can't change it after creation. | | **Enable Streaming** | Under **Response Streaming**, turn this on to stream LLM responses to Teams as the agent generates them, instead of waiting for the full response. Turn it off to deliver each response as a single message. | Select **Save Changes** to apply any changes. The **Deployment** tab shows these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown (for example: `Staging`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and when it was deployed. | Select **Save Changes** after switching the environment or version. # Set up Slack Source: https://koreai.mintlify.app/agent-platform/channels/slack [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital-channels) Deploy your agent as a Slack bot for team collaboration. Connect your Slack app to Artemis by entering its credentials manually. **Redirect URL and Request URL are different.** Slack uses two URLs at different stages: * **OAuth Redirect URL** — used only during the **Add to Slack** authorization flow, registered under **OAuth & Permissions** so Slack can return authorization back to Studio. * **Events Request URL** — generated for this connection and pasted under **Event Subscriptions → Request URL** in Slack, allowing Slack to deliver messages and events to Studio. ## Create an app in Slack At [api.slack.com/apps](https://api.slack.com/apps), select **Create New App**, choose **From scratch**, name the app, and choose a development workspace. You need three values from Slack before you can create the connection in Artemis: | Value | Where to find it | | - | - | | **Team ID** | Open your workspace in a browser. In `https://app.slack.com/client/T.../...` the segment beginning with `T` is your Team ID. | | **App ID** | Go to **Basic Information** -> **App Credentials** and copy **App ID**. | | **Signing Secret** | On the same **App Credentials** panel, select **Show** next to **Signing Secret** and copy it. | Go to **OAuth & Permissions** and, under **Bot Token Scopes**, add: | Scope | Purpose | | - | - | | `chat:write` | Send messages as the bot. | | `im:history` | Read direct message history with the bot. | | `commands` | Required for slash commands. | | `app_mentions:read` (optional) | Read @mentions of the bot in channels. | | `files:read` (optional) | Required for file attachments. | | `chat:write.public` (optional) | Required to post to public channels the bot hasn't joined. | On the same **OAuth & Permissions** page, select **Install to Workspace** (or **Reinstall to Workspace** if the app is already installed). Once installed, copy the **Bot User OAuth Token** from **OAuth Tokens** — it begins with `xoxb-`. Reinstall the app whenever you add or change scopes so the bot token receives the new grants. ## Integrate the Slack app in Artemis Open Artemis, select the required project, and go to **Deployments** -> **Channels** -> **MESSAGING** -> **Slack** -> **+ New**. Select **Enter credentials manually instead**, then fill in the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection, for example `Slack Production`. Up to 255 characters. | | **Slack Team ID:App ID** | The Team ID and App ID from Slack, joined by one colon with no spaces, for example `T0xxxxxxxxx:A0xxxxxxxxx`. | | **Bot Token** | The Bot User OAuth Token from **OAuth & Permissions** -> **OAuth Tokens**. Must begin with `xoxb-`. | | **Signing Secret** | The signing secret from **Basic Information** -> **App Credentials**. | | **Environment** | Select an environment to follow its active deployment. | | **Provider Verification Strength** | **Weak (default)** is set by default. Choose **Strong** only for channels/providers whose identity assertions you trust for stronger continuity and linking decisions. | | **Initial Status** | **Active** connections can be used immediately. **Inactive** connections stay saved but disabled until you activate them. | Select **Create**. Open the connection you created and copy the **Webhook URL** from the **Overview** tab. Go back to your Slack app's **Event Subscriptions** page, turn **Enable Events** on, and paste the Webhook URL into **Request URL**. Wait for Slack to show **Verified**, then, under **Subscribe to bot events**, add: | Event | Description | | - | - | | `message.im` | Direct messages sent to the bot. | | `app_mention` | @mentions in channels where the app is invited. | Save your changes. Send the app a direct message, then invite it to a channel and @mention it. Confirm the agent replies in both places. ## Manage the channel connection Opening a channel from **Deployments** -> **Channels** -> **MESSAGING** -> **Slack** shows four tabs: **Overview**, **Credentials**, **Configuration**, and **Deployment**. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | For example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | For example: `staging` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique channel identifier (UUID) | Under **Setup Instructions**: * **Require verified Slack identity** - Toggle on to require Slack workspace user identity with a resolvable email before this connection starts or resumes a session. Guests, external Slack Connect users, and users without email remain blocked. Before enabling this policy, complete the **Verified Slack identity setup**. Runtime uses Slack's `users.info` to resolve the sender's workspace email. 1. In the Slack app's **OAuth & Permissions**, add these Bot Token Scopes: `users:read` and `users:read.email`. 2. Reinstall or re-authorize the Slack app after adding scopes so the bot token receives the new permissions. * The tab also shows the **Webhook URL** to paste into Slack's Event Subscriptions **Request URL**, and the **Slack Team ID:App ID** for reference. Select the copy icon next to either value to copy it. Credentials are saved and encrypted after creation; the fields show placeholder text and accept new values only if you want to update them. | Field | Description | | - | - | | **Use Auth Profile** | Toggle on to authenticate through a saved Auth Profile instead of entering credentials manually. When enabled, select the profile from the dropdown in place of the fields below. | | **Bot Token** | The Bot User OAuth Token from Slack's **OAuth & Permissions** -> **OAuth Tokens**. Shown as *(saved — enter to update)*; leave blank to keep the current value. | | **Signing Secret** | The signing secret from Slack's **Basic Information** -> **App Credentials**. Shown as *(saved — enter to update)*; leave blank to keep the current value. | Select **Save Credentials** to apply any changes. Under **General**, update the **Display Name** and **Provider Verification Strength**. Under **Slack Settings**, the following fields apply. | Field | Description | | - | - | | **Slack Team ID:App ID** | The Team ID and App ID entered at creation, shown for reference. You can't change it after creation. | Under **Response Streaming**, configure the following. | Field | Description | | - | - | | **Enable Streaming** | Stream LLM responses to Slack in real time instead of waiting for the full response. | | **Chunk Size (characters)** | Minimum characters to buffer before sending to Slack. Lower values feel faster but make more API calls. | Under **Authorization Message**, configure the message shown when a tool needs the user to authorize. | Field | Description | | - | - | | **Message template** | The message text, with insertable variables: `{{#authorizations}}`, `{{profileName}}`, `{{authLink}}`, and `{{remaining}}`. Select **See all variables** for the full list, or **Show preview** to preview the rendered message. | Select **Save Changes** to apply any changes. The **Deployment** tab shows these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown (for example: `Staging`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and when it was deployed. | Select **Save Changes** after switching the environment or version. ## Optional Slack features | Feature | Setup | | - | - | | **Buttons, menus, and shortcuts** | Enable **Interactivity & Shortcuts** and use the same connection-specific Events Request URL as the **Interactivity Request URL**. | # Set up Telegram Source: https://koreai.mintlify.app/agent-platform/channels/telegram [Back to messaging channels](/agent-platform/channels#messaging-or-digital-channels) Connect a Telegram bot to your agent so users reach it in private chats, groups, and supergroups. Artemis registers the Telegram webhook for you and protects it with a generated secret, so you never call `setWebhook` yourself. You create the bot in Telegram, then hand Artemis two values from it. Everything after that happens automatically when you create the connection. ## How Artemis routes Telegram messages Four pieces carry the integration, and each one does a different job. Confusing them causes most setup failures. | Piece | Who owns it | What it does | | - | - | - | | **Bot Username** | BotFather | Routes incoming updates. Artemis puts the normalized username in the webhook path, then matches the last path segment to this connection. | | **Bot Token** | BotFather | Authorizes every outbound call Artemis makes to the Telegram Bot API, including `setWebhook`, `sendMessage`, typing indicators, callback answers, and media downloads. | | Secret token | Artemis | Verifies that an incoming update really came from Telegram. Artemis generates it, stores it encrypted, and never asks you for it. | | Webhook URL | Artemis | Receives updates from Telegram. Artemis builds it from your bot username and registers it during creation. | Because the username sits in the URL path, you can't change it after creation. Create a new connection if you need to point at a different bot. ## Create the Telegram bot Do this work in Telegram before you open Artemis. BotFather is a bot itself, so you create your bot by holding a chat with it rather than by filling in a web form. See Telegram's [introduction for developers](https://core.telegram.org/bots) if you've never built a bot before. Search Telegram for `@BotFather`, or open [t.me/botfather](https://t.me/botfather) from any device signed in to your account. Several accounts imitate BotFather, so confirm the blue verification badge beside the name before you send anything. A fake BotFather exists to harvest tokens. Select **Start** if you've never used it. BotFather replies with its command list. Send `/newbot`. BotFather asks two questions in turn. First it asks for a name, which is the title users see at the top of the chat. Spaces and most characters work here, and you can change it later. Then it asks for a username, which is permanent and must be unique across Telegram. Usernames run 5 to 32 characters, accept Latin letters, numbers, and underscores, ignore case, and must end in `bot`. BotFather rejects a taken username and asks again, so have a second choice ready. Choose carefully. Telegram never lets you change a bot username, and Artemis builds the webhook path from it, so a rename means a new bot and a new connection in both systems. BotFather confirms with a message containing the bot link and the token. The token is the long string after the numeric bot ID and a colon. Copy the username from the confirmation message without the `t.me/` prefix that surrounds it in the link. Artemis strips a leading `@` and stores the normalized value, so either form works. Nothing else routes: a display name, a numeric bot user ID, a chat ID, a token fragment, or a full URL all fail. Copy the token separately. Telegram formats it as a tappable code block, so tapping or clicking it copies the whole value. To retrieve either value later, send `/mybots`, select the bot, and select **API Token**. BotFather shows the username and token again without creating anything new. Return to BotFather and use these commands to control what users see before and during a chat. Each one prompts you to pick the bot first. | Command | What it sets | | - | - | | `/setname` | The title users see at the top of the chat. This one you can change whenever you like. | | `/setdescription` | The text on the empty chat screen, shown before a user selects **Start**, up to 512 characters. | | `/setabouttext` | The short line on the bot's profile page, which travels with the link when someone shares the bot, up to 120 characters. | | `/setuserpic` | The bot's profile photo. | | `/setcommands` | The command menu Telegram suggests as a user types `/`. | These shape discovery only. They don't affect message delivery, and they don't replace the webhook Artemis registers. Changes can take a few minutes to appear. Telegram documents the full command list under [BotFather](https://core.telegram.org/bots/features#botfather), and offers the same settings through the BotFather Mini App if you prefer a visual interface to chat commands. Artemis processes a group or supergroup message only when the message mentions the bot, replies to one of its messages, or uses `/start` or `/help`. Ordinary group chatter never reaches your agent. This holds whatever you set in BotFather. Telegram's own [privacy mode](https://core.telegram.org/bots/features#privacy-mode), which you toggle with `/setprivacy`, widens what Telegram sends when you disable it, and a bot added to a group as an admin receives everything regardless. Artemis still applies the mention and reply rule on top, so neither change alters what your agent sees, and the bot stays quiet in busy groups by design. One Telegram behavior does matter here: a privacy mode change takes effect only after you remove the bot from the group and add it back. Create one bot for development and another for production, each with its own token and its own Artemis connection. Telegram allows a single webhook per bot, so sharing one bot across environments means the two registrations overwrite each other. The bot token authorizes every Bot API call for that bot, and it travels in the request URL. Treat it like a password: keep it out of tickets, screenshots, logs, browser code, and client-side configuration. If it leaks, send `/token` to BotFather to generate a replacement, then update the credential in Artemis. The old token stops working as soon as the new one issues. ## Connect the bot in Artemis The connection stores your bot identity, binds the agent version that answers, and registers the webhook. Go to **Deployments** -> **Channels** -> **Telegram** -> **+ New** and complete the following. | Field | What it controls | | - | - | | **Display Name** | The label operators see in the connections list. Name it for the bot and the environment it serves, because a project can hold several Telegram connections. Routing ignores this value. | | **Bot Username** | The routing key. Enter the BotFather username exactly, with or without a leading `@`. Artemis writes the normalized value into the webhook path and can't change it later. | | **Bot Token** | The credential for every outbound Bot API call. Paste the complete token, including the numeric bot ID and the colon that precedes the secret portion. Artemis encrypts it on save. | | **Environment** | The published environment whose active deployment answers incoming messages. Selecting one keeps the connection current as you promote new versions. Leave the working copy default only while you develop. | | **Provider Verification Strength** | How far Artemis trusts Telegram's identity assertion when it links a user to an existing session. **Weak** suits most deployments. Choose **Strong** only when you trust the provider's assertions enough to base continuity and account-linking decisions on them. | | **Initial Status** | Whether the connection starts handling messages. **Active** takes traffic immediately. **Inactive** saves the configuration in a disabled state, which suits a connection you're staging ahead of a launch. | ### What happens when you select Create Artemis completes the Telegram-side registration in one step, so you don't touch the Bot API yourself. Artemis generates a high-entropy secret token, calls `setWebhook` with the URL built from your bot username, and subscribes to `message` and `callback_query` updates only. It also drops any updates left pending from an earlier webhook on that bot, so stale traffic can't enter the new connection. Messages users sent before you created the connection won't appear, by design. If registration fails, Artemis rolls the whole creation back rather than leaving a connection that can't receive anything. A conflict at this point means the token already belongs to another active Telegram connection in the project. Telegram permits one webhook per bot, so Artemis refuses the second connection instead of silently replacing the first one's webhook. ## Verify the connection Run these checks before you hand the bot to users. Each one exercises a different path, and a bot that answers private messages can still fail in groups or on buttons. Open the bot by its exact username, select **Start**, and send a short message you can search for later. Confirm you get one reply and that Artemis opens a single session. Send `/start`, then open a deep link carrying a start payload if your agent uses one. Artemis treats the command as a welcome action and passes the payload through as its value, so your agent can branch on where the user came from. Telegram documents the link format under [deep linking](https://core.telegram.org/bots/features#deep-linking). Add the bot to a group or supergroup, then address it by username, reply to one of its messages, and send `/start` or `/help`. Each should reach the agent. Send an unrelated message too, and confirm the bot ignores it. Trigger a response that carries an inline keyboard and select a button. Confirm the loading spinner clears and the resulting action lands in the same chat session. Send a photo, document, audio file, video, and voice note, depending on what your agent accepts. Artemis downloads the largest photo size or the attached file before the agent processes it. Include a caption in at least one test, since captions travel separately from the file. ## Manage the connection Opening a connection shows four tabs. The delete control sits beside the connection name. **Connection Summary** reports the connection's state and identity. | Field | Description | | - | - | | **Status** | Whether the connection currently handles messages. | | **Created** | Date and time of creation. | | **Updated** | Date and time of the last change. | | **Environment** | The environment whose deployment answers messages. | | **Source Type** | `Channel Connection`. | | **Source ID** | The connection's internal identifier, useful when you raise a support request. | **Setup Instructions** shows the **Webhook URL** Artemis registered with Telegram and the normalized **Bot Username**, each with a copy control. The URL follows the form `https://{host}/api/v1/channels/telegram/webhook/{bot-username}` and ends with the username minus any leading `@`. Use the **Webhook URL** to confirm what Telegram should be calling. If `getWebhookInfo` reports a different URL, something outside Artemis re-registered the bot. Update the bot token here when BotFather issues a new one. Artemis encrypts the stored token and never displays it again, so the field stays empty until you enter a replacement. Entering a new value and selecting **Save Credentials** overwrites the old one. Turn on **Use Auth Profile** to draw the credential from a stored authentication profile instead of holding a token on this connection. Rotate the credential whenever BotFather revokes or regenerates the token. Until you do, Artemis can neither re-register the webhook nor send messages, because both calls carry the old token. Adjust how the connection presents itself and how it handles responses. Under **General**, rename the connection with **Display Name**, and revisit **Provider Verification Strength** if your trust posture for this bot changes. Under **Telegram Settings**, **Bot Username** appears read-only. Artemis built the webhook path from it at creation, so changing it would break routing. Create a new connection to target a different bot. Turn on **Enable Streaming** to send the agent's response to Telegram as the model produces it, rather than holding the message until the response completes. Streaming shortens perceived latency on long answers. **Authorization Message** controls what the user sees when a tool needs them to authorize something before the conversation continues. Write the template with these variables. | Scope | Variables | Use | | - | - | - | | Message | `{{total}}`, `{{completed}}`, `{{remaining}}` | Summarize progress across every pending authorization, for example in a lead-in line before the list. | | Repeat block | `{{#authorizations}}` and `{{/authorizations}}` | Wrap the section Artemis repeats once per pending authorization. | | Item | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Identify and link each authorization inside the repeat block. | Item variables resolve only inside the repeat block. Use the preview to check the wording, and remember it fills from live pending authorizations at runtime. Choose which deployed version of the agent answers messages on this connection. Under **Agent Version**, set **Environment** to the environment you want the connection to follow. **Active Deployment** then shows the version currently serving traffic. Select **Save Changes** to apply the binding. A connection left on the working copy shows a warning, because the working copy is a development target that changes as you edit the agent. Bind the connection to a published environment before you put the bot in front of users. ## Telegram limits Telegram enforces these boundaries, and Artemis applies them before it calls the Bot API. Design your agent's responses to fit them. | Area | Limit | | - | - | | Text | Outbound messages run 1 to 4096 characters. Artemis substitutes a single ellipsis when a response would otherwise be empty. | | Inline keyboards | Up to 10 rows, with button text capped at 64 characters. | | Callback data | 64 bytes. Keep authored action IDs and values short enough to survive encoding. | | Media | Files up to 20 MB download, which is the Bot API's own ceiling rather than an Artemis one. Artemis rejects anything larger before upload. | | Channel posts | Not supported. The connection serves private chats, groups, and supergroups under the mention and reply rule. | ## Operate the connection safely A Telegram connection depends on two secrets and one webhook registration. Protect all three. The secret token is separate from the bot token. Telegram returns it in the `X-Telegram-Bot-Api-Secret-Token` header on every update, and Artemis compares it with the encrypted stored value using a constant-time check before processing anything. A proxy that strips unrecognized headers breaks this check, so preserve the header end to end. Never call `setWebhook` yourself for a connected bot. Telegram keeps one webhook per bot, so registering another URL or deleting the webhook outside Artemis stops the connection until you re-save or recreate it. Use `getWebhookInfo` for diagnosis only, run it through a secure operator tool, and keep the real token out of any transcript you share. Artemis deduplicates on Telegram's `update_id`, so a Telegram retry doesn't produce a duplicate agent turn. ## Troubleshooting Match the symptom to the check. Most failures trace to the token, the webhook registration, or the group mention rule. | Symptom | What to check | | - | - | | Creation fails with a webhook registration error | Verify the bot token, confirm the Artemis host is reachable over public HTTPS, confirm outbound access to `api.telegram.org`, and confirm BotFather permits `setWebhook` for the bot. | | Creation returns a conflict | The token already belongs to another active Telegram connection in this project. Reuse that connection or deactivate it rather than replacing its webhook. | | Updates return 401 | An external `setWebhook` call may have replaced the secret, or a proxy dropped the `X-Telegram-Bot-Api-Secret-Token` header. Re-save or recreate the connection and preserve the header. | | Private messages don't arrive | Confirm the user selected **Start**, check that `getWebhookInfo` points at this connection's URL, and confirm the connection is active. | | Private chats work but groups don't | Mention the exact bot username or reply to one of its messages, test `/start` and `/help`, and confirm the bot is a member of the group. Disabling privacy mode in BotFather doesn't make Artemis process unmentioned chatter. | | Text works but buttons don't | Confirm the callback payload fits within 64 bytes and that the authored action still exists. | | Text works but files fail | Confirm your agent accepts the media type, that the file is 20 MB or smaller, and that your attachment policy allows it. A failure here often means the token changed, since Artemis downloads through the Bot API. | | Messages sent before creation never arrive | Expected. The initial registration drops pending updates so stale events can't enter the new connection. | # Set up Twilio SMS Source: https://koreai.mintlify.app/agent-platform/channels/twilio [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital-channels) Connect a Twilio phone number to your agent so customers reach it over SMS and MMS. You prepare a messaging-capable sender in Twilio, hand Artemis the account credentials, and then point the number's incoming-message webhook at a connection-specific URL that Twilio signs on every request. The setup requires: 1. [Twilio setup](#twilio-setup): Choose the project, get a messaging-capable number, complete sender compliance, and copy the Account SID and Auth Token. 2. [Artemis channel configuration](#artemis-channel-configuration): Configure the connection using those credentials, then copy the generated webhook URL. 3. [Webhook configuration in Twilio](#webhook-configuration-in-twilio): Set the number's incoming-message webhook to that URL and select HTTP POST. After you complete the configuration, an SMS to your Twilio number opens a session in Artemis, the bound deployment answers it, and the reply goes back to the sender through Twilio's Messages API. **Important considerations** * **The reply doesn't travel in the webhook response**: Artemis returns an empty TwiML acknowledgement immediately and queues the agent, then sends the reply as a separate outbound API call. A 2xx on the webhook proves delivery of the inbound message and nothing more. * **Twilio signs the webhook URL itself**: The signature covers the full external URL and the sorted form fields. Scheme, host, port, and path all participate, so a proxy that rewrites any of them breaks signature validation even though the request arrives intact. * **One account boundary**: The Account SID, Auth Token, phone number, and any Messaging Service SID have to come from the same Twilio project or subaccount. Mixing a subaccount number with parent-account credentials fails in ways that look like a configuration error. * **Compliance sits outside Artemis**: Sender registration, consent, and opt-out rules belong to Twilio and the carriers. A working webhook doesn't exempt your traffic from them. ## Prerequisites * A Twilio account with access to the project or subaccount that owns the sender and its billing. * A messaging-capable Twilio phone number for the destination countries, in E.164 format with the leading plus and country code. * Any sender registration your traffic needs, such as [A2P 10DLC](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc) brand and campaign registration for United States long-code traffic. * Artemis project access with permission to open **Deployments** -> **Channels**, and a deployed agent in the environment you intend to bind. *** ## How Artemis routes Twilio SMS messages Twilio calls a URL you choose per phone number, so Artemis puts the connection identifier in the path rather than reading it from the payload. | Piece | Who owns it | What it does | | - | - | - | | **Connection Identifier** | You | Routes inbound messages. Artemis appends it to the webhook path and resolves the connection from that segment, so the identifier has to survive in the URL exactly as Twilio calls it. | | **Account SID** | Twilio | Identifies the account for outbound calls to the Messages API and for authenticated MMS downloads. It starts with `AC`. | | **Auth Token** | Twilio | Does two jobs. Artemis uses it to validate the `X-Twilio-Signature` header on every inbound request, and to authenticate outbound API calls. | | **Messaging Service SID** | Twilio | Optional. When present, Artemis sends replies through that service instead of replying from the inbound number. It starts with `MG`. | | **Webhook URL** | Artemis | Receives the incoming-message callback. It ends with the connection identifier, and its exact external form is part of the signature. | Artemis keys each conversation to the Twilio number and the customer number together, so the same person messaging two of your Twilio numbers holds two separate sessions. *** ## Twilio setup Do this work in the [Twilio Console](https://www.twilio.com/console) before you open Artemis. Sign in to the Twilio Console, then use the account selector at the top of the page to switch to the project or subaccount that owns the production phone number and its billing. Everything you collect from here has to come from this one account boundary. Twilio scopes the Auth Token to the account, so credentials from a parent account can't authenticate a subaccount's traffic. 1. Go to **Phone Numbers** -> **Manage** -> **Active numbers**. 2. Confirm the number you intend to use lists SMS capability, and MMS as well if your agent will handle media. 3. If you need a new number, buy one from **Phone Numbers** -> **Buy a number**, filtering by the capabilities and country you need. Keep the full E.164 value, including the leading plus and country code. Capability varies by country, so a number that sends SMS in one market may not support MMS in another. Finish the registration, sender-verification, consent, and opt-out requirements Twilio shows for your countries and traffic type. United States long-code traffic needs A2P 10DLC brand and campaign registration. Carriers filter unregistered traffic rather than failing it loudly, so messages appear to send and never arrive. Twilio surfaces this as error 30034. Configuring a webhook doesn't bypass any of this. Start compliance early, because it waits on external review rather than on you. 1. Open the Twilio Console dashboard for the selected account. 2. Find the account information panel, which shows the **Account SID** and the **Auth Token**. 3. Copy the `AC`-prefixed Account SID, then reveal and copy the Auth Token. The Auth Token authenticates outbound API calls and validates inbound request signatures, so treat it as a high-value secret and store it in your secret manager rather than in a ticket or a shared document. A Messaging Service is optional. Use one when you want Twilio to pick a sender from a pool, apply sticky sender behavior, or carry an A2P campaign registration. 1. Go to **Messaging** -> **Services** and create a service, or open an existing one. 2. Add your phone number to the service's sender pool. Twilio's [sender pool guidance](https://help.twilio.com/articles/4402705042075-Managing-a-Messaging-Service-Sender-Pool) covers the rules. 3. Copy the `MG`-prefixed SID. Leave the SID out of the Artemis connection to reply from the inbound number instead. Twilio's [Messaging Services documentation](https://www.twilio.com/docs/messaging/services) explains what each mode changes. A trial project restricts which recipients and senders you can use, and adds branding to outbound messages. Prove production behavior on an upgraded account with an approved sender. A trial test that works tells you the wiring is right, not that your launch will work. *** ## Artemis channel configuration Create the connection before you configure the Twilio webhook. The callback URL contains the connection identifier, so you can't finalize it in Twilio until Artemis has generated it. 1. Open Artemis and select the project. 2. Go to **Deployments** -> **Channels** -> **Twilio SMS** -> **+ New**. 3. Configure the following fields: | Artemis connection field | Source / Notes | | - | - | | Display Name | The label operators see in the connections list. Name it for the sender and the environment it serves, because a project can hold several Twilio connections. Routing ignores this value. | | Connection Identifier | The routing key, which Artemis writes into the webhook path. Choose a short, stable, URL-safe value that identifies this sender. You can't change it after creation, because the webhook URL is built from it. | | Account SID | Identifies the Twilio account for outbound calls and MMS downloads. From the account information panel in the Twilio Console dashboard. | | Auth Token | Validates the signature on inbound requests and authenticates outbound API calls. From the same account as the Account SID. | | Messaging Service SID | Optional. Supply it to send replies through that service. Leave it empty to reply from the number that received the message. | | Environment | The published environment whose active deployment answers incoming messages. Leave the working copy default only while you develop. | | Provider Verification Strength | How far Artemis trusts Twilio's identity assertion when it links a sender to an existing session. **Weak** suits most deployments. Choose **Strong** only when you trust those assertions enough to base continuity and account-linking decisions on them. | | Initial Status | Whether the connection starts handling messages. **Active** takes traffic immediately. **Inactive** saves the configuration in a disabled state, which suits a connection you're staging ahead of a launch. | 4. Select **Create**. 5. Open the connection and copy the **Webhook URL** from the **Overview** tab.
The URL ends with your connection identifier. Copy it exactly, because Twilio signs the complete URL and any difference invalidates the signature. ### Validation checklist | Validation item | Expected result | | :- | :- | | Select the Twilio account | Account SID, Auth Token, phone number, and any Messaging Service all come from one project or subaccount. | | Confirm sender capability | Number lists SMS capability for the destination countries, and MMS if the agent handles media. | | Complete compliance | Sender registration, consent, and opt-out requirements finished for the traffic type and region. | | Create the Artemis connection | Connection saved as Active with a stable connection identifier and the account credentials. | | Copy the webhook URL | Webhook URL copied from the **Overview** tab, ending with the connection identifier. | | Configure the Twilio webhook | The number's incoming-message webhook points at that exact URL with HTTP POST selected. | | Bind a deployment | The connection's **Deployment** tab shows a runnable active deployment. | | Send a real SMS | Test message reaches the agent and the reply arrives once. See [Verify the connection](#verify-the-connection). | *** ## Webhook configuration in Twilio Twilio calls this URL every time the number receives a message. Configure it on the number itself, or on the Messaging Service when that service owns inbound handling. For a direct number, go to **Phone Numbers** -> **Manage** -> **Active numbers** and select the number. For a Messaging Service that owns inbound handling, open the service and use its integration settings instead. Configure one or the other, because two handlers pointing at the same connection produce duplicate replies. 1. Scroll to the **Messaging** section of the number's configuration. 2. In the **A message comes in** field, select **Webhook**. 3. Paste the Artemis **Webhook URL** exactly as shown, including the connection identifier at the end. 4. Select **HTTP POST** from the method dropdown. 5. Select **Save**. Twilio sends `application/x-www-form-urlencoded` POST fields including `MessageSid`, `AccountSid`, `From`, `To`, `Body`, `NumMedia`, and any `MediaUrl` fields. Twilio's [incoming message webhook reference](https://www.twilio.com/docs/usage/webhooks/sms-webhooks) lists them all. Twilio signs the complete URL plus the sorted form fields using HMAC-SHA1 with your Auth Token, then sends the result in `X-Twilio-Signature`. Artemis validates the signature using its configured public base URL. Configure that base URL to the same external address Twilio calls, and forward the `X-Twilio-Signature` header unchanged. HTTPS termination and reverse-proxy host and protocol settings all have to agree, because scheme, host, port, path, and form values all participate in validation. Artemis returns HTTP 200 with an empty TwiML response straight away and queues the agent. Twilio treats that as an acknowledgement and sends nothing to the customer. The agent's reply follows separately through the Messages API. Twilio expects a response within 15 seconds and calls the fallback webhook when the primary fails or times out, which is why the acknowledgement returns before the agent runs. *** ## Verify the connection Artemis doesn't synthesize Twilio requests, so a real SMS is the only end-to-end proof. 1. From an allowed recipient, send a short SMS to the Twilio number that you can search for later.
On a trial project, the recipient has to be a verified caller ID. 2. Confirm Twilio reports a 2xx webhook result and that Artemis opens one session. 3. Check that the agent replies once to the original sender.
The reply arrives from the inbound Twilio number, or from the Messaging Service when you supplied its SID. The empty TwiML acknowledgement isn't the reply, so a 2xx with no message means the outbound path failed. 4. Send a second message from the same customer number to the same Twilio number, and confirm it continues the existing session rather than starting a new one.
A different sender or recipient number opens a separate conversation. 5. Send an MMS where the number, destination, and carrier support it, and confirm the agent handles the downloaded attachment. 6. Repeat the whole test on an upgraded account with the production sender and an ordinary recipient, then check opt-in and opt-out handling, delivery status, throughput, and per-segment cost. *** ## SMS behavior and limits SMS is a plain-text channel with carrier economics attached, which changes how you author agent responses. | Area | Behavior | | - | - | | Formatting | SMS carries plain text only. Markdown, buttons, and rich cards have no equivalent, so author replies that read correctly without them. | | Message length | Carriers split long messages into multiple segments, and each segment bills separately. Unicode characters reduce the per-segment character count, so test representative content for your target countries. | | Delivery retries | Twilio delivers webhooks at least once and makes no ordering guarantee, so the same inbound message can arrive more than once. | | Media | MMS availability depends on the number, the destination, and the carrier. Artemis authenticates downloads from allowed Twilio hosts using the Account SID and Auth Token, then applies attachment type and size policy. | | Timeout | Twilio expects a webhook response within 15 seconds and falls back to the secondary handler after that. | | Compliance | Consent, opt-out keywords, and sender registration apply to every message regardless of how the agent behaves. | *** ## Secure the connection Twilio authentication rests on one credential doing two jobs, and on a URL that both sides have to match exactly. Every inbound request carries `X-Twilio-Signature`. Artemis computes HMAC-SHA1 over the configured public callback URL and the sorted form parameters using the Auth Token as the key. Twilio's [security guide](https://www.twilio.com/docs/usage/security) describes the same calculation from the sending side. Treat the external URL as signed data. Keep HTTPS termination, host, protocol, and port aligned with the public base URL you configured for Artemis, and pass the signature header through untouched. A reverse proxy that presents a different internal URL to Artemis breaks validation even though nothing about the message changed. The Auth Token validates inbound signatures and authorizes outbound sends, so rotate it in Twilio and Artemis as one coordinated change rather than two. A rotation applied on one side only stops inbound and outbound traffic at the same moment. Use separate Twilio subaccounts or senders, credentials, webhooks, and connections for development and production. *** ## Manage the channel connection Opening a connection shows four tabs. The delete control sits beside the connection name. **Connection Summary** reports the connection's state and identity. | Field | Description | | - | - | | **Status** | Whether the connection currently handles messages. | | **Created** and **Updated** | Date and time of creation, and of the last change. | | **Environment** | The environment whose deployment answers messages. | | **Source Type** | Reads `Channel Connection`. | | **Source ID** | Identifies the connection uniquely. Quote it when you raise a support ticket. | **Setup Instructions** shows the **Webhook URL** to configure in Twilio and the **Connection Identifier** it ends with, each with a copy control. Compare the URL here with what Twilio actually calls whenever signature validation fails. Artemis encrypts credentials after you save them, so each field reads as saved rather than showing its value. Leave a field empty to keep the current value, or enter a new one to replace it. | Field | Description | | - | - | | **Use Auth Profile** | Draws the credentials from a saved auth profile instead of the fields below. Prefer it when the same credentials serve more than one connection, since a rotation then touches one profile. | | **Account SID** | Replace it here if you move the sender to a different Twilio account or subaccount. | | **Auth Token** | Replace it here the moment you rotate the token in Twilio, because inbound validation and outbound sends both stop until you do. | | **Messaging Service SID** | Add or replace it to route replies through a service. Clear it to reply from the inbound number instead. | Select **Save Credentials** to apply your changes. Under **General**, rename the connection with **Display Name**, and revisit **Provider Verification Strength** if your trust posture for this sender changes. Under **Twilio SMS Settings**, **Connection Identifier** appears read-only. The webhook URL is built from it, so changing it would invalidate the URL configured in Twilio. Create a new connection to serve a different sender. **Authorization Message** controls what the person sees when a tool needs them to authorize something before the conversation continues. Write the template with these variables. | Scope | Variables | Use | | - | - | - | | Message | `{{total}}`, `{{completed}}`, `{{remaining}}` | Summarize progress across every pending authorization, typically in a lead-in line above the list. | | Repeat block | `{{#authorizations}}` and `{{/authorizations}}` | Wrap the section Artemis repeats once per pending authorization. | | Item | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Identify and link each authorization inside the repeat block. | Item variables resolve only inside the repeat block. Keep the wording short, because SMS renders it as plain text and long templates split into extra segments. Select **Save Changes** to apply your changes. Choose which deployed version of the agent answers messages on this connection. Under **Agent Version**, set **Environment** to the environment you want the connection to follow. **Active Deployment** then shows the version currently serving traffic and when someone deployed it. Check this pair when the channel connects but the agent behaves like an older build. The connection follows whatever the environment currently has deployed, not the version you last edited. Select **Save Changes** after you switch the environment. *** ## Troubleshoot connection issues | Symptom | Likely cause | Recommended action | | - | - | - | | Artemis reports no connection found | Twilio is calling a URL that doesn't end with this connection's identifier, or the connection isn't active | Compare the URL configured in Twilio with the **Webhook URL** on the **Overview** tab, including the trailing identifier, and confirm the connection is Active. | | The webhook returns 401 | The signed URL or the Auth Token doesn't match | Compare the exact external URL Twilio called with the public base URL you configured for Artemis, preserve `X-Twilio-Signature` through your ingress, and confirm the Auth Token belongs to the same Account SID. | | The webhook returns 200 but no session appears | The request carried nothing to process, or the queue is unhealthy | Send a message with a non-empty body or at least one media item, then check queue health. | | Inbound works but no reply goes out | The outbound credential, sender ownership, compliance state, or deployment is wrong | Verify the Account SID and Auth Token, confirm the account owns the sender, check destination eligibility and registration, and confirm the bound environment has a runnable deployment. | | The Messaging Service sends from the wrong number or fails | The service belongs to another account, or the sender isn't in its pool | Confirm the `MG` SID belongs to the same account and the intended number sits in its sender pool. Clear the SID to reply directly from the inbound number. | | SMS works but MMS fails | Capability, media access, or attachment policy blocks it | Confirm carrier and number support for MMS, that the Twilio media URL is reachable from an allowed host, that the credentials authenticate the download, and that content type and size pass your attachment policy. | | Customers receive duplicate replies | Two inbound handlers point at this connection, or Twilio retried | Confirm only one number or Messaging Service handler targets this connection, and deduplicate on `MessageSid` to absorb Twilio's retries. | | Long messages cost or render unexpectedly | Carrier segmentation split the message | Shorten agent replies, test representative Unicode content, and check per-segment behavior for the destination countries. | *** ## Twilio reference Use these Twilio pages when you need detail beyond what this article covers. | Resource | Use it for | | - | - | | [Twilio Console](https://www.twilio.com/console) | Account credentials, active numbers, and Messaging Services. | | [Incoming message webhooks](https://www.twilio.com/docs/usage/webhooks/sms-webhooks) | The form fields Twilio posts and how the incoming-message webhook behaves. | | [Messaging webhooks overview](https://www.twilio.com/docs/usage/webhooks/messaging-webhooks) | The difference between incoming-message webhooks and status callbacks. | | [Security](https://www.twilio.com/docs/usage/security) | How Twilio builds the signature and what participates in it. | | [Messaging Services](https://www.twilio.com/docs/messaging/services) | Sender pools, sticky sender behavior, and service-level inbound handling. | | [A2P 10DLC](https://www.twilio.com/docs/messaging/compliance/a2p-10dlc) | Brand and campaign registration for United States long-code traffic. | **Related articles:** * [Set up WhatsApp](/agent-platform/channels/whatsapp) * [Set up Messenger](/agent-platform/channels/messenger) * [Set up Instagram](/agent-platform/channels/instagram) # Voice channels Source: https://koreai.mintlify.app/agent-platform/channels/voice [Back to voice channels](/agent-platform/channels#voice-channels) ## Set up Realtime LLM Voice channel Supports live voice conversations using Realtime LLM models (OpenAI Realtime, Gemini Live) through the Kore.ai Voice Gateway or a Bring Your Own SIP connection. To configure a Realtime LLM Voice channel, go to **Deployments** > **Channels** > **VOICE** > **Realtime LLM Voice** > **+ New** and complete the following steps in order. On the **New Realtime LLM Voice Connection** page, provide the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection. | | **Connection Name** | A system name for the connection, such as `realtime-voice-prod`. Up to 255 characters. | | **Environment** | The deployment the connection binds to. Select an environment to follow its active deployment automatically, or leave the default to use the working copy. When no environment has an active deployment yet, create a deployment first to bind the connection to a published environment. | | **Provider Verification Strength** | Controls how strictly the platform trusts the provider's identity assertions for session continuity and linking. **Weak** is the default. Choose **Strong** only for channels or providers whose identity assertions you trust. | | **Initial Status** | **Active** connections are usable immediately. **Inactive** connections stay saved but disabled until you activate them. | Click **Create**. After creating the channel, go to the **Configuration** tab. If you choose **Voice Provider: Kore.ai Voice Gateway**, set the following fields under **Kore.ai Voice Gateway**. | Route | Description | | - | - | | **Soft Phone** (FREE) | Dial the agent from your browser with no DID purchase or telephony charges. Use it for evals, QA, and demos. **Launch dialer** opens the browser dialer. | | **Phone Number (DID)** | Purchase a real phone number through Twilio. Disable this route to stop DID listing and call routing while keeping the number saved. | If you select **Phone Number (DID)**, complete the next step. If you selected **Voice Provider: Bring Your Own SIP** instead, skip to Configure SIP Settings. Set the **Provider** to **Twilio**, toggle **Inbound calls** (route calls from this number into the voice agent) and **Outbound calls** (allow this number to be used for campaign dialout), and select a **Country**, **Number Type**, and optional **Area Code**. Select **Search Available Numbers** to list numbers that match your filters. Once the channel is created, select it, then go to the **Configuration** tab. If you chose **Voice Provider: Bring Your Own SIP**, set the following fields under **SIP Configuration**. | Field | Description | | - | - | | **Our SIP Server Address** | Configure your SIP provider to send `INVITE` to this address, for example `korevg-np.kore.ai:5060`. Select **Copy** to copy the address. | | **SIP Gateway IPs / CIDRs** | Provider SIP source addresses. Separate multiple IPv4 addresses or CIDR ranges with commas, for example `203.0.113.50:5080, 3.80.16.0/23`. | | **SIP Termination URI** | Choose whether your SIP provider identifies by **IP Address** or **Domain Name**, then enter the SIP termination URI. | | **DID / Phone Number** | The phone number (DID) your SIP provider routes to this SIP gateway. | | **Enable E.164 (leading +)** | Prepend a leading `+` to outbound numbers sent to the carrier. On by default, which international (full-E.164) numbers require. Turn off only for local/national-format numbers — otherwise the leading `+` misroutes them. | Under **Realtime LLM Voice settings**, set the following fields. * **Realtime orchestration**: Select how runtime coordinates the conversation flow and business tool execution for realtime voice interactions. Realtime orchestration determines whether the realtime model or runtime controls conversation progression and business actions, such as bookings, orders, and payments. | Mode | Description | Recommended for | | - | - | - | | **Legacy - model-selected tools** | Preserves the existing behavior where the realtime model manages both the conversation and business tool selection. | Existing agents and backward compatibility. | | **Runtime Guarded - hybrid freeform (recommended)** | Allows the realtime model to conduct the conversation naturally while runtime validates tool execution and context updates against the current FLOW step. Business tools execute only when they belong to the active FLOW step, preventing out-of-order actions while maintaining the authored FLOW. | New realtime voice agents that require natural conversations while maintaining FLOW integrity. | | **Runtime Owned - canonical turn** | Runtime controls every conversation turn and executes the FLOW exactly as authored. The realtime model handles conversational responses but cannot invoke business tools directly. | Workflows involving payments, bookings, orders, or other irreversible business actions and highly deterministic flows. | * Changes to the orchestration mode apply only to new realtime sessions. Calls already in progress continue using the mode that was active when the session started. * Existing channel configurations without a configured orchestration mode continue to use Legacy behavior. * **Response wording owner**: Select who generates the wording of responses for realtime voice conversations. This setting is available only when the Realtime orchestration mode is set to **Runtime Owned — canonical turn**. * **Runtime text (recommended)**: Runtime generates the response text. This option provides consistent response generation. * **Provider-grounded voice**: Uses the realtime voice provider to generate spoken responses while runtime continues to control conversation flow and business actions. This option is available only for Gemini Live. For unsupported or unsafe conversation turns, it automatically falls back to runtime-generated text responses. * **Semantic convergence**: Semantic Convergence determines how the platform coordinates with the realtime voice provider during a voice interaction. Controls whether realtime voice turns use the coordinated utterance runtime. This setting is available only when the realtime orchestration config is set to **Legacy mode**. | Mode | Description | Recommended use | | - | - | - | | **Off** | Uses the legacy realtime voice execution model. The realtime provider executes tool calls directly without semantic coordination. | Use for backward compatibility with existing voice applications. | | **Shadow** | Uses the legacy execution model while collecting diagnostics to compare legacy behavior with semantic convergence. Doesn't affect customer interactions. | Use for validation, testing, and migration before enabling **Enforce**. | | **Enforce** | Uses semantic convergence to route voice turns through the platform's coordinator, ensuring tool execution and business logic are processed consistently with the agent runtime. | Recommended for new realtime voice deployments and production environments requiring consistent behavior across voice and text. | * **S2S Provider**: The realtime speech-to-speech model the channel uses. Select one of the following: **Amazon Nova Sonic (S2S)**, **Azure OpenAI Realtime**, **Grok Realtime (S2S)**, **Google Gemini Live**, or **OpenAI Realtime**. Each option shows which credentials it uses: | Provider | Credentials used | | - | - | | **Amazon Nova Sonic (S2S)** | Voice Service credentials (Amazon Nova Sonic (S2S) Credentials). | | **Azure OpenAI Realtime**, **Grok Realtime (S2S)**, **Google Gemini Live**, **OpenAI Realtime** | The project-selected model connection. See **Realtime model connection** below. | * **Realtime model connection**: Shown for every S2S provider except Amazon Nova Sonic, which authenticates via Voice Service credentials instead. Selects the project-pinned realtime model this provider uses - shows `Use project realtime model` until a workspace admin adds one: 1. Configure the model under [Voice Services](/agent-platform/administration/ai-configuration#voice-services), then add it to the [model catalog](/agent-platform/administration/ai-configuration#model-catalog) as an LLM provider. 2. In the project, go to **Settings** -> **Model** and add the model you configured at the workspace level. * **Provider-specific settings**: After you select an S2S provider, a configuration section for that provider appears. Expand the provider below to see its settings. Under **Amazon Nova Sonic Configuration**, set the following fields. | Field | Description | | - | - | | **Voice** | The voice Nova Sonic uses to speak to callers. Default: **Tiffany**. | | **Model** | The Amazon Bedrock model ARN, for example `arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-2-sonic-v1:0`. | | **Maximum tokens** | The maximum number of tokens in a model response. Default: `1024`. | | **Temperature** | Controls response randomness. Lower values give more predictable responses; higher values give more varied responses. Default: `0.70`. | | **Top P** | Limits the model to the smallest set of likely tokens whose combined probability reaches this value. Default: `0.90`. | | **Endpointing sensitivity** | How quickly Nova decides the caller has finished speaking. **Medium** (default) suits general conversation. **High** responds sooner but may interrupt callers. **Low** allows more pause before responding. | **Pending tool fillers** Turn on **Pending tool fillers** to let Nova speak a brief hold phrase when a project tool is still running. Fast tools finish without a filler. | Field | Description | | - | - | | **Filler delay (milliseconds)** | Nova speaks only if the tool is still pending after this delay. Leave empty to use the default of `900` ms. | | **Tool filler instruction** | The instruction Nova follows to generate the hold phrase. Use `{{toolName}}` to insert the name of the running tool. Default: `The tool {{toolName}} is still in progress. Briefly tell the caller that you are working on it.` | **Coordinator turn filler** Turn on **Coordinator turn filler** to let Nova speak a short hold phrase during convergence-enabled coordinator turns while Runtime decides and executes the turn. | Field | Description | | - | - | | **Coordinator filler instruction** | The instruction Nova follows to generate the hold phrase. Default: `Briefly tell the caller that you are still working on their request.` Runtime sends this as native cross-modal input and keeps its transcript out of the canonical conversation history. | **Advanced voice behaviour (Nova-specific)** | Field | Description | | - | - | | **Failed tool call fallback** > **Tell the caller and end the call** | When a Nova tool call fails, Nova says a short apology and ends the call. Off by default. | Also configure the **Advanced voice behaviour** settings that apply to all providers, described after the provider list. Under **Azure OpenAI Realtime Configuration**, set the following fields. | Field | Description | | - | - | | **Voice** | The voice used to speak to callers. Default: **Marin (recommended)**. | | **Temperature** | Controls response randomness. Range: **Focused** (`0.6`) to **Creative** (`1.2`). Default: `0.8`. | **Turn detection** | Field | Description | | - | - | | **Speech detection sensitivity** | Adjusts how readily the model detects speech. Default: `0.5`. | | **Silence Duration (ms)** | How long the caller must be silent before the turn is treated as complete. Default: `700`. | | **Prefix Padding (ms)** | Audio kept before detected speech so the start of the caller's utterance is preserved. Default: `300`. | Also configure the **Advanced voice behaviour** settings that apply to all providers, described after the provider list. Under **Grok Realtime Configuration**, set the following fields. | Field | Description | | - | - | | **Voice** | The voice used to speak to callers. Default: **Ara**. | | **Temperature** | Controls response randomness. Range: **Focused** (`0.0`) to **Creative** (`2.0`). Default: `0.8`. | | **Threshold** | Controls speech detection. Range: **Sensitive** (`0.0`) to **Conservative** (`1.0`). Default: `0.5`. | | **Silence Duration (ms)** | How long the caller must be silent before the turn is treated as complete. Default: `500`. | | **Prefix Padding (ms)** | Audio kept before detected speech so the start of the caller's utterance is preserved. Default: `300`. | Also configure the **Advanced voice behaviour** settings that apply to all providers, described after the provider list. Under **Gemini Live Configuration**, set the following fields. | Field | Description | | - | - | | **Voice** | The voice used to speak to callers, for example **Puck — Upbeat**. | | **Enable context window compression** | Lets Gemini compress older session context so long-running Live conversations can continue beyond the model's context window. Off by default. | | **Temperature** | Controls response randomness. Range: **Deterministic** (`0.0`) to **Creative** (`2.0`). Default: `0.8`. | **Tool response scheduling** | Field | Description | | - | - | | **Tool response scheduling** | Controls how Gemini resumes after a final tool result. **Interrupt** responds immediately to the final result. **When idle — finish the current response first** (default) waits for the current response to finish. **Silent** retains the final result without speaking right away. Progress and filler updates always wait until Gemini is idle. | | **Pending tool fillers** | Lets Gemini Live acknowledge long-running project tools while the original tool call remains pending. Off by default. | **Realtime turn filler** Separate from project Voice Fillers. Applies only to Google or Vertex realtime S2S calls while semantic convergence is set to **Enforce**. | Field | Description | | - | - | | **Native turn filler** | Lets Gemini speak a short hold phrase while Runtime settles an enforced turn. It never applies to transfers, handoffs, or protected playback. | | **Generative native filler** | Lets Gemini choose a short, contextual hold phrase in the current language instead of using the deterministic phrase pool. | | **Generative filler prompt override** | Overrides the prompt Gemini uses to generate the hold phrase. Default: `Reply with a short, natural hold phrase in the current language.` | **Filler delivery safeguards** Shared by pending-tool and realtime turn fillers. | Field | Description | | - | - | | **Settlement watchdog** | Time allowed for filler generation or a held final response to settle before Runtime releases the safe fallback. Accepted range: `1000`–`60000` ms. Leave empty to use the platform default of `10000` ms. | **Voice activity detection** Controls when Gemini starts listening to a caller turn and how long it waits before closing that turn. | Field | Description | | - | - | | **Start Sensitivity** | Higher detects speech sooner but may trigger on noise. Lower waits for a stronger speech signal. Default: **Default**. | | **End Sensitivity** | Higher ends turns sooner for lower latency. Lower allows longer pauses before Gemini responds. Default: **Default**. | | **Silence Duration (ms)** | Milliseconds of non-speech before Gemini closes the caller turn. Increase to allow pauses; decrease for faster responses. Default: `100`. | | **Prefix Padding (ms)** | Milliseconds of detected speech required before Gemini commits a speech start. Lower values catch shorter utterances but can increase false starts. Default: `20`. | **Caller transcription** | Field | Description | | - | - | | **Caller language** (Optional) | The language callers speak, such as `en-US`. If left empty, Gemini guesses the language on every turn, which can turn English into another script. The call's language setting, when there is one, takes precedence. | **Silent-turn recovery** | Field | Description | | - | - | | **Recover silent turns** | Re-prompts, then restarts, a Gemini session that stays silent when it owes the caller an answer. | | **Recover unanswered caller turns only** | Limits recovery to caller turns that received no answer. Included automatically when **Recover silent turns** is on. If Gemini hears the caller but does not respond within 8 seconds, the Gemini session restarts with the caller's words, without re-prompting tool results. | **Advanced voice behaviour (Gemini-specific)** | Field | Description | | - | - | | **Silent resume recovery** > **Recover a silent resumed session** | Starts a fresh Gemini session when a resumed one connects but never speaks. Off by default. | Also configure the **Advanced voice behaviour** settings that apply to all providers, described after the provider list. Under **OpenAI Realtime Configuration**, set the following fields. | Field | Description | | - | - | | **Voice** | The voice used to speak to callers. Default: **Marin (recommended)**. | **Turn detection** Tune how OpenAI detects when speech starts and when the caller has finished speaking. | Field | Description | | - | - | | **Turn detection mode** | How OpenAI detects turns, for example **Server VAD — audio activity**. | | **Speech detection sensitivity** | Range: **More sensitive** (`0.0`) to **More conservative** (`1.0`). Lower values pick up quieter speech; higher values reduce background-noise triggers. Default: `0.5`. | | **Silence Duration (ms)** | Quiet time before OpenAI treats the caller turn as complete. Default: `700`. | | **Prefix Padding (ms)** | Audio kept before detected speech so the beginning of the caller's utterance is preserved. Default: `300`. | **Caller transcription** How the caller's speech is written down. This text is stored as the caller's turn and is what the agent checks answers against. | Field | Description | | - | - | | **Caller transcriber** | The transcription model, for example **Whisper (default)**. | | **Caller language** (Optional) | The language callers speak, such as `en-US`. If left empty, the transcriber guesses the language on every turn, which can turn English into another script. | | **Words to expect** | Brand names and terms callers use, for example `Acme, Rewards, email addresses spelled out letter by letter`. Used by Whisper and GPT-4o Transcribe. | Never put a caller's own details in **Words to expect**. The transcriber would then hear them from anyone. Also configure the **Advanced voice behaviour** settings that apply to all providers, described after the provider list. * **Advanced voice behaviour (all providers)**: Optional behaviours for this channel. Each one is off, empty, or set to the earlier platform behaviour until you change it. | Field | Description | | - | - | | **Caller wording** > **Wording a caller turn runs on** | Select the wording the agent runs a caller turn on: **Model's wording** (default), **Auto: prefer the caller's transcript**, or **Caller transcript**. | | **Default transfer announcement** > **Line spoken before a transfer** (Optional) | The line the agent speaks before a transfer, for example `One moment while I connect you.` | * **Noise isolation**: Applies server-side noise reduction to inbound call audio to filter out background voices and ambient noise. * **Voice transfer and survey capabilities**: A read-only panel that shows which transfer and survey capabilities Runtime supports for the selected provider. ### Manage an existing realtime LLM voice channel Open a channel from **Deployments** > **Channels** > **Voice** > **Realtime LLM Voice**. It contains three tabs: **Overview**, **Configuration**, and **Deployment**. **Connection Summary** displays the following fields. | Field | Description | | - | - | | **Status** | For example: `Active` | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | For example: `dev` | | **Source Type** | Realtime LLM Voice Channel | | **Source ID** | Unique identifier (UUID) of channel | The channel includes the same **General** (Display Name), **Provider Verification Strength**, call routing, **Realtime LLM Voice Settings**, and provider-specific settings (which vary by the selected S2S provider) described in [Set up Realtime LLM Voice](#set-up-realtime-llm-voice-channel). You can edit these settings any time and with immediate effect. The **Deployment** tab displays these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown (for example: `Development`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Displays the bound deployment version and when it was deployed. | Click **Save Changes** after switching the environment/version. ## Set up Pipeline Voice channel It's a voice channel that handles calls through separate speech-to-text, LLM, and text-to-speech stages, connected through Kore.ai Voice Gateway. Unlike Realtime LLM Voice, which uses a single speech-to-speech model, this pipeline lets you choose and configure the ASR (speech-to-text) and TTS (text-to-speech) providers independently. Choose your voice provider before you begin - it determines which additional fields apply later in this setup. To configure a Pipeline Voice channel, go to **Deployments** > **Channels** > **Voice** > **Pipeline Voice** > **+ New** and complete the following steps in order. On the **New Pipeline Voice Connection** page, provide the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection. | | **Connection Name** | A system name for the connection, such as `pipeline-voice-prod`. Up to 255 characters. | | **Environment** | The deployment the connection binds to. Select an environment to follow its active deployment automatically, or leave the default to use the working copy. When no environment has an active deployment yet, create a deployment first to bind the connection to a published environment. | | **Provider Verification Strength** | Controls how strictly the platform trusts the provider's identity assertions for session continuity and linking. **Weak** is set by default. Choose **Strong** only for channels/providers whose identity assertions you trust for stronger continuity and linking decisions. | | **Initial Status** | **Active** connections are usable immediately. **Inactive** connections stay saved but disabled until you activate them. | Click **Create**. After creating the channel, go to the **Configuration** tab. Under **Pipeline Voice Settings**, set **Voice Provider** to either **Kore.ai Voice Gateway** or **Bring Your Own SIP** — this determines which call routing fields appear next. If you chose **Kore.ai Voice Gateway**, set the following fields under **Call routing**. Choose how calls reach this agent — enable Soft Phone for browser testing, or add a DID when this channel needs a real phone number. | Route | Description | | - | - | | **Soft Phone** (FREE) | Dial the agent from your browser. No DID purchase or telephony charges. Use this for evals, QA, and demos. Select **Launch dialer** to open the browser dialer. | | **Phone Number (DID)** | Purchase a real phone number through Twilio. Disable this route to stop DID listing and call routing while keeping the number saved. | If you enable **Phone Number (DID)**, complete the next step. If you chose **Voice Provider: Bring Your Own SIP** instead, skip to Configure SIP Settings. Set the **Provider** to **Twilio**, toggle **Inbound calls** (route calls from this number into the voice agent) and **Outbound calls** (allow this number to be used for campaign dialout), and select a **Country** and **Number Type**. Optionally enter an **Area Code** to filter available local numbers. Select **Search Available Numbers** to list matching numbers. If you chose **Voice Provider: Bring Your Own SIP**, set the following fields under **SIP Configuration**. | Field | Description | | - | - | | **Our SIP Server Address** | Configure your SIP provider to send `INVITE` to this address, for example `korevg-np.kore.ai:5060`. Select **Copy** to copy the address. | | **SIP Gateway IPs / CIDRs** | Provider SIP source addresses. Separate multiple IPv4 addresses or CIDR ranges with commas, for example `203.0.113.50:5080, 3.80.16.0/23`. | | **SIP Termination URI** | The termination address your SIP provider expects outbound calls to reach, as either an **IP Address** or **Domain Name**. Enter the URI in the field below the selected type. | | **DID / Phone Number** | The phone number (DID) your SIP provider routes to this SIP gateway, for example `+14155551234` or `98492`. | Toggle **Enable E.164 (leading +)** to prepend a leading `+` to outbound numbers sent to the carrier - on by default, which international (full-E.164) numbers require. Turn it off only for local/national-format numbers, since the leading `+` otherwise misroutes them. Under **Speech Recognition**, set the following fields. | Field | Description | | - | - | | **Provider** | The speech-to-text credential set to use, for example `Deepgram Speech Credentials`. | | **Language** | The recognition language, for example `Multilingual`. | | **STT Model** | The recognition model, for example `Nova 3 (latest, most accurate)`. | | **Minimum STT confidence** | Reject transcripts below this confidence (0-1) and treat them as no-input. `0` disables the gate. Provider-relative - calibrate per channel. | | **Custom vocabulary** | Add domain-specific terms to improve speech recognition. You can set the **Phrase** (for example `ACME, policy number`) and optional **Boost**. For **Emotech**, add **hotwords** (domain-specific words or phrases you want the speech recognition system to recognize more reliably) and set the **Hotword Weight**. A value from 5 to 10 is recommended. Clear the custom vocabulary list to disable hotwords. | | **Multilingual speech recognition** | Choose the recognizer for each configured language. Enable **A different recognizer per language** to select the speech recognizer for each language. Add a language profile before configuring language-specific recognition. | Under **Speech Synthesis**, set the following fields. | Field | Description | | - | - | | **Voices by language** | Enable this option to configure separate text-to-speech settings for each language supported by the agent. When disabled, the channel uses a single TTS configuration for all languages. | | **Language** | Select the language for which you want to create a TTS profile, then click **Add language**. | | **Provider** | The text-to-speech credential set to use for the selected language, for example `ElevenLabs (TTS) Credentials` or `Deepgram Speech Credentials`. | | **Voice** | The voice used for synthesized speech for the selected language, for example `Sarah - Mature, Reassuring, Confident - (american, young, female)`. | | **Switch TTS for detected language** | When enabled, calls can change the TTS language, voice, and model after speech recognition detects a different language. | * After you click **Add language**, the platform creates a dedicated TTS configuration section for that language. Configure the provider, voice, model, and any provider-specific settings independently for each language. * Available fields vary by provider. Some providers expose additional settings such as TTS model, output format, speed, temperature, and voice tuning controls. Each language profile maintains its own provider configuration, allowing multilingual agents to use different voices and models for different languages. If you selected an ElevenLabs voice, override playback and prosody for this channel connection under **ElevenLabs Voice Settings**. | Field | Description | | - | - | | **TTS Model** | The synthesis model, for example `Eleven Multilingual v2`. | | **Speed** | Adjusts speaking pace. `1.0` is normal; lower slows the voice down, higher makes it faster. Range: `0.7`–`1.2`. | | **Stability** | Controls consistency across generations. Higher is more stable and less expressive; lower allows more variation. Range: `0`–`1`. | | **Similarity boost** | Keeps the output closer to the selected voice. Higher values preserve more of the original voice character. Range: `0`–`1`. | | **Style exaggeration** | Amplifies voice style and emotion. Higher values can sound more expressive but may increase latency or instability. Range: `0`–`1`. | | **Speaker boost** | Enhances speaker similarity and clarity. Turning it off may reduce latency for some voices. | Under **Preview Voice**, enter sample text (up to 500 characters) and select **Generate** to hear the configured voice before saving. Set the following fields. | Field | Description | | - | - | | **Barge-in** | Allow the caller to interrupt the agent while it is speaking. | | **Speech Timeout (ms)** | Controls how long the speech provider waits through silence before finalizing the current speech segment. This does not combine separate final results. For example, `3000`. | | **Transcript Grouping Timeout (ms)** | After the speech provider returns a final result, waits this long for another final result and combines the parts into one user input. Leave blank to process each final result immediately. For example, `1500`. | | **Welcome Message** | The opening prompt the agent speaks, such as `Hello! How can I help you today?`. | | **Noise isolation** | Apply server-side noise reduction to inbound call audio to filter out background voices and ambient noise. | Under **Connect greeting** (audio), optionally play a clip as soon as the call connects, before the agent's first turn. If the project's greeting-audio library has no clips yet, the panel shows a note and an **Open Greeting Audio** link to add one. Click **Save Changes** to apply the configuration. ### Manage an existing Pipeline Voice channel Open a channel from **Deployments** > **Channels** > **Voice** > **Pipeline Voice**. It displays the following three tabs. The **Connection Summary** displays these fields. | Field | Description | | - | - | | **Status** | for example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | for example: `dev` | | **Source Type** | `Channel Connection` | | **Source ID** | Unique identifier (UUID) of the channel | Contains the same **General** (Display Name, Provider Verification Strength), voice provider and call routing, **Speech Recognition**, **Speech Synthesis** (including ElevenLabs voice tuning and preview, where applicable), and **Conversation Controls** described in [Set up Pipeline Voice](#set-up-pipeline-voice) above, editable at any time. Changing any of these settings here updates the live channel immediately after **Save Changes**. The **Deployment** tab displays these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** menu (for example: `Development`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Displays the bound deployment version and when it was deployed. | Click **Save Changes** after switching the environment or the version. # Set up Web SDK channel Source: https://koreai.mintlify.app/agent-platform/channels/web-sdk-channel [Back to SDK](/agent-platform/channels#sdk-channels) Embed a chat widget in your website with a single script tag. Web SDK is a deployment channel that connects a chat and voice widget on your site to a specific agent deployment. ## Create a Web SDK channel The Web SDK channel connects a browser-based chat or voice widget to your agent. It supports two authentication modes — **Anonymous / Public-key bootstrap** for client-only apps, and **Hosted token exchange** for setups where a backend can hold a server secret. Choose your authentication mode before you begin — it determines which additional fields apply later in this setup. To configure a Web SDK channel, go to **Deployments** > **Channels** > **SDK** > **Web SDK** > **+ New** and complete the following steps in order. On the **New Web SDK Connection** page, provide the following fields. | Field | Description | | - | - | | **Display Name** | A label for the connection (up to 255 characters). | | **Environment** | The environment this channel follows; it tracks that environment's active deployment. | | **Authentication** | How customer browsers bootstrap SDK sessions. Select **Anonymous / Public-key bootstrap** or **Hosted token exchange**. | | Mode | When to Use | | - | - | | **Anonymous / Public-key bootstrap** | Browser or client app. Store the public `pk_*` key, runtime endpoint, and channel selection in client-side configuration. No ABL secret is required, so you provision nothing extra on the customer server. | | **Hosted token exchange** | Customer backend. The backend stores a reveal-once `sk_*` server secret and issues one-time `bootstrapToken` values; the browser stores only the short-lived token, runtime endpoint, and project ID. | If you select **Hosted token exchange**, complete the next step. Otherwise, skip to Select the Public SDK Key. The **Token envelope policy** controls whether Hosted Exchange SDK bootstrap and session credentials are signed or encrypted. | Option | Meaning | | - | - | | Use runtime default | Defers to the project-level default Hosted Exchange token envelope policy | | Signed tokens | Issues signed bootstrap/session tokens only | | Prefer encrypted tokens (`jwe_preferred`) | Uses encrypted (JWE) tokens when Runtime's encryption capability is ready; falls back to signed tokens otherwise | | Require encrypted tokens (`jwe_required`) | Requires encrypted issuance and verification end to end; fails closed rather than returning a signed token | Select the **Public SDK Key** the browser will use during SDK bootstrap, then select **Create**. Once the channel is created, select it, then go to the **Configuration** tab to update the following fields: | Field | Description | | - | - | | **Allowed Origins (CORS)** *(optional, both modes)* | One URL per line, for example, `https://example.com`, `https://*.example.com`. These origins are enforced for the public key linked to this SDK channel. | | **Server secret** *(Hosted token exchange only)* | Studio generates the `sk_*` server secret automatically on creation and reveals it once. Copy and store it in your customer backend immediately, since it cannot be viewed again. **Server Secret Status**, **Server Secret Prefix**, and **Last Rotated** are shown afterward; use **Rotate server secret** to issue a new one — the previous secret stops working immediately. | | **Customer-issued encrypted bootstrap** *(Hosted token exchange only, toggle, off by default)* | Accept customer-backend-issued JWE bootstrap tokens so sensitive and identity data can be encrypted before it reaches Runtime. | Regardless of authentication mode, the same widget behavior settings apply, under **Web SDK Settings**. | Field | Description | | - | - | | **Widget Mode** | `Chat Only`, `Voice Only`, or `Chat + Voice`. | | **Features** | Toggle **Chat** and **Voice** independently. | | **Show Agent Activity Updates** | Off by default. Displays transient handoff, delegate, and reasoning activity for this SDK channel. | | **Theme** | Choose a preset (for example `Light`) or select **Add themes** to tune the widget to match your host site. | | **Welcome Message** | The first message shown to users, for example "Hello! How can I help you today?" | Click **Save Configuration**, then **Save Changes** to apply the configuration. ## Manage an existing channel Opening a channel from **Deployments** > **Channels** > **Web SDK** shows four tabs: **Overview**, **Configuration**, **Deployment**, and **Testing**. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | for example: *Active* | | **Created** | Timestamp the channel was created | | **Updated** | Timestamp of the last change | | **Environment** | for example: `dev` | | **Source Type** | `SDK Channel` | | **Source ID** | Unique channel identifier (UUID) | Contains the same **General** (Display Name), **Authentication**, and widget behavior settings described in [Create a Web SDK Channel](#create-a-web-sdk-channel) above, editable at any time. Changing the auth mode, token envelope policy, allowed origins, or public key here updates the live channel immediately after **Save Changes**. The **Deployment** tab shows these fields. | Field | Description | | - | - | | **Agent Version** | An **Environment** dropdown (for example: `Development`) that selects which deployed version handles incoming messages. | | **Active Deployment** | Shows the bound deployment version and when it was deployed. | Click **Save Changes** after switching the environment/version. Select **+ Generate Link** under **Secure Preview Link** to create a secure, time-limited link to preview your widget. The link expires after 7 days. Copy the **Embed Code** the tab provides, for example: ```html theme={null} theme={null} ``` 1. Copy the above embed code. 2. Replace the API key placeholder with your actual key. 3. Paste the code before the closing `` tag. 4. The widget will appear on your site automatically. The embed code is generated per channel and already includes the correct `project-id`, `endpoint`, and `channel-id` for this connection — only the API key placeholder needs to be replaced. # Webhook Source: https://koreai.mintlify.app/agent-platform/channels/webhook *** ## title: Set up Webhooks (HTTP Async) [Back to webhook channels](/agent-platform/channels#webhook-channel) Send messages to your agent over HTTP and receive the agent's reply as a callback to your own server. Artemis accepts the message, acknowledges it immediately, then posts the completed response to your callback URL as a separate request. Use this channel for server-to-server integrations where your application owns the user interface and can't hold a connection open while the agent works. If your application needs the reply on the same connection, use the API channel instead. ## How the exchange works The channel splits one conversational turn into two HTTP exchanges. Your application sends the message and receives an acknowledgement, then Artemis calls your server back with the agent's answer. ```mermaid actions={false} theme={null} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4', 'actorBkg': '#EAF8E4', 'actorBorder': '#5CC83A', 'actorTextColor': '#1a1a1a', 'actorLineColor': '#5CC83A', 'signalColor': '#5CC83A', 'signalTextColor': '#2F9E1E', 'labelBoxBkgColor': '#EAF8E4', 'labelBoxBorderColor': '#5CC83A', 'labelTextColor': '#1a1a1a', 'loopTextColor': '#2F9E1E', 'noteBkgColor': '#EAF8E4', 'noteBorderColor': '#5CC83A', 'noteTextColor': '#1a1a1a', 'activationBorderColor': '#5CC83A', 'activationBkgColor': '#EAF8E4', 'sequenceNumberColor': '#2F9E1E'}}}%% sequenceDiagram participant Client as Client application participant Artemis as Artemis HTTP Async participant Agent as Agent runtime participant Callback as Client callback API Client->>Artemis: 1. POST message + project/subscription Artemis-->>Client: 2. 202 Accepted + id Artemis->>Agent: 3. Resolve, queue, execute Agent-->>Agent: 4. Suspend / resume if needed Agent-->>Artemis: 5. Response ready Artemis->>Callback: 6. POST callback payload Callback-->>Artemis: 7. 200 OK Note over Client,Callback: Correlate: session_key → session_id → message_id ``` | Stage | What happens | Identifiers | | - | - | - | | 1. Your server to Artemis | Your application posts the message to the HTTP Async endpoint. | `project_id`, `subscription_id`, `session_key` | | 2. Artemis to your server | Artemis returns `202 Accepted` to confirm it queued the message. | `message_id`, `session_key` | | 3. Inside Artemis | Artemis resolves the project, deployment, agent, and session, then queues execution. | `session_id` | | 4. Inside Artemis | The agent runs. Long-running tools can suspend execution and resume it later. | Suspension ID, when applicable | | 5. Artemis to your server | Artemis posts the completed agent response to your callback URL. | `message_id`, `session_key` | | 6. Your server to Artemis | Your callback endpoint returns `200 OK` and stores the event idempotently. | `message_id` | The `202 Accepted` response confirms acceptance only. Treat the callback as the business response, and don't resend a message while Artemis is still processing it. ## Before you begin Complete the following before you create the connection. You need an active deployment, a Platform API key, and an HTTPS endpoint that receives the callback. Deploy the agent to the target environment and confirm which deployment handles incoming messages. Use a deployment rather than a draft version, and tell your integration team which agent answers first. Go to **Deployments** -> **API Keys** -> **Platform keys** and create a key for this integration. Assign only the scope that authorizes HTTP Async message submission. Don't grant deployment administration, project write, or API key administration scopes. Record the exact scope label your tenant shows, because the deployed platform version determines those labels. Store the key in your secret manager, and define rotation, expiry, ownership, and emergency revocation before you go to production. Never put the key in the request body or in source code. Stand up an HTTPS endpoint that accepts a JSON `POST`, returns `200 OK` quickly, and processes each `message_id` once. Artemis calls this endpoint with the agent response, so allow traffic from the Artemis environment you're integrating with. ## Create the channel connection in Artemis Each connection creates a webhook subscription that holds your callback URL, the events you want, and its own identifier. Open **Deployments** -> **Channels** -> **WEBHOOK** -> **Webhooks (HTTP Async)** and expand **Setup instructions** to see the current guidance for your environment. Select **+ New** and supply the following. | Field | Description | | - | - | | **Display Name** | A label for the connection, for example `Order Support Callback`. The connections list shows this name. | | **Callback URL** | The HTTPS endpoint on your server that receives agent responses. Artemis posts a JSON body to this URL for every event you subscribe to. | | **Event Subscriptions** | The events Artemis delivers to your callback URL. Choose **Agent Response** (`agent.response`) for completed agent replies, **Agent Status** (`agent.status`) for status transitions, and **Agent Media** (`agent.media`) for media output. | Open the connection and go to the **Overview** tab. Copy the **Source ID** from the **Connection Summary**. Your application sends this value as `subscription_id`. Take the project identifier from the Artemis URL while the project is open, in the form `/projects/{project_id}/deployments`. Go to the **Testing** tab, enter a message, and select **Send Test Message**. Open the **Activity** tab to confirm the delivery reached your endpoint. ## Send a message Your application posts to a fixed platform route and names the target integration in the request body. Replace `{host}` with the Artemis host for your environment, for example `agents.kore.ai`. For on-premise solution, see the list of [region-specific platform URLs](/agent-platform/getting-started#region-specific-urls-and-allowlist-of-ips). ```http theme={null} theme={null} POST https://{host}/api/v1/channels/http-async/message X-API-Key: abl_ Content-Type: application/json { "project_id": "01a00e62-84f9-7c41-9502-0acf40789e8b", "subscription_id": "01a08410-6544-7dd6-be6a-1a0e4d6a06e7", "message": "Hey!", "session_key": "customer-002", "metadata": { "customerName": "John" } } ``` The `project_id` and `subscription_id` route the request. The `X-API-Key` header authenticates it. ### Request fields The body carries the routing identifiers, the message text, and your conversation key. | Field | Required | Description | | - | - | - | | `project_id` | Yes | The Artemis project that owns the webhook subscription. | | `subscription_id` | Yes | The **Source ID** from the connection's **Overview** tab. Don't substitute an identifier from a different channel. | | `message` | Yes | The user message text you want the agent to answer. | | `session_key` | Yes | Your conversation key. Send a key of your own on the first message, for example a customer ID. On every follow-up message, send the exact canonical `session_key` that Artemis returned in the acknowledgement. | | `metadata` | No | Business context your application owns. Artemis returns this object unchanged in the callback. Keep secrets and correlation keys out of it. | The request doesn't take a `session_id` field. A unique `session_key` on its own creates the session. ### Acknowledgement Artemis replies immediately to confirm it queued the message. This response isn't the agent's answer. ```http theme={null} theme={null} HTTP/1.1 202 Accepted Content-Type: application/json { "message_id": "10a49805-3eda-45ab-830a-c3c49145a1da", "session_key": "http_async:019dcf8c-4f78-759f-aa58-3dd72d26d229:01a08410-6544-7dd6-be6a-1a0e4d6a06e7:customer-002", "status": "accepted", "_note": "Message has been queued for processing. Response will be delivered to your callback URL." } ``` Save the `message_id`, the returned `session_key`, the request timestamp, `project_id`, and `subscription_id` with your conversation record. The returned `session_key` carries an Artemis namespace. Store it exactly as Artemis returns it, and don't reconstruct or alter it. ### Callback When the agent finishes, Artemis posts the response to your callback URL. ```http theme={null} theme={null} POST https://client.example.com/artemis/callback Content-Type: application/json { "message_id": "a996d19e-1703-47ee-8b9b-1df894cb3182", "session_key": "http_async:019dcf8c-4f78-759f-aa58-3dd72d26d229:01a08410-6544-7dd6-be6a-1a0e4d6a06e7:customer-002", "response": "You said: Hey2! What else can I help you with?", "channel_output": { "kind": "text", "text": "You said: Hey2! What else can I help you with?" }, "outcome": { "status": "ok", "usedFallback": false }, "trace_context": { "session_id": "5914535f-dfc4-49f6-9c3d-5301777dcd87", "delivery": "correlation_only" }, "session_id": "5914535f-dfc4-49f6-9c3d-5301777dcd87", "is_new_session": false, "response_metadata": { "isLlmGenerated": true, "responseProvenance": { "schemaVersion": 1, "kind": "mixed", "disclaimerRequired": true, "usedLlmInternally": true } }, "metadata": { "customerName": "John" } } ``` Authenticate the callback, validate the payload with your schema, persist the event, and return `200 OK` promptly. Make your endpoint idempotent, because Artemis can deliver the same event more than once. ### Callback fields The callback repeats the identifiers from the acknowledgement and adds the agent output with its provenance. | Field | Type | Description | | - | - | - | | `message_id` | String | The identifier from the acknowledgement. Use it as the idempotency key for the delivered response. | | `session_key` | String | The canonical conversation key. Use it to find your conversation record, and resend this exact value on the next request. | | `response` | String | The agent's reply as plain text. | | `channel_output` | Object | The channel-renderable form of the reply. Read `channel_output.text` for text replies. | | `outcome` | Object | The processing result, including `status` and whether the agent used a fallback. | | `trace_context` | Object | The runtime session identifier and the delivery context you use for tracing. | | `session_id` | String | The Artemis runtime session identifier for this conversation. | | `is_new_session` | Boolean | Whether Artemis created a new runtime session for this request. | | `response_metadata` | Object | Provenance for the reply, including whether an LLM generated it and whether it requires a disclaimer. | | `metadata` | Object | The `metadata` object from your request. Artemis returns it unchanged. | Artemis doesn't return `intent` or `conv_status`. If your application needs either one, agree it as an extension inside `metadata`. ## Handle errors Your application and your callback endpoint can each encounter failures. Handle each category on its own path instead of resending a message blindly. ### Request errors | Status | Cause | What to do | | - | - | - | | `401` / `403` | The `X-API-Key` is missing, invalid, expired, or doesn't carry the scope for HTTP Async message submission. | Check the key value and its assigned scope under **Deployments** -> **API Keys** -> **Platform keys**. | | `429` | Your application sent requests faster than the channel accepts them. | Back off and retry after a delay instead of resending immediately. | | `5xx` | Artemis failed to accept the request. | Retry with backoff. Don't assume the message reached the agent. | Only resend a message after a failed request. Don't resend just because the callback hasn't arrived yet — the `202 Accepted` response means Artemis already queued it. ### Callback delivery Artemis can call your callback URL more than once for the same event, and long-running or suspended tool executions can delay a callback beyond what a synchronous exchange would take. Design your endpoint to do the following. * Validate the payload against the [callback fields](#callback-fields) table before you persist it, and reject anything that doesn't match. * Process each `message_id` once, and treat a repeated `message_id` as already handled rather than as a new event. * Return `200 OK` as soon as you've accepted the event, and do the rest of your processing after you respond. ## Correlate requests and continue conversations Three identifiers carry the exchange, and each one does a different job. Mixing them up splits conversations and duplicates responses. | Identifier | What it identifies | How to use it | | - | - | - | | `message_id` | A single accepted message | Match the callback to the request that produced it, and use it as your idempotency key. | | `session_key` | Your conversation | Send the exact canonical value from the acknowledgement on every follow-up message. Don't change it between turns. | | `session_id` | The Artemis runtime session | Store it with the conversation record for tracing. Don't generate one per message. | Persist `project_id`, `subscription_id`, `session_key`, and `session_id` with each conversation record. Correlate on `message_id` and `session_key` only, never on `intent` or a `metadata` value. ## Secure the callback Artemis authenticates your outbound request with the `X-API-Key` header. Your server owns the security of the return path, so decide how you'll authenticate the callback before you go to production. Serve the callback endpoint over HTTPS and reject unauthenticated requests. HMAC signing or mTLS suits high-assurance integrations, and bearer token authentication works where that matches your approved configuration. Agree the mechanism with your Artemis delivery team, then test it end to end. Async tool callbacks follow a different rule. Artemis documents a per-suspension HMAC-SHA256 signature in the `X-Callback-Signature` header, which applies when an external system calls the Artemis suspension callback. It doesn't apply when Artemis calls your webhook. ## Manage the channel connection Opening a connection from **Deployments** -> **Channels** -> **WEBHOOK** -> **Webhooks (HTTP Async)** shows five tabs. The controls beside the connection name pause or delete the subscription. The **Connection Summary** shows these fields. | Field | Description | | - | - | | **Status** | The current state of the subscription, for example *Active*. | | **Created** | Date and time of creation. | | **Updated** | Date and time of the last change. | | **Environment** | The environment this subscription belongs to, for example `Working Copy (draft)`. | | **Source Type** | `Webhook Subscription`. | | **Source ID** | The subscription identifier (UUID). Your application sends this value as `subscription_id`. | **Setup Instructions** shows the **Callback URL** currently registered for the subscription, with a control that copies it. Under **General**, update the **Display Name**. Under **Webhooks (HTTP Async) Settings**, choose where Artemis sends events and which events it sends. | Field | Description | | - | - | | **Callback URL** | The HTTPS endpoint on your server that receives agent responses. Changing it redirects every later delivery for this subscription. | | **Event Subscriptions** | The events Artemis delivers. Choose **Agent Response** (`agent.response`) for completed agent replies, **Agent Status** (`agent.status`) for status transitions, and **Agent Media** (`agent.media`) for media output. | Select **Save Changes** to apply your changes. This tab doesn't bind the subscription to a deployment. A webhook subscription receives events from every active deployment in the project, so the tab offers no environment or version selector. Use this tab to confirm the subscription reaches your endpoint before you wire up your application. Enter text in **Test Message** and select **Send Test Message**. Artemis sends the message through the webhook subscription and records the result on the **Activity** tab. **Recent Deliveries** lists the delivery attempts Artemis made for this subscription, and **Refresh** reloads the list. Check this tab to confirm a test message reached your endpoint, and to review delivery status when your application stops receiving callbacks. # Set up WhatsApp Source: https://koreai.mintlify.app/agent-platform/channels/whatsapp [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital-channels) Connect your agent to a WhatsApp Business sender so customers reach it from WhatsApp. Artemis reaches WhatsApp through a Business Solution Provider, and the provider you choose decides which credentials you collect, which webhook route you register, and where the sender lives. The setup requires: 1. [Choose a provider](#choose-a-provider-first), then complete either [Meta setup](#meta-setup) or [Infobip setup](#infobip-setup) to collect the routing identifier and credentials. 2. [Artemis channel configuration](#artemis-channel-configuration) using those values, then copy the generated webhook URL. 3. [Webhook configuration with your provider](#webhook-configuration-with-your-provider): register the callback URL so inbound messages reach Artemis. After you complete the configuration, your provider posts inbound WhatsApp messages to Artemis, which matches the sender identifier to this connection and hands the message to the deployed agent. **Important considerations** * **The provider selector changes the whole setup**: It decides which credential fields the dialog shows and which webhook route you register. Credentials from one provider never work with another's route, so pick the provider before you collect anything. * **Who owns the WhatsApp Business account differs by provider**: On Meta Cloud API, you hold it directly and Meta issues a test sender in minutes. That's why most projects start there. On Infobip, Infobip holds it and onboards the sender for you, which removes the Meta paperwork at the cost of a provisioning wait. * **The Meta test token expires within 24 hours**: To test and not to run. See [Create a permanent access token](#create-a-permanent-access-token). * **Verification proves less than it appears to**: A webhook that returns 2xx proves only that your provider reached the runtime. It says nothing about whether your outbound credentials work. ## Prerequisites * An account with your chosen Business Solution Provider: A Meta developer account, or an Infobip account. * For production on either path, the business details Meta requires for verification. Both routes wait on Meta review, so start early. * An HTTPS-reachable Artemis runtime, since Meta calls the callback URL during verification. * Artemis project access with permission to open **Deployments** -> **Channels**, and a deployed agent in the environment you intend to bind. *** ## Choose a provider first Pick the provider before you collect anything, and keep the identifier, credentials, webhook route, and provider-side sender on that single path. | Provider | Routing identifier | Credentials | | - | - | - | | **Meta Cloud API** | Phone Number ID | Access Token and Phone Number ID for outbound, App Secret and Verify Token for inbound verification | | **Infobip** | WhatsApp phone number, digits only | API Base URL with an API key or basic auth, plus a network allowlist for inbound verification | | **Gupshup** | Business number the provider sends in the callback body | API username and password, plus an optional bearer token secret for inbound verification | Each provider also has its own webhook route. Meta uses the generic WhatsApp route, while Infobip and Gupshup each add their provider slug to the URL. Copy the route that matches the provider you selected in Artemis rather than assuming one URL serves all three. Work through the provider section that matches your choice, then rejoin the shared path at [Artemis channel configuration](#artemis-channel-configuration). *** ## Meta setup Complete this section only if you selected Meta Cloud API. Everything here happens in Meta, before you open Artemis, and each screen depends on the one before it. Meta covers the same ground in its own words in the [Cloud API get started guide](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started). 1. Go to [Meta for Developers](https://developers.facebook.com/) and sign in with your Facebook account. 2. Complete developer registration when Meta prompts you, confirming an email address and a phone number. A personal Facebook account gets you started. You need a Meta Business portfolio only when you move past the test sender. 1. Open [My Apps](https://developers.facebook.com/apps/) and select **Create App**. 2. When Meta asks what the app does, choose the business messaging use case, or choose **Other** and then **Business** as the app type if Meta shows you that older flow. 3. Enter an app name and a contact email, attach a Meta Business portfolio if you have one, and create the app. The app type matters, because only a business app exposes the WhatsApp product. Meta opens the App Dashboard once the app exists. 1. On the App Dashboard, scroll the **Add products to your app** list to **WhatsApp** and select **Set up**. If you've navigated away from that screen, the left menu carries an **Add product** entry that returns you to the same list. Meta provisions several things at once: a test WhatsApp Business Account, a test business phone number, and a set of pre-approved message templates. The test number reaches up to five verified recipients at no cost. The left menu now shows a **WhatsApp** section containing **API Setup** and **Configuration**, which are the two screens you'll use. All of this is test infrastructure. It works for development and none of it works for production. Go to **WhatsApp** -> **API Setup** in the left menu. This screen carries most of what Artemis needs. 1. Select **Generate access token** and complete the prompt, then copy the token. Meta issues a fresh one each time you open this panel, and it expires within 24 hours. 2. Below the **From** field, Meta shows the test business phone number, and directly beneath that, a numeric **Phone Number ID**. Copy the ID. The Phone Number ID isn't the phone number. It's Meta's internal identifier for the sender, and entering the displayed number instead is the most common reason a connection never receives traffic. The two values sit next to each other on this screen, which is exactly why they get swapped. See [Create a permanent access token](#create-a-permanent-access-token) before you move past testing. The App Secret lives outside the WhatsApp section, so the left menu is the only way to reach it. 1. Go to **App settings** -> **Basic**. 2. Select **Show** beside **App secret**. 3. Re-enter your Facebook password when Meta asks, then copy the value. Meta signs every inbound callback with this secret, and Artemis checks that signature. A wrong value here means every inbound message fails verification even though the webhook itself verified successfully, which makes it a confusing failure to diagnose later. Choose any secret string. Meta doesn't issue this one, so you invent it and use the same value in two places: the Artemis connection, and the webhook configuration in Meta. A mismatch fails webhook verification. Treat it as a secret rather than a label, and store it where you can retrieve it, because Meta shows it back to you only while you edit the webhook. While the app stays in development mode, Meta delivers only to numbers you register, so your own number receives nothing until you add it. 1. Return to **WhatsApp** -> **API Setup**. 2. Select the **To** field and choose **Manage phone number list**. 3. Add your WhatsApp number, then enter the confirmation code Meta sends you in WhatsApp. You can register up to five recipients. Select your number in the **To** field once it verifies. *** ## Infobip setup Complete this section only if you selected Infobip. Infobip owns the WhatsApp Business account and the sender. You collect three things from the Infobip web interface instead of from Meta: the sender number, your account's API base URL, and an API key. 1. Sign in to the [Infobip web interface](https://portal.infobip.com/login/). 2. Go to **Channels and Numbers** -> **Channels** -> **WhatsApp** -> **Senders**. 3. Note the sender number. Artemis wants it as digits only, with no plus sign, spaces, or punctuation. A trial account comes with a shared test sender that reaches numbers you verify, which is enough to prove the integration works. A production sender goes through Infobip onboarding, where Infobip registers the number with Meta and runs business verification on your behalf. Infobip's [WhatsApp over API guide](https://www.infobip.com/docs/whatsapp/whatsapp-over-api) covers what the channel supports once the sender is live. Start that process early, because it waits on Meta review rather than on Infobip. Infobip gives every account a personalized base URL in the form `xxxxx.api.infobip.com`, which you'll see on the homepage once you sign in. Infobip explains the scheme under [base URL](https://www.infobip.com/docs/developer-resources/about-infobip-apis/base-url). This URL isn't a credential. It routes your requests to the data center serving your account, so a wrong one degrades or fails the integration rather than exposing it. Enter it in Artemis with the `https://` scheme. 1. Go to **Developer Tools** -> **API Keys** and create a key for this integration. 2. Copy the key when Infobip displays it and store it in your secret manager. Infobip also shows an existing key on the homepage, but a dedicated key per integration lets you revoke this one without disturbing anything else. If your account authenticates with a username and password instead, note those rather than a key, and select basic authentication as the method when you create the connection. *** ## Artemis channel configuration Create the connection before you register the webhook with your provider. Provider callbacks can arrive as soon as you save the webhook, and the runtime has to resolve an inbound sender to a saved connection before it can authenticate the event. 1. Open Artemis and select the project. 2. Go to **Deployments** -> **Channels** -> **WhatsApp** -> **+ New**. The **New WhatsApp Connection** dialog opens. 3. Configure the fields that apply whichever provider you chose: | Artemis connection field | Source / Notes | What it controls | | - | - | - | | Display Name | You choose it | The label operators see in the connections list. Name it for the sender and the environment it serves, because a project can hold several WhatsApp connections. Routing ignores this value. | | Provider | Your chosen Business Solution Provider | Decides which credential fields the dialog shows and which webhook route you register later, so set it before you fill in anything below it. | | Environment | Your published environments | The environment whose active deployment answers incoming messages. Leave the working copy default only while you develop, and bind the connection to an environment later from the **Deployment** tab. | | Provider Verification Strength | Defaults to **Weak** | How far the runtime trusts the provider's identity assertion when it links a sender to an existing session. Choose **Strong** only when you trust the provider's assertions enough to base continuity and account-linking decisions on them. | | Initial Status | Defaults to **Active** | Whether the connection starts handling messages. **Active** takes traffic immediately. **Inactive** saves the configuration in a disabled state, which suits a connection you're staging ahead of a launch. | 4. Configure the routing identifier and credential fields, which change with the provider: | Artemis connection field | Source / Notes | What it controls | | - | - | - | | Phone Number ID | **WhatsApp** -> **API Setup**, below the test phone number | Routes inbound messages to this connection. The runtime matches the sender identifier on each callback to this value, so a wrong entry here produces a connection-not-found result rather than an authentication error. | | Access Token | **WhatsApp** -> **API Setup**, or a system user token for production | Authenticates every outbound message Artemis sends. | | Phone Number ID | Same value as the routing identifier | Identifies the sender on outbound messages. | | App Secret | **App settings** -> **Basic** | Verifies the signature Meta puts on each inbound callback. Without it, Artemis can't confirm that a callback came from Meta. | | Verify Token | You invent it | Meta echoes it during the webhook handshake, and Artemis compares the two. | Meta Cloud API asks for the Phone Number ID twice, once as the routing identifier and once as an outbound credential. Enter the same value in both. Other providers use different values in each field, which is why they stay separate. | Artemis connection field | Source / Notes | What it controls | | - | - | - | | WhatsApp Phone Number | **Channels and Numbers** -> **Channels** -> **WhatsApp** -> **Senders** | Routes inbound messages to this connection. Enter the sender number as digits only, because a stray plus sign or space produces a connection-not-found result. | | Authentication Method | Matches how your Infobip account is set up | Whether Artemis authenticates with an API key or with a username and password. | | API Base URL | The Infobip homepage, in the form `xxxxx.api.infobip.com` | Your account's personalized host, entered with the `https://` scheme. Requests to the wrong base URL take a suboptimal route or fail outright. | | API Key | **Developer Tools** -> **API Keys** | The key Artemis sends on every outbound call to Infobip. The dialog shows credential fields for basic authentication instead when you select that method. | Infobip doesn't sign its callbacks with a shared secret the way Meta does, so Artemis verifies inbound traffic by source network. Confirm with your Infobip contact which addresses their platform sends from, and allowlist those before you go live. 5. Select **Create**. 6. Open the connection and copy the **Webhook URL** from the **Overview** tab. The URL carries the route for the provider you selected, so copy it rather than constructing it. If the dialog reports that the runtime URL is unavailable, configure Runtime first, then reopen the connection. *** ## Webhook configuration with your provider The webhook is what turns a one-way integration into a conversation. Until your provider has a callback URL, your agent can send but never receives. In your Meta app, go to **WhatsApp** -> **Configuration** in the left menu. The **Webhook** row sits at the top of this screen. 1. Select **Edit**. Meta opens a dialog with two fields. 2. Paste the Artemis **Webhook URL** into the callback URL field, and the same **Verify Token** you entered in Artemis into the verify token field. 3. Select **Verify and save**. Meta immediately calls the URL with a challenge and expects your verify token back, so the dialog stays open with an error when the handshake fails. A dialog that closes means the URL verified. 4. Below the webhook row, find the webhook fields list, select **Manage**, and subscribe to the **messages** field. Verification alone delivers nothing. Meta verifies the URL whether or not you subscribe to anything, so a verified webhook with no subscription looks correct on screen and still sends your agent no traffic. Check this first when the setup looks right and nothing arrives. In the Infobip web interface, go to **Channels and Numbers** -> **Channels** -> **WhatsApp** -> **Senders**. 1. Find the tile for your sender and open its three-dot menu. 2. Select **Edit configuration**. 3. Turn on forwarding to a URL, which Infobip labels as forwarding to HTTP in some accounts, and paste the Artemis **Webhook URL** into the URL field. 4. Select **Save**. Infobip has no verification handshake, so saving the configuration is the whole registration. Infobip documents what arrives on that URL under [inbound messages](https://www.infobip.com/docs/whatsapp/message-types-and-templates/inbound-messages). Nothing confirms the URL works until a real message arrives, which makes the test in the next section the only proof you have. Configuration lives on the sender rather than on the account, so a second sender needs its own forwarding configuration and its own Artemis connection. ### Validation checklist | Validation item | Expected result | | :- | :- | | Choose a provider | The provider selected in Artemis matches the account where the sender lives. | | Complete provider setup | Meta: app created, WhatsApp product added, test recipient verified. Infobip: sender available, base URL and API key collected. | | Collect credentials | Routing identifier and credentials copied from the same provider account. | | Create the Artemis connection | Connection saved as **Active** with the provider's fields and the intended environment. | | Register the callback URL | Meta: the webhook dialog closes after **Verify and save**. Infobip: forwarding is on for that sender. | | Subscribe the events | Meta only: the webhook subscribes to the **messages** field. | | Send a live message | A real WhatsApp conversation produces exactly one agent reply. See [Verify the connection](#verify-the-connection). | ## Verify the connection Artemis doesn't synthesize provider callbacks, so a real WhatsApp message is the only way to confirm the integration works end to end. 1. From a WhatsApp account you registered as a test recipient, send the business sender a short message you can search for later. A distinctive string makes the message easy to find in session records. 2. Check that the agent replies once, from the intended sender and the intended environment. Duplicate replies point to duplicate provider subscriptions, another automation on the same number, or repeated webhook delivery rather than an agent fault. 3. If no reply arrives, open **Sessions** and search for the message you sent. A session that exists tells you the webhook works and the outbound path failed, while no session tells you nothing reached the runtime at all. The two need different fixes, so make this check before you change any configuration. 4. If the agent sends buttons or lists, select a button and a list row and confirm the action returns to the same session. WhatsApp limits how many reply buttons a message carries and how long their labels run, so check that your authored content renders in WhatsApp rather than only in preview. 5. Test only the message types your provider enables, then confirm the agent handles the downloaded attachment rather than only acknowledging it. A webhook that returns 2xx proves only that your provider reached the runtime. It says nothing about whether your outbound credentials work. Always confirm a real two-way conversation before you call the channel done. *** ## Create a permanent access token This section applies to Meta Cloud API only. Infobip API keys don't expire on a schedule, so rotate them on your own policy instead. The token on the API Setup page expires within 24 hours, which is enough to test and not enough to run. Replace it with a system user token, which lasts until you revoke it. Meta explains how the token types differ under [access tokens](https://developers.facebook.com/docs/whatsapp/access-tokens). This work happens in Meta Business Suite rather than the App Dashboard, and it needs a Meta Business admin. 1. Open [Meta Business Suite](https://business.facebook.com/) and select your business portfolio from the dropdown at the top of the page. 2. Select its settings icon to open **Business settings**. 3. Go to **Users** -> **System users** and select **Add**. 4. Name the system user, set its role to **Admin**, and create it. A system user represents your integration rather than a person, which is why its token survives staff changes. 1. Select the system user you created and select **Assign assets**. 2. Assign your app and your WhatsApp Business account, granting full control on each. 3. Reload the page and confirm both show as assigned before you continue, because a token generated before the assets attach can't reach them. 1. Select **Generate token**, choose your app, and set the expiration to never. 2. Select the `whatsapp_business_messaging` permission so the token can send and receive messages, and `whatsapp_business_management` so it can manage templates and account settings. 3. Generate the token and copy it straight into your secret manager, because Meta displays it once and never again. 4. Update the **Access Token** on the Artemis connection from the **Credentials** tab. *** ## Move to production A test sender covers development and nothing else. Who does the production paperwork depends on your provider. On Meta Cloud API, you complete business verification, add a payment method, register a business phone number in WhatsApp Manager, and get your display name approved. You do all of this from [Meta Business Suite](https://business.facebook.com/). On Infobip, Infobip runs the sender registration and verification for you, and you supply the business details it asks for. Either way the work waits on Meta review, so start it well ahead of your launch date. Two WhatsApp rules shape what your agent can say, whichever provider carries the traffic: **The customer service window.** After a customer messages you, you can reply freely for 24 hours. Outside that window, an outbound message needs an approved template, so an agent that reaches out first needs templates approved before it can start a conversation at all. Template approval is its own review, and a rejected template blocks the conversation you wrote it for. **Separate senders per environment.** Use different senders, credentials, webhooks, and connections for development and production. Sharing a sender means test traffic reaches the same conversation history as customer traffic. *** ## Manage the channel connection Opening a WhatsApp connection from **Deployments** -> **Channels** shows four tabs. The header carries the connection name, its environment, its status, and a delete control. **Connection Summary** records what the connection is and when it changed. | Field | Description | | - | - | | **Status** | Whether the connection currently handles messages. | | **Created** and **Updated** | Date and time of creation, and of the last change. | | **Environment** | The environment whose deployment answers messages. | | **Source Type** | Reads `Channel Connection`. | | **Source ID** | Identifies the connection uniquely. Quote it when you raise a support ticket. | **Setup Instructions** below the summary carries the values you need on the provider side, each with a copy control. | Value | Description | | - | - | | **Webhook URL** | The callback URL to register with your provider. It already carries the provider route, so copy it rather than building it by hand. | | Routing identifier | The value this connection matches inbound messages to, shown as the Phone Number ID or the WhatsApp phone number depending on your provider. Check it here first when messages arrive and no session appears. | The platform encrypts credentials after you save them, so every field reads as saved rather than showing its value. Leave a field empty to keep the current value, or enter a new value to replace it. The fields below the first one change with your provider. Meta Cloud API shows the access token, Phone Number ID, App Secret, and Verify Token. Infobip shows the authentication method, API base URL, and either an API key or basic authentication credentials. | Field | Description | | - | - | | **Use Auth Profile** | Switches the connection to a saved auth profile in place of the fields below. Prefer it when the same credentials serve more than one connection, since a rotation then touches one profile. | | Outbound credential | Authenticates outbound messages. Replace it here when you rotate a Meta token or an Infobip API key. | | Inbound verification | Confirms that a callback came from your provider. Changing the Verify Token here breaks the Meta webhook until you change it in Meta as well. | Select **Save Credentials** to apply your changes. Under **General**, update the **Display Name** and the **Provider Verification Strength**. Under **WhatsApp Settings**, the routing identifier appears read-only. You can't change it after creation, so a connection pointed at the wrong sender needs replacing rather than editing. **Authorization message** The connection sends this message when a tool needs the user to authorize before the conversation continues. Edit the template to match your agent's voice, since the default reads as system text rather than as the agent talking. The editor lists the variables you can insert, offers a preview, and expands to show every available variable. The variables cover the number of outstanding authorizations, the profile each one belongs to, and the link the user follows. Select **Save Changes** to apply your changes. **Agent Version** carries an **Environment** dropdown that selects which deployed version handles incoming messages. **Active Deployment** shows the bound version and when someone deployed it. Check this pair when the channel connects but the agent behaves like an older build: the connection follows whatever the environment currently has deployed, not the version you last edited. Select **Save Changes** after you switch the environment. *** ## Key values reference Each value sits on a different provider screen, and two of them look alike enough to swap by accident. Use the table for your provider to collect everything in one pass. | Value | Where to find it | Where it goes | | - | - | - | | **Phone Number ID** | **WhatsApp** -> **API Setup**, directly below the test business phone number | The routing identifier and the outbound credential field in Artemis | | **Access Token** | **WhatsApp** -> **API Setup** for testing. For production, **Business settings** -> **Users** -> **System users** in Meta Business Suite | The **Access Token** field in Artemis | | **App Secret** | **App settings** -> **Basic**, revealed by selecting **Show** | The **App Secret** field in Artemis | | **Verify Token** | You choose it | The **Verify Token** field in Artemis, and the webhook configuration in Meta | | **Webhook URL** | The **Overview** tab of the Artemis connection | The callback URL in **WhatsApp** -> **Configuration** in the Meta app | | Value | Where to find it | Where it goes | | - | - | - | | Sender number | **Channels and Numbers** -> **Channels** -> **WhatsApp** -> **Senders** | The **WhatsApp Phone Number** field in Artemis, as digits only | | **API Base URL** | The homepage of the Infobip web interface, in the form `xxxxx.api.infobip.com` | The **API Base URL** field in Artemis, with the `https://` scheme | | **API Key** | **Developer Tools** -> **API Keys** | The **API Key** field in Artemis | | **Webhook URL** | The **Overview** tab of the Artemis connection | The forwarding URL in the sender's **Edit configuration** screen | *** ## Troubleshoot connection issues | Symptom | Likely cause | Recommended action | | - | - | - | | The agent never replies, and a session exists | The recipient isn't verified on a test sender | On Meta, development mode accepts inbound messages from anyone but delivers only to verified recipients, so add your number under **WhatsApp** -> **API Setup**. On an Infobip trial sender, verify the recipient number in Infobip. | | The agent never replies, and no session exists | Nothing reached the runtime | On Meta, confirm the webhook subscribes to the `messages` field, that the callback URL verified, and that the App Secret matches. On Infobip, confirm forwarding is on for that sender and that the URL matches the connection. | | The runtime reports no connection found | The routing identifier doesn't match the inbound callback | Compare the sender identifier on the inbound callback with the value saved in Artemis, and confirm the provider selector matches the provider that sent it. On Infobip, check for a plus sign or spaces in the saved number. | | Inbound messages arrive but no reply goes out | The outbound credential is wrong or expired | Check the outbound credential, the sender identifier, the account-specific base URL where your provider uses one, the connection status, and whether a runnable deployment exists. A temporary Meta token that expired overnight produces exactly this symptom. | | Inbound callbacks reach the runtime but fail verification | The inbound verification setting doesn't match the provider | On Meta, the App Secret doesn't match the app that sent the callback. On Infobip, your allowlist is missing the address their platform sent from. | | Customers receive duplicate replies | More than one subscription or automation answers the same number | Remove duplicate provider subscriptions, disable any other automation on the number, and account for provider retry behavior. | | Text works but buttons or lists fail | The content exceeds a provider limit, or the authored action is gone | Check the provider's limits, the reply payload IDs, whether your provider supports the interactive type, and how the agent handles the returned action. | | Text works but media fails | The message type isn't enabled, or the file breaks a policy | Confirm the provider enabled that message type, the credentials can download media, size and host policies allow it, and the agent handles attachments. | | Outbound sends fail outside the customer service window | The 24-hour window has closed | Send an approved WhatsApp template instead. | *** ## Provider reference Use these pages when you need detail beyond what this article covers. | Resource | Use it for | | - | - | | [Cloud API get started](https://developers.facebook.com/docs/whatsapp/cloud-api/get-started) | Meta's own walkthrough of app creation, the test sender, and the first test message. | | [Access tokens](https://developers.facebook.com/docs/whatsapp/access-tokens) | How user, system user, and business tokens differ, and which permissions each one needs. | | [My Apps](https://developers.facebook.com/apps/) | Returning to an app you created, or checking which app owns a sender. | | [Meta Business Suite](https://business.facebook.com/) | Business settings, system users, business verification, and WhatsApp Manager. | | Resource | Use it for | | - | - | | [WhatsApp over API](https://www.infobip.com/docs/whatsapp/whatsapp-over-api) | Sender configuration, inbound routing, and what Infobip supports on the channel. | | [Inbound messages](https://www.infobip.com/docs/whatsapp/message-types-and-templates/inbound-messages) | The message types users can send you and how Infobip stores inbound media. | | [Base URL](https://www.infobip.com/docs/developer-resources/about-infobip-apis/base-url) | Why Infobip personalizes the base URL and what a wrong one does. | | [Developer resources](https://www.infobip.com/docs/developer-resources) | Authentication methods, API keys, and the wider API reference. | **Related articles:** * [Channels](/agent-platform/channels) * [Set up Messenger](/agent-platform/channels/messenger) * [Set up LINE](/agent-platform/channels/line) * [Set up Telegram](/agent-platform/channels/telegram) # Set up Zendesk Source: https://koreai.mintlify.app/agent-platform/channels/zendesk [Back to messaging or digital channels](/agent-platform/channels#messaging-or-digital) Connect a Zendesk Sunshine Conversations app to your agent so customers reach it through any messaging channel that app already serves. Sunshine Conversations normalizes Web Widget, social channels, and SMS into one API, so Artemis integrates once and inherits every entry point the app carries. The setup requires: 1. [Zendesk setup](#zendesk-setup): Select or create the Sunshine Conversations app, copy its App ID, and create an API key. 2. [Artemis channel configuration](#artemis-channel-configuration): Enter those values, then copy the generated webhook URL. 3. [Webhook configuration in Zendesk](#webhook-configuration-in-zendesk): Register that URL as a conversations integration and subscribe it to the message and postback triggers. After the configuration is complete, a customer message on any channel connected to the app opens a session in Artemis, the bound deployment answers it, and the reply posts back into the same conversation as a business author. **Important considerations** * **The App ID does two jobs**: It routes inbound events and identifies the app for outbound replies. The connection dialog asks for it twice, once as the routing identifier and once as a credential, and both have to name the same Sunshine Conversations app. * **Two setup paths exist**: Zendesk Suite customers work in Admin Center, while accounts with a separate Sunshine Conversations license use the Sunshine Conversations dashboard. The values are the same either way; the navigation isn't. * **Webhook verification is optional and needs middleware**: Artemis expects an `x-api-key` header carrying an HMAC of the raw body, which Sunshine Conversations doesn't send natively. Leave **Webhook Secret** empty unless something in front of Artemis generates that header. * **Artemis doesn't create the customer entry point**: The app still needs a connected channel or client through which a customer can start a conversation. Registering the webhook adds no channel of its own. ## Prerequisites * A Sunshine Conversations app with at least one customer-facing channel already connected, such as Web Widget or a social channel. * Zendesk Suite Professional or above with Agent Workspace activated, if you're working in Admin Center. The Conversations API page doesn't appear on lower plans. * Administrator access in Zendesk, since only an administrator can create the API key. * Artemis project access with permission to open **Deployments** -> **Channels**, and a deployed agent in the environment you intend to bind. *** ## How Artemis routes Zendesk events Artemis puts the App ID at the end of the webhook path and resolves the connection from that segment, then uses the matching app in the payload for replies. | Piece | Who owns it | What it does | | - | - | - | | **App ID** | Zendesk | Routes inbound events. Artemis appends it to the webhook path, so the generic Zendesk webhook route alone can't resolve a connection. It also identifies the app on outbound calls. | | **API Key ID** | Zendesk | The username half of the HTTP Basic credential Artemis uses to post replies. | | **API Key Secret** | Zendesk | The password half. Zendesk shows it once at creation and never again. | | **Webhook Secret** | You | Optional. When set, Artemis requires an `x-api-key` header carrying the lowercase hexadecimal HMAC-SHA256 of the exact raw request body, and rejects anything without a match. | | **Webhook URL** | Artemis | Receives conversation events. It ends with the App ID, so the full URL matters rather than the route alone. | The App ID travels in a public URL and isn't a secret. It exists for deterministic lookup, so authorization rests on the API key and on whatever verification you put in front of the endpoint. *** ## Zendesk setup Complete the tab that matches your Zendesk account. Both paths produce the same three values: the App ID, the API Key ID, and the API Key Secret. Use this path if your messaging runs on Zendesk Suite. Zendesk's [Conversations API keys article](https://support.zendesk.com/hc/en-us/articles/4576088682266-Using-the-Conversations-API-keys) covers the same screen. 1. Open Admin Center and select **Apps and integrations** in the sidebar. 2. Select **APIs** -> **Conversations API**. If the page doesn't appear, your plan or workspace configuration is the cause rather than a permissions problem. The page needs Zendesk Suite Professional or above with Agent Workspace activated. 1. Click **Create API key**. 2. Enter an identifying name for the key, then click **Next**. 3. Copy the **App ID**, **Key ID**, and **Secret** from the panel Zendesk shows. Zendesk displays the secret once. Copy all three values into your secret manager before you close the panel, because recovering the secret means creating a replacement key. Create a dedicated key for this Artemis connection rather than reusing one that already serves another integration. An account stores up to 10 keys, so a separate key per integration costs nothing and lets you revoke this one in isolation. Check that the app already carries a channel a customer can message, such as the Web Widget or a connected social channel. Artemis attaches to an existing app rather than creating an entry point. Without a connected channel, the webhook registers correctly and no customer can ever reach it. Use this path if your account holds a separate Sunshine Conversations license rather than the one bundled with Zendesk Suite. Sign in to the Sunshine Conversations dashboard and select the app that owns the customer-facing integration, or create one. Keep development and production on separate apps. An app aggregates every channel it serves, so sharing one mixes test conversations into customer history. Open the app's settings and copy the app-level identifier. Sunshine Conversations surfaces several identifiers that look similar. Don't substitute a Zendesk subdomain, an integration ID, a conversation ID, a user ID, a webhook ID, or the API key ID. The App ID identifies the app itself and never changes. 1. Open the **Settings** tab, then find the **API keys** section. 2. Create a key for this Artemis connection. 3. Copy both the **Key ID** and the **Key Secret** while the secret is still visible. These two values authorize outbound messages for every conversation in the app, so treat the secret as a high-value credential and store it only in your secret manager. *** ## Artemis channel configuration Create the connection before you register the webhook. The callback URL ends with the App ID, and Artemis can resolve inbound events only after an active connection exists. 1. Open Artemis and select the project. 2. Go to **Deployments** -> **Channels** -> **Zendesk** -> **+ New**. 3. Configure the following fields: | Artemis connection field | Source / Notes | | - | - | | Display Name | The label operators see in the connections list. Name it for the app and the environment it serves, because a project can hold several Zendesk connections. Routing ignores this value. | | App ID (identifier) | The routing key. Artemis writes it into the webhook path and selects the connection from it. Enter the exact Sunshine Conversations App ID. | | App ID (credential) | The same value again, used for provider operations. A mismatch between the two can accept one app's inbound route and then fail or misdirect the outbound reply. | | API Key ID | The username half of the Basic credential for outbound messages. From the same app as the App ID. | | API Key Secret | The password half. Zendesk shows it once at key creation. | | Webhook Secret | Optional. Set it only when something in front of Artemis can add the matching `x-api-key` HMAC header. Leave it empty otherwise, or every inbound event fails. | | Environment | The published environment whose active deployment answers incoming messages. Leave the working copy default only while you develop. | | Provider Verification Strength | How far Artemis trusts Zendesk's identity assertion when it links a customer to an existing session. **Weak** suits most deployments. Choose **Strong** only when you trust those assertions enough to base continuity and account-linking decisions on them. | | Initial Status | Whether the connection starts handling messages. Set it to **Active**, because Artemis resolves inbound webhooks only for an active connection. | 4. Click **Create**. 5. Open the connection and copy the **Webhook URL** from the **Overview** tab.
The URL ends with the App ID. Copy the whole thing, because the generic Zendesk webhook path can't identify which connection an event belongs to. ### Validation checklist | Validation item | Expected result | | :- | :- | | Select the Sunshine Conversations app | App identified, with at least one customer channel already connected to it. | | Copy the App ID | App-level identifier copied, distinct from subdomain, integration, conversation, user, webhook, and key IDs. | | Create the API key | Key ID and Key Secret copied and stored while the secret was visible. | | Create the Artemis connection | Connection saved as Active, with the same App ID in both the identifier and credential fields. | | Copy the webhook URL | Webhook URL copied from the **Overview** tab, ending with the App ID. | | Register the integration | Conversations integration created in the same app, targeting that URL. | | Subscribe the triggers | `conversation:message` subscribed, plus `conversation:postback` if the agent sends buttons. | | Bind a deployment | The connection's **Deployment** tab shows a runnable active deployment. | | Send a real message | Test conversation reaches the agent and one reply appears. See [Verify the connection](#verify-the-connection). | *** ## Webhook configuration in Zendesk Zendesk calls the webhook a conversations integration in Admin Center, and a webhook in the Sunshine Conversations dashboard. Both create the same object. Zendesk documents the Admin Center route in [Creating conversations integrations in Admin Center](https://support.zendesk.com/hc/en-us/articles/4576083789850-Creating-conversations-integrations-in-Admin-Center). In Admin Center, select **Apps and integrations** in the sidebar, then **Integrations** -> **Conversations integrations**, and click **Create integration**. In the Sunshine Conversations dashboard, add a webhook from the app's integrations area instead. Create it in the same app that issued your API key, because a webhook registered on a neighboring app delivers events that this connection can't claim. Paste the complete **Webhook URL** from the connection's **Overview** tab, including the App ID segment at the end. Zendesk sends an HTTP HEAD request to the root domain when you save, checking that the domain resolves and a service answers. A save that fails at this point signals a reachability issue, not a problem with the path. Select **Conversation message** to receive customer text, which Zendesk sends as `conversation:message`. Add the postback trigger as well if your agent sends buttons. Postback taps arrive as `conversation:postback` rather than as messages, so an agent with buttons and no postback subscription answers text and then goes silent when someone taps. Artemis ignores event types it doesn't support, so extra triggers cause no harm beyond noise. Save the integration, then check the delivery history after your first test to confirm Zendesk receives a 2xx response. Preserve the event identifiers Zendesk sends. Artemis deduplicates on the first event ID, so middleware that rewrites identifiers turns a provider retry into a duplicate agent turn. *** ## Verify the connection Artemis doesn't synthesize Zendesk events, so a real conversation through a connected channel is the only end-to-end proof. 1. From the app's connected customer channel, send a short message you can search for later.
Use the actual customer client rather than an agent-side tool, because the two produce different author types. 2. Confirm one `conversation:message` delivery, one Artemis session, and one agent turn. 3. Check that the reply appears in the same customer conversation.
Artemis posts it as a business author through the Sunshine Conversations API, so the reply shows as coming from the business rather than from an agent. 4. Send an authored response carrying a button, tap it in the customer client, and confirm the postback resumes the same conversation with the action value you authored. 5. Confirm the agent's own reply doesn't trigger a second turn.
Artemis ignores business-authored message events by design. A loop here means middleware is rewriting agent messages as customer or user authors. *** ## Message behavior and limits Sunshine Conversations normalizes many channels into one API, which shapes what your agent can rely on. | Area | Behavior | | - | - | | Channel coverage | The connection inherits every channel the app serves. A reply that renders well in the Web Widget may fall back to plain text on SMS, so author for the least capable channel your app carries. | | Buttons | Zendesk falls back automatically where a channel lacks native buttons, for example rendering a link button as a hyperlink. Test the fallback rather than assuming the authored form. | | Postbacks | Postback buttons stay tappable after use, so the same action can arrive more than once. Make your authored handling idempotent. | | Author types | Artemis processes customer-authored events and ignores business-authored ones, which is what stops the agent answering itself. | | Retries | Zendesk retries failed deliveries. Artemis deduplicates on the first event ID, so leave event identifiers untouched in transit. | | Switchboard control | When a custom integration holds control of a conversation, Agent Workspace doesn't receive it, so messages appear without a ticket. Plan handoff explicitly if human agents need to take over. | *** ## Secure the connection The API key is the credential that matters here, and nothing protects the inbound side unless you add that protection yourself. The Key ID and Key Secret authorize the Sunshine Conversations REST API for every conversation in the app. Use a dedicated key for this connection, keep it in encrypted storage, redact it from diagnostics, and revoke keys you no longer use. Rotating the key means updating both halves in Artemis together, because Artemis sends them as one Basic credential. Webhook verification is optional in this connection contract, and that's a deliberate trade rather than an oversight. With **Webhook Secret** empty, Artemis accepts inbound events without a signature. Setting it requires every request to carry an `x-api-key` header holding the lowercase hexadecimal HMAC-SHA256 of the untouched raw body, which Sunshine Conversations doesn't produce on its own. Put middleware in front of Artemis that generates the header exactly that way, or enforce an equivalent control at your ingress. Don't paste the API Key Secret into **Webhook Secret**. Each secret protects a different direction of traffic, and reusing one value for both spreads a single compromise across inbound and outbound paths. Use distinct Sunshine Conversations apps or API keys, webhooks, secrets, and connections for development and production. *** ## Manage the channel connection Opening a connection shows four tabs. The delete control sits beside the connection name. **Connection Summary** reports the connection's state and identity. | Field | Description | | - | - | | **Status** | Whether the connection currently handles messages. | | **Created** and **Updated** | Date and time of creation, and of the last change. | | **Environment** | The environment whose deployment answers messages. | | **Source Type** | Reads `Channel Connection`. | | **Source ID** | Identifies the connection uniquely. Quote it when you raise a support ticket. | **Setup Instructions** shows the **Webhook URL** to register in Zendesk and the **App ID** it ends with, each with a copy control. Compare the App ID here with the one in the payload whenever Artemis reports no connection found. Artemis encrypts credentials after you save them, so each field reads as saved rather than showing its value. Leave a field empty to keep the current value, or enter a new one to replace it. | Field | Description | | - | - | | **Use Auth Profile** | Draws the credentials from a saved auth profile instead of the fields below. Prefer it when the same credentials serve more than one connection, since a rotation then touches one profile. | | **App ID** | The credential copy of the App ID. Keep it matched to the identifier, which you can read on the **Overview** tab. | | **API Key ID** | Replace it when you issue a new key in Zendesk. | | **API Key Secret** | Replace it alongside the Key ID, because Artemis sends the pair as one Basic credential. | | **Webhook Secret** | Set or clear the optional HMAC requirement. Clearing it returns the endpoint to accepting unsigned events. | Click **Save Credentials** to apply your changes. Under **General**, rename the connection with **Display Name**, and revisit **Provider Verification Strength** if your trust posture for this app changes. Under **Zendesk Settings**, **App ID** appears read-only. The webhook URL is built from it, so changing it would invalidate the integration registered in Zendesk. Create a new connection to serve a different app. **Authorization Message** controls what the customer sees when a tool needs them to authorize something before the conversation continues. Write the template with these variables. | Scope | Variables | Use | | - | - | - | | Message | `{{total}}`, `{{completed}}`, `{{remaining}}` | Summarize progress across every pending authorization, typically in a lead-in line above the list. | | Repeat block | `{{#authorizations}}` and `{{/authorizations}}` | Wrap the section Artemis repeats once per pending authorization. | | Item | `{{profileName}}`, `{{authLink}}`, `{{authUrl}}`, `{{index}}`, `{{count}}` | Identify and link each authorization inside the repeat block. | Item variables resolve only inside the repeat block. Check the preview, and remember the message may render on a channel that strips formatting. Click **Save Changes** to apply your changes. Choose which deployed version of the agent answers messages on this connection. Under **Agent Version**, set **Environment** to the environment you want the connection to follow. **Active Deployment** then shows the version currently serving traffic. A connection left on the working copy shows a warning, because the working copy changes as you edit the agent. Bind the connection to a published environment before customers reach it. Click **Save Changes** after you switch. *** ## Troubleshoot connection issues | Symptom | Likely cause | Recommended action | | - | - | - | | The webhook returns connection not found | The URL doesn't carry the right App ID, or the connection isn't active | Confirm the URL ends with the exact App ID saved in Artemis, confirm the connection is Active, and confirm the payload's app identifier names the same app. | | Outbound replies return 401 or 403 | The API key doesn't belong to the credential App ID, or someone revoked it | Verify the Key ID and Key Secret come from the same app as the credential App ID, and confirm nobody regenerated or revoked the key. | | Customer messages never arrive | The integration sits on another app, the trigger isn't subscribed, or the endpoint isn't reachable | Confirm the integration lives in the same app as the credentials, subscribe `conversation:message`, confirm the endpoint answers over public HTTPS, and check Zendesk's delivery history for a 2xx. | | Inbound processing works but no reply appears | The payload lacks a conversation reference, the credentials mismatch, or no deployment can run | Confirm the payload carries the conversation identifier, the app credentials match, the bound environment has a runnable deployment, and the Zendesk API is reachable. | | The webhook returns 401 after you set Webhook Secret | Nothing is generating the expected header | Have your middleware set `x-api-key` to the lowercase hexadecimal HMAC-SHA256 of the untouched raw body, or clear the field for an intentionally unsigned deployment. | | Replies create repeated turns | Something rewrites the author type | Inspect the event author type. Artemis ignores business authors, so a loop means middleware is presenting agent messages as customer or user authors. | | Text works but buttons don't | The postback trigger isn't subscribed, or the channel can't render the action | Subscribe `conversation:postback`, confirm the customer channel supports reply actions, and confirm the authored action payload returns unchanged. | | Messages appear in Zendesk but agents receive no ticket | Switchboard control sits with a custom integration rather than Agent Workspace | Check which integration holds control of the conversation, and pass control to Agent Workspace when a human agent needs to take over. | *** ## Zendesk reference Use these Zendesk pages when you need detail beyond what this article covers. | Resource | Use it for | | - | - | | [Using the Conversations API keys](https://support.zendesk.com/hc/en-us/articles/4576088682266-Using-the-Conversations-API-keys) | Creating and managing API keys in Admin Center, and the plan requirements. | | [Creating conversations integrations in Admin Center](https://support.zendesk.com/hc/en-us/articles/4576083789850-Creating-conversations-integrations-in-Admin-Center) | Registering the webhook, viewing its IDs, and editing it later. | | [API quickstart](https://developer.zendesk.com/documentation/conversations/getting-started/api-quickstart/) | Zendesk's own walkthrough of the webhook and the first reply. | | [Receiving messages](https://developer.zendesk.com/documentation/conversations/messaging-platform/programmable-conversations/receiving-messages/) | Webhook payload structure, trigger list, and switchboard control. | | [Structured messages](https://developer.zendesk.com/documentation/conversations/messaging-platform/programmable-conversations/structured-messages/) | Button types, postback behavior, and channel fallbacks. | | [Creating and managing apps](https://docs.smooch.io/guide/creating-and-managing-apps/) | App-level concepts for accounts on a standalone Sunshine Conversations license. | **Related articles:** * [Set up WhatsApp](/agent-platform/channels/whatsapp) * [Set up Messenger](/agent-platform/channels/messenger) * [Set up Twilio](/agent-platform/channels/twilio) # Install the Artemis CLI Source: https://koreai.mintlify.app/agent-platform/cli The Artemis CLI lets you build and manage projects from your terminal or CI pipeline. CLI lets developers automate the complete agentic lifecycle, including authentication, project sync, eval runs, and deployment, from external systems, without Studio. To create, review, customize, test, and deploy agents with the CLI, see [Use the Artemis CLI](/agent-platform/use-cli). ## Prerequisites * Node.js 22 or later. * An **Agent Platform account** with access to the workspace and project you want to manage. * The **Platform URL** provided by your platform administrator. For hosted solution, see the list of [region-specific platform URLs](/agent-platform/getting-started#region-specific-urls-and-allowlist-of-ips). * Additional permissions or credentials when using model execution, connector OAuth, or Git synchronization. ## Install the CLI Install the CLI globally using npm: ```bash theme={null} npm install --global @koreai/artemis-platform-cli artemis-platform-cli --version artemis-platform-cli --help ``` To use the CLI as a project dependency, install it as a development dependency: ```bash theme={null} npm install --save-dev @koreai/artemis-platform-cli npx artemis-platform-cli --version ``` To upgrade or remove a global installation: ```bash theme={null} npm update --global @koreai/artemis-platform-cli npm uninstall --global @koreai/artemis-platform-cli ``` ### Install a release candidate (RC) build Versions containing `-rc.` are release candidates, published separately from the stable `@koreai/artemis-platform-cli` package. Release candidates build are for testing only and you must use those in a non-production project. To try a release candidate (RC) build before its stable release, explicitly install the RC package using the command `npm install --global @koredev/artemis-platform-cli@-rc.`. After installation, verify the version using the command `artemis-platform-cli --version`. ## Connect to Agent Platform Before using the CLI, configure the URLs for the Platform services. In most deployments, `apiUrl`, `runtimeApiUrl`, and `searchAiApiUrl` use the same base domain. For example: ```bash theme={null} artemis-platform-cli config set apiUrl https://platform.example.com artemis-platform-cli config set runtimeApiUrl https://runtime.example.com artemis-platform-cli config set searchAiApiUrl https://search.example.com artemis-platform-cli config show ``` For a private, regional, or a separately-routed deployment, use the base URLs provided by your platform administrator. For hosted solution, it's a [region-specific platform URL](/agent-platform/getting-started#region-specific-urls-and-allowlist-of-ips). Don't append paths such as `/api`, `/runtime`, or `/search` unless your administrator explicitly provides URLs that require them. You can also configure the service URLs using environment variables: ```bash theme={null} export KORE_API_URL=https://agents.kore.ai export KORE_RUNTIME_API_URL=https://agents.kore.ai export KORE_SEARCHAI_API_URL=https://agents.kore.ai ``` Environment variables override stored URL settings for the current process. ## Authenticate Sign in to Platform using the device authorization flow: ```bash theme={null} artemis-platform-cli login artemis-platform-cli whoami ``` The `login` command opens the device authorization flow in your browser. Complete the browser step using an account that has access to the workspace and project you want to manage. For CI pipelines, provide an access token instead of signing in interactively. See [Use the CLI in CI](/agent-platform/use-cli#use-the-cli-in-ci). ## Select a workspace and project List and select the workspace you want to work in: ```bash theme={null} artemis-platform-cli workspaces list artemis-platform-cli workspaces select ``` List and select a project: ```bash theme={null} artemis-platform-cli projects list artemis-platform-cli projects select artemis-platform-cli projects current ``` Interactive commands use the selected project. For CI workflows, use an explicit `--project ` whenever the command supports it. Once the CLI is installed, connected, and authenticated, continue to [Use the Artemis CLI](/agent-platform/use-cli) to create, review, customize, test, and deploy agents. # Building blocks Source: https://koreai.mintlify.app/agent-platform/concepts Agent Platform is built on four interconnected pieces: **Agents**, **Supervisors**, **Tools**, and **Sessions**. Understanding how they relate gives you a foundation for everything else on the platform. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph TB subgraph Project["Project"] SUP["Supervisor"] A1["Agent A"] A2["Agent B
(with steps)"] A3["Agent C"] T1["Tool: CRM API"] T2["Tool: Search"] T3["Tool: Payment"] end User((User)) -->|message| SUP SUP -->|handoff| A1 SUP -->|handoff| A2 SUP -->|handoff| A3 A1 --> T1 A2 --> T2 A3 --> T3 A1 -->|response| User A2 -->|response| User A3 -->|response| User ``` | Building Block | Analogy | Responsibility | | - | - | - | | Supervisor | Reception desk | Routes conversations to the right team. | | Agent | Domain specialist | Handles a specific task with deep knowledge. | | Tool | Phone, computer, database | Gives agents real-world capabilities. | | Session | Conversation thread | Tracks state across messages. | | Flow | Step-by-step checklist | Guides agents through a structured process. | Here is how a message flows through the system: 1. The user sends a message to your project endpoint. 2. The supervisor evaluates the message against its handoff rules. 3. The right agent activates and takes over the conversation. 4. The agent processes the message using its goal, persona, tools, and constraints. 5. Tools execute API calls, database queries, or other actions. 6. The agent responds back to the user through the session. 7. The session persists all state for the next message. ## Agents An agent is a self-contained unit that handles a specific domain. It has a goal, a persona, tools it can use, and rules it must follow. Think of an agent like a specialist on a support team — one handles billing, another handles shipping, and each brings their own expertise. Every agent is defined in ABL™ (Agent Blueprint Language), a schema-driven language purpose-built for multi-agent orchestration. You declare *what* the agent should do; the platform figures out *how* to execute it. ABL definitions compile into immutable intermediate representations (IRs) — every version is auditable, every deployment is reproducible. ```yaml expandable=true theme={null} AGENT: Billing_Support GOAL: "Help customers resolve billing inquiries" PERSONA: | Friendly billing specialist who explains charges clearly. Always shows itemized breakdowns before totals. TOOLS: get_invoice(customer_id: string) -> {invoice: object} process_refund(invoice_id: string, amount: number) -> {success: boolean} GATHER: customer_id: prompt: "What is your customer ID?" type: string required: true ``` ### ABL design philosophy ABL favors **declaration over implementation**. Describing the agent's goal, persona, tools, and constraints — rather than writing execution logic — gives you several advantages: * **Portability**: The same agent definition runs on different runtimes (voice, digital, workflow). * **Testability**: Evaluate agent behavior without deploying infrastructure. * **Composability**: Agents, supervisors, and tools combine like building blocks. * **Evolvability**: Change one piece without rewriting the system. ## Supervisors A supervisor routes conversations to the right specialist agent. It evaluates user intent against a set of handoff rules and transfers control to the matching agent. Supervisors do not handle domain tasks directly. ```yaml theme={null} SUPERVISOR: Support_Hub GOAL: "Route customers to the right specialist" HANDOFF: - TO: Billing_Support WHEN: user asks about invoices, charges, or refunds - TO: Shipping_Agent WHEN: user asks about delivery, tracking, or shipments - TO: Live_Agent WHEN: user requests human assistance ``` The supervisor uses an LLM to evaluate `WHEN` conditions — routing is intelligent. "Where's my package?" routes to the shipping agent even though the user never said "delivery" or "shipment." This separation matters. The supervisor holds the map of your organization's capabilities; each agent holds deep domain knowledge. Add a new specialist by adding a new `HANDOFF` rule — no changes to existing agents. A supervisor can route to other supervisors, enabling hierarchical orchestration for complex organizations. **When to use a supervisor:** * Your system has multiple specialist agents. * Different user intents should route to different capabilities. * You need centralized routing logic with priority-based fallbacks. * You want to add or remove agents without modifying existing agents. ## Tools Tools give agents the ability to do things in the real world — call APIs, query databases, process files. Without tools, an agent can only converse. With tools, it can take action. ```yaml theme={null} TOOLS: search_flights(origin: string, destination: string, date: date) -> {flights: array} description: "Search available flights by route and date" create_booking(flight_id: string, passenger: object) -> {booking_id: string} description: "Book a flight for a passenger" ``` ABL declares tool *signatures* — the name, inputs, and outputs. The actual implementation lives in your backend services. The platform connects the two at deployment time through **tool bindings**. The same agent definition can connect to different backends in staging vs. production. ### Tool execution When a tool executes, the platform: 1. **Resolves the tool binding** from the deployment configuration. 2. **Validates inputs** against declared parameter types before the call. 3. **Makes the external call** with the appropriate authentication. 4. **Processes the result** and makes it available to the agent — in the conversation context for reasoning agents, or as session variables for agents with steps. 5. **Handles errors** using `ON_ERROR` handlers for retry logic, fallback responses, or escalation triggers. Tool execution includes timeout enforcement, error categorization, and automatic retry for transient failures. ## Sessions A session represents a single conversation between a user and your agent system. When a user connects — through a web widget, SDK integration, voice channel, or API call — the platform creates a session and binds it to your project's deployed agent configuration. Sessions persist across messages. When the user returns after a pause, the session remembers where they left off. If the supervisor hands off to a specialist, the session maintains the full history so the specialist has context. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4', 'actorBkg': '#EAF8E4', 'actorBorder': '#5CC83A', 'actorTextColor': '#1a1a1a', 'actorLineColor': '#5CC83A', 'signalColor': '#5CC83A', 'signalTextColor': '#2F9E1E', 'labelBoxBkgColor': '#EAF8E4', 'labelBoxBorderColor': '#5CC83A', 'labelTextColor': '#1a1a1a', 'loopTextColor': '#2F9E1E', 'noteBkgColor': '#EAF8E4', 'noteBorderColor': '#5CC83A', 'noteTextColor': '#1a1a1a', 'activationBorderColor': '#5CC83A', 'activationBkgColor': '#EAF8E4', 'sequenceNumberColor': '#2F9E1E'}}}%% sequenceDiagram participant User participant Supervisor participant Agent participant Tool User->>Supervisor: "I need to rebook my flight" Supervisor->>Agent: handoff to Booking_Agent Agent->>User: "What is your booking reference?" User->>Agent: "ABC123" Agent->>Tool: get_booking("ABC123") Tool-->>Agent: {booking details} Agent->>User: "Here are your rebooking options..." ``` ### Session state Sessions manage three categories of state: | State type | What it holds | | - | - | | **Session variables** | Data collected during the conversation: GATHER field values, tool results, and SET values. Reference any variable as `{{variable_name}}` in any subsequent step, tool call, or response template. | | **Conversation history** | The ordered sequence of messages between user and agent, including tool call results. Long conversations are automatically compacted to preserve essential information within the LLM's context window. | | **Flow state** | The current flow step and which fields have been gathered (for agents with steps). Supports backtracking when the user wants to change a previous answer. | ### Persistent memory Agents can store and retrieve information that persists across sessions using `MEMORY` declarations: ```yaml expandable=true theme={null} MEMORY: session: - selected_booking - action_type persistent: - user.booking_history - user.preferences remember: - WHEN action_completed == true STORE: {booking_id: selected_booking, action: action_type} -> user.booking_history recall: - ON: session:start ACTION: inject_context PATHS: [user.booking_history, user.preferences] ``` * **Session memory**: tracks values within the current conversation. * **Persistent memory**: stores facts scoped to the individual user, surviving across sessions. * **REMEMBER rules**: define when to write to persistent memory. * **RECALL rules**: define when to load persistent memory into context. A returning customer's preferences, past interactions, and relevant history are available to the agent without the user repeating themselves. ### Session hierarchy in multi-agent systems When a supervisor hands off to a specialist, the platform creates a **thread** within the existing session — not a new session. The user has one continuous conversation regardless of how many agents participate. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph TB subgraph Session["Session (single ID)"] T0["Thread 0: Supervisor"] T1["Thread 1: Billing_Agent"] T2["Thread 2: Refund_Processor"] end T0 -->|handoff| T1 T1 -->|delegate| T2 T2 -->|return| T1 T1 -->|return| T0 ``` Threads form a stack: handoffs push new threads, completions pop back to the parent. Each thread maintains its own conversation history and gathered data but can access data from parent threads. **Handoff** transfers the conversation — the user interacts directly with the target agent. When the target completes, it returns data to the parent. ```yaml theme={null} HANDOFF: - TO: Billing_Support WHEN: user asks about billing PASS: customer_id RETURN: true ``` **Delegate** runs a sub-task in the background — the parent agent seamlessly incorporates the result into its own response. ```yaml theme={null} DELEGATE: - AGENT: Fee_Calculator WHEN: action_type == "modify" PURPOSE: "Calculate total fees for the requested changes" INPUT: booking_id: selected_booking change_type: action_type RETURNS: total_fee: quoted_fee ``` ### Session lifecycle states | State | Description | | - | - | | Active | The session is processing messages normally. | | Waiting | The session is waiting for user input. | | Suspended | The session is paused, waiting for an async callback. | | Completed | The agent has fulfilled its goal; the session is done. | | Escalated | The session has been transferred to a human operator. | | Expired | The session timed out due to inactivity. | Sessions are scoped to a single user within a single project. Data from one session never leaks to another. Session data is cleaned up automatically according to your tenant's retention policy. ### Concurrency The platform handles concurrent messages within a session using configurable strategies: * **Serial** (default): Messages are queued and processed one at a time, in order. Use this for most conversational agents to ensure full context from the previous message. * **Preemptive**: A new message cancels in-progress execution and starts fresh. Use this for real-time interfaces where the user might correct themselves mid-response. * **Parallel**: Multiple messages process simultaneously. Use this for batch operations. ## Execution modes Every agent in ABL reasons by default using an LLM. You can modify this behavior by adding a `FLOW` section (structured steps) or using the `SUPERVISOR` declaration (routing-only). The platform derives the execution mode from your definition — there is no explicit `MODE` keyword. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph LR subgraph Modes["Execution modes"] R["Agent
LLM-driven by default"] S["Agent with FLOW
Structured steps"] SUP["Supervisor
Routes to specialists"] end R -.- |"add FLOW section"| S SUP -->|handoff| R SUP -->|handoff| S ``` How the platform determines execution mode: * **No `FLOW` section**: The agent reasons using the LLM agentic loop. * **Has `FLOW` section**: The agent follows its defined steps sequentially. * **Has `SUPERVISOR` declaration**: The agent runs as a router with handoff evaluation. * **`REASONING: true` on a flow step**: That individual step uses the LLM reasoning loop within an otherwise step-based flow. * **`REASONING: false` on a flow step**: That step runs deterministically without LLM involvement. ### Agents (reasoning mode) An agent in reasoning mode runs an **agentic loop** — iterative LLM calls and tool executions until it has enough information to respond. It can handle ambiguous requests, ask clarifying questions, and chain multiple tool calls without explicit instructions for every scenario. In addition to the tools you define, agents in reasoning mode have access to system tools: | System tool | What it does | | - | - | | `handoff` | Transfers the conversation to another agent. | | `delegate` | Runs a sub-task with another agent and incorporates the result. | | `complete` | Marks the task as done. | | `escalate` | Transfers to a human operator with context. | | `fan_out` | Executes multiple sub-tasks in parallel. | **When to use reasoning mode:** * The task requires interpreting ambiguous or complex natural language. * Multiple valid conversation paths exist for the same goal. * The agent needs to make judgment calls about which tools to use and when. * You want the agent to handle edge cases without explicit rules for each one. ### Agents with steps (flow mode) Add a `FLOW` section when you need a defined sequence — data collection, tool calls, and responses in a specific order. Each step has a specific action (`GATHER`, `CALL`, `RESPOND`) and an explicit transition (`THEN`) to the next step. ```yaml theme={null} AGENT: Hotel_Booking GOAL: "Guide users through a complete hotel booking process" FLOW: steps: - get_destination - get_dates - search_hotels - select_hotel - collect_guest_info - confirm_booking get_destination: REASONING: false GATHER: - destination: required THEN: get_dates get_dates: REASONING: false GATHER: - checkin_date: required type: date - checkout_date: required type: date THEN: search_hotels search_hotels: REASONING: false CALL: search_hotels(destination, checkin_date, checkout_date) THEN: select_hotel select_hotel: REASONING: false GATHER: - hotel_selection: required THEN: collect_guest_info collect_guest_info: REASONING: false GATHER: - guest_name: required - guest_email: required type: email THEN: confirm_booking confirm_booking: REASONING: false CALL: create_booking(selected_hotel_id, guest_name, guest_email) RESPOND: "Booking confirmed! Confirmation: {{booking_id}}" THEN: COMPLETE ``` **When to use flow mode:** * The process has a defined sequence that must be followed. * Regulatory or compliance requirements dictate the conversation structure. * You need deterministic, auditable behavior. * The task involves structured data collection with validation rules. ### Per-step reasoning control Within a `FLOW`, each step has a `REASONING` toggle that controls whether that step uses LLM reasoning. This lets you mix deterministic and LLM-driven behavior within a single agent: ```yaml theme={null} AGENT: Insurance_Claim GOAL: "Process insurance claims with data collection and intelligent assessment" FLOW: steps: - collect_policy_info - collect_incident_details - assess_claim - present_decision collect_policy_info: REASONING: false GATHER: - policy_number: required - incident_date: required type: date THEN: collect_incident_details collect_incident_details: REASONING: false GATHER: - description: required - damage_estimate: required type: number THEN: assess_claim assess_claim: REASONING: true INSTRUCTIONS: | Review the claim details and assess coverage eligibility. Check policy terms, evaluate the incident description, and determine the recommended payout amount. THEN: present_decision present_decision: REASONING: false RESPOND: "Based on my assessment: {{assessment_result}}" THEN: COMPLETE ``` The first two steps use deterministic data collection. The `assess_claim` step switches to `REASONING: true`, giving the LLM full autonomy to evaluate the claim using all available context. The final step returns to deterministic mode to present the result. Start with steps for predictable processes and add `REASONING: true` to individual steps that need flexibility. This keeps token costs low while leveraging LLM intelligence where it matters. ### Choosing the right mode | Factor | Agent (Reasoning) | Agent with Steps | Supervisor | | - | - | - | - | | **Conversation path** | Unpredictable | Defined sequence | N/A (routing) | | **Decision complexity** | High (judgment needed) | Low (rules suffice) | Medium (intent) | | **Compliance requirements** | Flexible | Strict, auditable | N/A | | **Data collection** | Organic, conversational | Structured, sequential | None | | **Tool usage** | Agent decides when | Explicit in each step | None | | **Predictability** | Lower | Higher | Medium | | **Token cost** | Higher (LLM per turn) | Lower (LLM optional) | Low | | **Best for** | Support, advisory | Forms, workflows | Multi-agent | ### Combining agents in a system Real-world systems combine agents with different execution modes: ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph TB SUP["Supervisor
(intelligent routing)"] R1["Product Advisor"] S1["Order Process
(with steps)"] R2["Support Agent"] S2["Return Flow
(with steps)"] SUP -->|"product questions"| R1 SUP -->|"place order"| S1 SUP -->|"general help"| R2 SUP -->|"return request"| S2 R2 -->|"return detected"| S2 ``` The supervisor handles routing. Agents without steps handle open-ended conversations (product recommendations, general support). Agents with steps handle structured processes (checkout, returns). Any agent can hand off to another when it detects a task that requires a different approach. ## Agent lifecycle Every agent follows a lifecycle from initial definition to production monitoring. The lifecycle is a loop — insights from monitoring drive the next round of changes. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph LR D["Define"] --> B["Build"] B --> T["Test"] T --> Dep["Deploy"] Dep --> M["Monitor"] M --> I["Iterate"] I --> D style D fill:#e8f4e8 style B fill:#e8f0f8 style T fill:#f8f0e8 style Dep fill:#e8e8f8 style M fill:#f8e8e8 style I fill:#f0e8f8 ``` | Lifecycle Stage | What Happens | Studio Feature | | - | - | - | | **Define** | Write the agent in ABL: declare goal, persona, tools, constraints, and completion criteria. | Visual editor, ABL code editor, flow canvas | | **Build** | The ABL compiler validates syntax, checks cross-agent references, and produces the IR. | Real-time compilation, error highlighting, IR preview | | **Test** | Validate behavior with conversation testing, evaluation sets, and tool mocking. | Built-in chat, tool mocking, evaluation sets | | **Deploy** | Pin specific agent versions so in-progress sessions are not disrupted by future changes. | Deployment management, version pinning, environment tags | | **Monitor** | Review session traces, aggregate metrics, and escalation patterns. | Session traces, execution metrics, escalation dashboard | | **Iterate** | Use monitoring insights to refine ABL definitions and redeploy. | Version history, diff view, quick redeploy | ### Define At this stage you decide the agent's scope, data requirements, tool dependencies, business rules, and completion criteria. Studio provides a visual editor for agents with and without steps, and a canvas-based flow builder for agents with steps. ```yaml theme={null} AGENT: Refund_Processor GOAL: "Process customer refund requests with policy validation" PERSONA: | Empathetic customer service specialist. Always explains refund timelines before asking for confirmation. TOOLS: lookup_order(order_id: string) -> {order: object, eligible: boolean} process_refund(order_id: string, reason: string) -> {refund_id: string, amount: number} GATHER: order_id: prompt: "What is your order number?" type: string required: true reason: prompt: "Could you share the reason for the refund?" type: string required: true CONSTRAINTS: pre_refund: - REQUIRE lookup_order.eligible == true ON_FAIL: "This order is not eligible for a refund. {{lookup_order.reason}}" COMPLETE: - WHEN: refund_processed == true RESPOND: "Refund {{refund_id}} processed for {{amount}}. Allow 5-7 business days." ``` ### Build The ABL compiler transforms your `.abl` files into an executable IR: ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph LR ABL[".abl files"] --> Parser["Parser"] Parser --> AST["AST"] AST --> Compiler["Compiler"] Compiler --> IR["Agent IR"] IR --> Runtime["Runtime"] ``` The compiler catches errors early: syntax errors, type mismatches between tool parameters and GATHER field types, invalid cross-agent handoff references, and conflicting constraints. The IR is framework-agnostic — the same definition runs on digital, voice, and workflow runtimes. Studio compiles automatically as you edit, flagging errors in real time. ### Test Testing happens at multiple levels: * **Syntax validation**: The compiler checks required sections, valid references, and type consistency. * **Conversation testing**: Interact with your agent in Studio's built-in chat. Observe responses, tool calls, and state transitions. * **Evaluation sets**: Define personas (simulated users), scenarios (conversation scripts), and evaluators (quality criteria). The platform runs automated conversations and scores agent performance. * **Tool mocking**: Configure mock responses for tools to verify the agent's decision-making logic without calling real APIs. ### Deploy A deployment pins specific versions of your agents, tools, and configuration: ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph LR subgraph Studio["Studio"] WC["Working Copy
(Latest edits)"] end subgraph Deployments["Deployments"] V1["v1.0
(Staging)"] V2["v2.0
(Production)"] end WC -->|deploy| V1 V1 -->|promote| V2 ``` * **Version pinning**: In-progress sessions continue with the same agent behavior; new sessions pick up the latest deployment. * **Environment separation**: Deploy to staging for final testing before promoting to production. * **Rollback**: Revert to a previous deployment. The platform keeps full deployment history. * **Zero-downtime**: Deployments do not interrupt active sessions. ### Monitor Once deployed, monitoring surfaces how agents perform in production: * **Session traces**: The full execution path for every conversation — which agent handled each message, what tools were called, what data was gathered, and how long each step took. * **Metrics**: Aggregate performance data — completion rates, average session duration, tool error rates, and LLM token usage. * **Escalation tracking**: When and why agents hand off to human operators. High escalation rates signal that an agent's capabilities or constraints need refinement. ### Iterate ABL's declarative nature makes iteration fast. Changing agent behavior is a matter of editing a few lines of ABL, not refactoring application code. Common iteration patterns: * Agent mishandles a common edge case → add a constraint or refine instructions. * Users drop off at a specific flow step → simplify GATHER prompts or improve error messages. * A tool fails frequently → add retry logic in `ON_ERROR` or improve error responses. * Escalation rates are high for a topic → create a new specialist agent and add a handoff rule. Every change creates a new version in the deployment system, giving you a full audit trail of how your agents evolved over time. ## Constraints and guardrails Constraints enforce business rules throughout agent execution. They are evaluated at specific checkpoints — before tool calls, after data collection, and at task completion. ```yaml theme={null} CONSTRAINTS: pre_refund: - REQUIRE order.eligible == true ON_FAIL: "This order is not eligible for a refund." - REQUIRE refund_amount <= 1000 ON_FAIL: ESCALATE "Refund exceeds automatic approval limit" ``` When a constraint fails, the configured `ON_FAIL` action executes: a message to the user, a handoff to another agent, or an escalation to a human operator. **Guardrails** provide additional safety checks on agent output — content moderation, PII detection, topic boundaries, and format validation. These run after the LLM generates a response but before it reaches the user. ## Tracing Every execution emits trace events that capture the full decision path. Traces let you answer questions like: why did the agent route this way, which tools were called, and how long did each step take? | Event | What It Captures | | - | - | | `execution.queued` | Message received, position in queue. | | `execution.started` | Processing begins, agent identified. | | `handoff_match` | Supervisor routing decision and target. | | `tool_call` | Tool name, parameters, timing. | | `tool_result` | Tool response, success/failure, duration. | | `gather_extraction` | Fields extracted from user message. | | `constraint_check` | Constraint evaluation, pass/fail. | | `flow_transition` | Flow step change with reason. | | `thread_return` | Child agent returning to parent. | | `execution.completed` | Final response, total duration, token usage. | Traces are available in Studio's session detail view. For programmatic access, traces are available through the platform API. Set trace verbosity to `verbose` during development to see detailed decision reasoning — why the LLM chose a specific tool, why a constraint passed or failed, and how entity extraction matched values to fields. *** # Axero connector Source: https://koreai.mintlify.app/agent-platform/connectors/axero Axero is a digital workplace and intranet platform for managing knowledge, communication, and collaboration. The Axero connector ingests content from Axero spaces into Search AI. | Specification | Details | | - | - | | Repository type | Cloud | | Supported content | Articles, CMS Pages, Forums, Announcements, Blogs, Wikis, Files | | Permission-aware ACL | Yes | | Content filtering | Yes | ## Prerequisites Before configuring the connector: * You must have an Axero account with permission to access the content you want to ingest. * Obtain either: * an **API Key**, or * a **Bearer Token** * Note your Axero host URL (for example, `https://company.axerosolutions.com/axero`) ## Step 1: Generate an API Key in Axero 1. Log in to your Axero account. 2. Go to **My Account** and select **Preferences**. 3. In the left navigation, click **My REST API Key**. 4. Click **Create** to generate a new API key. 5. Copy the generated key. [Learn More.](https://my.axerosolutions.com/spaces/5/axero-documentation/wiki/view/370/rest-api-overview) ## Step 2: Configure the Axero Connector 1. Navigate to **Knowledge Bases** > **Axero**. 2. Select or create an authentication profile. 1. Click **New Auth Profile**. 2. Select one of the following authentication types: 1. API Key 2. Bearer Token 3. Add a unique name for the profile, description of the profile and provide the key. 4. After saving the profile, return to the connector configuration. 3. Configure the connection: 1. **Axero Auth profile** - Select the auth profile to use for authentication. 2. **Axero Host URL** - Base URL of your Axero instance. 4. Once the connection is established, the available Axero spaces are loaded automatically. ## Step 3: Select spaces Choose the content you want to ingest. You can choose one of the following options: * **Selected Spaces** - Ingest content only from the selected Axero spaces. Select one or more spaces from the list. * **All Spaces** - Ingest content from all accessible spaces in the tenant. ## Step 4: Configure content Choose which content types to ingest and configure ingestion options. Supported content types include the following: * Articles * CMS Pages * Forums * Announcements * Blogs * Wikis * Files **Content options** | **Setting** | **Description** | | - | - | | Fold comments into parent content | Includes comments within the parent article, blog, wiki, or forum document during indexing. | | Enforce Axero space permissions | Resolves active space users and enforces Axero access permissions during ingestion. Content without valid permission evidence is skipped. | | File policy | Specifies which file formats are processed during ingestion. | | Max file size (MiB) | Maximum file size allowed for ingestion. Files larger than this limit are skipped. | | Modified on or after | Ingest only content that has been modified on or after the specified date. Useful for incremental ingestion. | ## Step 4: Preview and activate Before activating the connector, validate the configuration. | **Action** | **Description** | | - | - | | Generate proposal | Generates a package proposal based on the selected configuration. | | Run dry run | Validates the configuration without ingesting content. | | Approve & Start | Activates the connector and starts content ingestion. | ## Access control When **Enforce Axero space permissions** is enabled, the connector retrieves user permissions from Axero and applies them during indexing. Only users with access to a space can retrieve its content through the knowledge base. # Box connector Source: https://koreai.mintlify.app/agent-platform/connectors/box [Back to the list of Connectors](/agent-platform/knowledge#connectors) Use the Box connector to index documents stored in your Box enterprise and make them searchable through Artemis. The connector authenticates via OAuth 2.0, allowing the platform to crawl files accessible to the authorized Box account while respecting Box access controls. | **Specification** | **Details** | | - | - | | Repository type | Cloud | | Supported content | Documents and files (PDF, Office documents, text files, HTML, XML, JSON, CSV, Markdown) | | Content filtering | Yes | | Permission-aware ACL | Yes (uses Box file and folder permissions) | ## Prerequisites Before configuring the connector: * A Box enterprise account with permission to create OAuth applications. * A Box OAuth application configured for Artemis. * An OAuth profile created in Artemis using the Box application's credentials. This profile is used to authenticate the connector. ## Creating a Box OAuth application For instructions on creating a Box OAuth application, see the Box Developer documentation: [**Create a Box OAuth 2.0 application**](https://developer.box.com/guides/authentication/oauth2/oauth2-setup). Configure the following application scopes: * `root_readonly` * `root_readwrite` Use the **callback URL** displayed when creating the OAuth profile in Artemis as the **OAuth 2.0 Redirect URI** for your Box application. ## Configure content for ingestion After selecting the Box OAuth profile, configure the content that Artemis should crawl and index. The connector can index only the content that the authenticated Box account is authorized to access. ## Scope Choose the content that should be indexed. | **Field** | **Description** | | - | - | | **Folder scope** | Select **All files** to crawl all content accessible to the authenticated account, or **Selected folders** to limit indexing to specific folders. | | **Include subfolders** | Includes nested folders under the selected scope. | | **Modified after** | Index only files modified after the selected date. Useful for limiting the initial crawl or performing incremental indexing. | | **File types** | Select the file types to include in the crawl, or choose **All supported files** to index every supported file type. | | **Enforce Box permissions** | Preserves Box document permissions (ACLs) so only authorized users can search and access indexed content.

The Box connector in Artemis automatically mirrors Box's own access controls: it grants visibility to the file owner and to every user or group listed as a collaborator on that file (fetched directly from Box's collaborations API), expanding groups to include their individual members. Permissions are refreshed as files sync and group memberships are re-checked every 24 hours. If Box's collaboration data can't be fetched, the file is withheld from search. | ## Generate proposal Before activation, the platform validates the configuration and prepares the connector. | **Action** | **Description** | | - | - | | **Generate proposal** | Validates the connection and generates the proposed crawl configuration. | | **Dry run** | Performs a validation without activating the connector, allowing you to verify the configuration. | | **Proposal preview** | Displays the package summary, permission configuration, and validation results before activation. | | **Activate** | Activates the connector and starts the initial crawl after successful validation. | ## Crawl behavior The Box connector: * Discovers files within the configured scope. * Optionally crawls nested folders. * Extracts supported document types for indexing. * Synchronizes Box access permissions (ACLs) when **Enforce Box permissions** is enabled. * Indexes only documents accessible to the authenticated Box account. # Confluence Cloud connector Source: https://koreai.mintlify.app/agent-platform/connectors/confluence [Back to the list of Connectors](/agent-platform/knowledge#connectors) The Confluence Cloud Connector integrates Search AI with your Atlassian Confluence Cloud account, enabling intelligent search over your organization’s knowledge articles using OAuth 2.0 authentication. ## Specifications | Specifications | Details | | - | - | | Supported content | Pages, Blog Posts, Attachments, Comments | | Permission-aware ACL support | Yes | | Content filtering | Yes | Attachments and comments are synchronized only for the selected parent content. When permission-aware ACLs are enabled, search results respect the permissions configured in Confluence Cloud. ## Prerequisites Ensure that: * You have access to the Confluence Cloud sites you want to index. * You have the required permissions to access content in the Confluence Cloud instance. * If using webhook-based synchronization, ensure that webhook integration is configured for the Confluence Cloud site. * Create an [Atlassian OAuth application](https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/#oauth-2.0-\(3lo\)-apps) and authorize it with the required scopes. The Confluence Cloud connector requires the following OAuth scopes: * `read:space:confluence` * `read:page:confluence` * `read:blogpost:confluence` * `read:attachment:confluence` * `read:comment:confluence` * `read:content-details:confluence` * `read:user:confluence` * `read:email-address:confluence` * `read:jira-user` * `read:group:confluence` * `read:space.permission:confluence` * `search:confluence` * `offline_access` * Create an Atlassian OAuth Profile under `Project Settings > Auth Profiles` before configuring the connector. ## Connector configuration ### Connection Specify the Confluence Cloud site and authentication settings. | Field | Description | | - | - | | Atlassian OAuth profile | Specifies the OAuth profile used to connect to the Confluence Cloud instance. Use an existing auth profile or create a new one. | | New OAuth profile | Creates a new Atlassian OAuth profile. | | Discover sites | Discovers Confluence Cloud sites available using the selected OAuth profile. | | Confluence site | Specifies the Confluence Cloud site to synchronize. | | Required OAuth coverage | Displays the OAuth scopes required by the connector. | ### Scope and filters Select the content to synchronize and configure content filters. | Field | Description | | - | - | | Pages | Synchronizes Confluence pages. | | Blog posts | Synchronizes Confluence blog posts. | | Attachments | Synchronizes attachments associated with the selected pages and blog posts. | | Space keys | Specifies one or more Confluence spaces to synchronize. | | Discovered spaces | Displays the spaces discovered from the selected Confluence site. | | Content filters | Selects the content type for which filters are configured. | | Modified after | Synchronizes content modified after the specified date. | | Modified before | Synchronizes content modified before the specified date. | | Ancestor page IDs | Restricts synchronization to content under the specified parent pages. | | Attachment formats | Specifies the attachment formats to synchronize. | | Include comments | Synchronizes comments associated with the selected pages and blog posts. | | Include archived content | Includes archived pages and blog posts during synchronization. | | Advanced CQL Override | Specifies a Confluence Query Language (CQL) query to further filter the content synchronized. | ### Permissions Configure permission-aware synchronization. When enabled, Confluence permissions are synchronized and enforced during search so that users only see the knowledge content that they're authorized to access. | Field | Description | | - | - | | Permission-aware ACL | Synchronizes Confluence access permissions and applies them to search results. | Generate a Proposal Preview and optionally run a Dry Run to validate the connector configuration before starting the initial synchronization. Once you have verified the configuration, select **Approve & Start** to activate the connector. # Custom HTTP connector Source: https://koreai.mintlify.app/agent-platform/connectors/custom-connector [Back to the list of Connectors](/agent-platform/knowledge#connectors) Connect a knowledge base to any REST API or HTTP-accessible data source. The Custom HTTP Connector connects a knowledge base to any REST API or HTTP-accessible data source and ingests its content for search and retrieval. Use it when no pre-built connector exists for your source system. The connector supports: * Full sync and delta sync (incremental updates) * Multiple document objects from one connector * Permission-aware indexing (ACL crawling) * Custom scripting to parse non-standard responses ## How it works 1. The connector builds a pre-authenticated HTTP client from the attached auth profile. 2. It paginates the list endpoint and collects item identifiers. 3. It calls the content endpoint once per item to retrieve the document body. 4. It chunks, embeds, and indexes each document with its metadata and ACL permissions. ## Setting up the connector Configuring the Custom HTTP Connector involves six steps: selecting a mode, connecting to your API, defining listing behavior, mapping content and document structure, mapping fields, and reviewing the configuration. ### Step 1: Mode Select the mode that matches your API structure. This determines how the connector fetches content. | Mode | Use when | How it works | | - | - | - | | **Most APIs** | Your API has separate list and detail endpoints (for example, `GET /articles` followed by `GET /articles/{id}`). | The connector calls the list endpoint to discover new or updated items, fetches each item individually from the content endpoint, then maps, chunks, embeds, and indexes the retrieved content. | | **A Single API** | One paginated endpoint returns the complete content for each item, with no separate content request required. | The connector pages through the API response and maps each item directly to a document. The per-item content fetch stage is skipped. | | **A Set of IDs** | You have a predefined list of record IDs to index. | You specify the IDs in the connector configuration. The connector fetches and indexes each record individually. | ### Step 2: Connect Set the API base URL and configure authentication. These settings apply to every request the connector makes for listing, fetching, and connection testing. | Field | Description | | - | - | | **Base URL** | The root URL for your API. Endpoint paths configured in later steps are appended to this URL. Enter the full URL, including the protocol — for example, `https://jsonplaceholder.typicode.com/`. | | **Auth profile** | The authentication profile the connector uses to call your API. Defaults to **No auth** for public APIs. Select a saved profile from the dropdown if your API requires authentication. | | **mTLS client certificate** (optional) | A client certificate for mutual TLS authentication, required if your API expects the connector to present a certificate during the TLS handshake. Defaults to **None**. | | **Connection test path** (optional) | A path appended to the Base URL, used only to verify connectivity. Defaults to `/`. If the root URL returns an error, point this to a real endpoint, such as `/health` or `/v1/items`. This setting affects only the connectivity test, not connector behavior. | **Testing the connection**: Click **Test Connection** to verify that the connector can reach your API using the configured Base URL and auth profile. A successful test confirms connectivity only — it doesn't validate the listing or content endpoints configured in later steps. ### Step 3: Listing Define one or more object types to sync from your API. Each object syncs independently and is indexed as a separate searchable document type. #### Objects You can configure multiple object types from the same API. Each one appears as a separate tab in the Listing step. Click **Add object** to configure another. For each object, configure the following: * **Object name**: A URL-safe identifier for the object type. Letters, numbers, hyphens (`-`), and underscores (`_`) are allowed — for example, `articles` or `posts`. * **Listing Mode**: The mode selected in Step 1, shown here as read-only. To use a different mode, return to Step 1. **Listing API request**: Configure the endpoint used to retrieve the list of records. | Field | Description | | - | - | | **Method** | The HTTP method for the request. | | **URL** | The endpoint path appended to the Base URL from Step 2 — for example, `/items`. | | **Params** | Query parameters to include in the request. Click **Add param** to add a key-value pair. Parameters can reference variables using the variable chip selector. | | **Headers** | Request headers required by the listing endpoint. | Click **Send** to test the request and inspect the response in the preview panel. **The object**: Defines how a record from the listing response maps to a searchable document. * **Items array dot-path**: The dot-path to the array in the API response that contains the individual records. For example, if the response is: ```json theme={null} { "data": { "results": { "items": [] } } } ``` enter `data.results.items`. Leave this field blank if the response root is the array itself. You can click the array in the response preview to populate the path automatically, or enter it manually. * **Attributes**: The fields to extract from each record. Click **Add attribute** to map fields from the response. * **Pagination**: Enable this if the listing endpoint returns results across multiple pages. When enabled, values from the response are stored as `ctx` variables and referenced in subsequent requests. For each pagination field, specify: * **Field name** * **Source path** in the response (for example, `meta.next_cursor`) * **Default value** (optional), used for the first request Additional setting: * **Stop paging when**: Controls when pagination stops. Defaults to **Auto (no more results)**, which stops when the API returns no new records. * **Max pages per sync**: Limits the number of pages processed during a sync. Defaults to **100000**. Enable **No limit** to process all pages. **Sync and freshness**: Select how the connector detects changes and performs delta synchronization. | Option | Description | | - | - | | **Read everything every run** | Fetches all records on every sync and compares them locally to detect changes. Disables scheduled delta sync. | | **API filters by changed time** | Uses a date or time filter to retrieve only records modified since the previous sync. | | **API returns a change cursor** | Uses a change cursor returned by the API to retrieve only new or updated records. | | **API sorts the newest changes first** | Retrieves records in descending modification order and stops once it reaches older records. | | **List all and compare the modified field** | Fetches the full list on each run and compares each record's modified field to determine changes. | | **Compare fetched content hash** | Computes a content hash for each record and reindexes only records whose content has changed. | **Enable scheduled delta**: Available only when a delta synchronization mode is selected. When enabled, the connector runs delta syncs automatically at the interval specified in the **Interval** field (in minutes). ### Step 4: APIs Configure the per-item content request — the API call the connector makes to fetch the full content of each record discovered during the Listing step — and define how the response maps to a searchable document. Each object configured in Step 3 appears as its own tab. #### Content API The endpoint the connector calls once per item to retrieve its full content. | Field | Description | | - | - | | **Method** | The HTTP method for the request. | | **URL** | The endpoint path appended to the Base URL — for example, `/posts/{id}`. Use document field variables in curly braces to inject values from the list response, for example, `{id}` inserts the `id` attribute of each item. Click a variable chip to insert a field reference into the URL at the cursor position. | | **Params** | Query parameters to include in the content request. Click **Add param** to add a key-value pair, or click a variable chip to insert a reference. No parameters are added by default. | | **Headers** | Request headers required by the content endpoint. | Click **Send** to execute the request with the test values configured below, then inspect the response in the preview panel. **Testing with a sample document**: Set test values for any document field variables used in the URL. Click **Load sample from List** to populate the variables automatically using a real item from the listing response. #### The document Controls how the connector treats each fetched item and which fields it extracts. **How should we treat each item?** Choose how the connector handles items returned by the Content API. | Option | Description | | - | - | | **Document** | Each list item becomes one document. The content request enriches it: attributes from the list response are read into the request, and fields are then picked from the content response. Use this for articles, pages, posts, or any item whose full content lives in a single detail endpoint. | | **Document + fields** | Each list item becomes one document and also exposes individual fields for selective extraction. Use this when you need both the full document body and specific structured fields — such as title, author, or date — indexed as separate facets. | | **Record** | Each list item is treated as a structured record rather than a content document. Fields are extracted and indexed individually, with no document body. Use this for structured data such as product catalogs, user records, or datasets with no long-form content to index. | **Object attributes**: The attributes mapped in the Listing step. These are available as variables in the content request URL and carry through to the final document. To add or change attributes, return to the Listing step, or click a field in the response preview and select **Add as attribute**. **Document body**: The dot-path within the content response that holds the main document content — for example, `data.body` or `content.html`. Leave empty to use the entire response as the document body. **Content type**: Defines how the document body should be interpreted. Defaults to **Auto-detect from response**. | Option | Description | | - | - | | **Auto-detect from response** | Infers the content type from the response headers and body. Use this when the API sets the correct `Content-Type` header. Default. | | **Plain text** | Treats the document body as unformatted plain text. Use this when the content contains no markup or formatting. | | **Markdown** | Treats the document body as Markdown and parses the formatting before indexing. | | **HTML** | Treats the document body as HTML, stripping tags and extracting readable content before indexing. | | **JSON** | Treats the document body as a JSON string to parse, rather than indexing it as a literal string. | ### Step 5: Fields Map the attributes defined in the Listing and Content steps to your knowledge base's canonical fields. Mapped fields become filterable metadata. You can adjust these mappings after the first sync. ### Step 6: Review Review the configuration and save it. Click **Start Sync** to begin ingestion. ## Connector overview Once configured, the connector's overview page shows sync status, a configuration summary, indexed objects, and recent activity. **Sync**: Click **Sync now** to trigger a sync manually. * **Full sync**: Fetches all records from the API and re-indexes them, regardless of what was previously indexed. * **Delta sync**: Fetches only new, changed, or deleted records since the last sync. Available only when a delta mode is configured in the Listing step. **Reconfigure**: Return to the setup wizard to modify any connector setting. **Health**: Shows the result of the most recent sync, including: * Status (Completed or Failed) * Sync type (Full or Delta) * Number of documents processed * Number of failed documents * Timestamp **Configuration**: A summary of the connector setup, including strategy, mode, and routes. **Objects**: Each object type configured in the connector, with its sync state, document count, last sync time, and any failures. Click **View documents** to inspect the indexed documents for an object. **Recent Activity**: The latest sync run's status, including the number of documents processed, failed, and skipped. # FAQ connector Source: https://koreai.mintlify.app/agent-platform/connectors/faq The **FAQ connector** lets you upload frequently asked questions and their answers to a knowledge base. The uploaded FAQ content is **i**ndexed and used by the knowledge base to retrieve relevant information and generate answers to user questions. You can upload FAQ data in **JSON or CSV** format and configure how each field is used for indexing, answering, and citations. The connector supports mapping FAQ fields, adding custom fields, configuring citations, and choosing how FAQ records are chunked for indexing. ## Step 1: Add an FAQ source 1. Navigate to the knowledge base. 2. Select **FAQ** as the source. 3. Enter a name for the FAQ source. 4. Upload one or more JSON or CSV files. 5. You can also add FAQs manually instead of uploading a file. The FAQ source provides sample JSON and CSV files that you can download and use as templates. ## Step 2: Prepare the FAQ file **Required fields** | **Field** | **Type** | **Description** | | - | - | - | | **Question** | Text | The question that a user may ask. | | **Answer** | Text | The answer provided for the question. | All the other optional fields present in the file are also uploaded and can be mapped to index fields. ## Step 3: Map file fields After you upload a file, the connector displays the fields detected in the file. The connector automatically matches file keys to FAQ fields when their names match. Fields that do not match can be mapped manually. For each field, select or review the corresponding FAQ field from the **Maps to** dropdown. For example, map the file keys to the corresponding FAQ fields as shown below. ```text theme={null} question → Question answer → Answer url → Citation URL ``` **Field usage** | **Option** | **Description** | | - | - | | **Embed** | Includes the field in the content indexed and retrieved by the knowledge base. | | **Answer** | Makes the field available as a separate answer field and can be combined with **Embed**. | | **Exclude** | Does not store the field in the FAQ data. | | **Citation link** | Uses the field as the citation URL for the FAQ. | ## Step 4: Add custom fields If your FAQ file contains information that is not covered by the default fields, you can create custom fields. 1. Select **Add custom field**. 2. Define the custom field. 3. Map the corresponding field from your uploaded file. 4. Select whether the field should be embedded, included in the answer, or excluded. Up to **10 custom string fields** can be used. ## Step 5: Configure chunking Under **Advanced settings**, select how FAQ records should be chunked for indexing. * **Standard - by FAQ:** Creates **one chunk per FAQ**. This is the default option and provides faster indexing. * **Precise - by question:** Creates **one chunk per question**, including the answer to the question. This option provides more precise question-level indexing but takes longer to index. ## Step 6: Save the FAQ source After uploading the file and configuring field mappings and chunking: 1. Review the field mappings. 2. Verify that **Question** and **Answer** are mapped correctly. 3. Verify the usage configured for each field. 4. Select the required chunking strategy. 5. Click **Save**. The connector processes the uploaded FAQs. Once the FAQ source is indexed and available in the knowledge base, an agent can use it to retrieve relevant FAQs and answer user questions. ## Points to Note * Use **JSON** or **CSV** format for the FAQ data file. * **File size limit:** 100 MB per file. * **Custom field limit:** 10 custom fields. # Google Drive connector Source: https://koreai.mintlify.app/agent-platform/connectors/googledrive [Back to the list of Connectors](/agent-platform/knowledge#connectors) The Google Drive connector enables the platform to index content from Google Drive into a knowledge base. During setup, you authenticate using a Google OAuth profile, select the folders to index, configure content filters, and enable permission-aware search. The connector supports indexing content from My Drive and Shared Drives, with configurable file type filters, export formats for Google Workspace documents, and synchronization settings. ## Prerequisites Make sure that: * You have access to the Google Drive account or Shared Drive that you want to index. * You've created a Google OAuth profile with the required permissions. To create a new profile, go to **Settings** > **Auth Profiles** > **Add Profile** > **Integration Profile** > **Google Docs**. [Learn More](/agent-platform/administration/integrations-settings#auth-profiles). * The OAuth profile includes read-only Google Drive access. * You have permission to access the folders that you want to synchronize. * The connector requires a Google OAuth profile with the scope as `https://www.googleapis.com/auth/drive.readonly`. ## Create a Google Drive connector 1. Navigate to **Knowledge Base** > **Sources**. 2. Click **Add Source**. 3. Select **Google Drive**. The connector opens in the Connect step of the setup wizard. Provide the following details. | Field | Description | | :- | :- | | Google OAuth Profile | Specifies the Google OAuth profile used to authenticate the connector with Google Drive. Only authorized OAuth profiles are available for selection. Create a new Google OAuth profile for authenticating with Google Drive, if it doesn't exist. | | Drive Scope | Specifies the Google Drive location to index. You can choose content from your personal Google Drive or a Shared Drive. | | Folders | Specifies one or more folders to synchronize. Only content from the selected folders is indexed. | | Modified After | Restricts synchronization to files modified after the specified date. Leave blank to index all eligible files. | | File Types | Specifies the file types to include during synchronization. Only files matching the selected types are indexed. | | Google-native Export Format | Specifies the format used to export Google Workspace files, such as Google Docs, Sheets, and Slides, before extracting their content for indexing. | | Index Subfolders | Determines whether subfolders under the selected folders are included during synchronization. | | Enforce Google Drive Permissions | Enables permission-aware search by indexing Google Drive access permissions and applying them to search results. | | Generate Proposal Preview | Generates a proposal preview to validate the connector configuration, selected scope, permissions, and estimated indexing workload before approval. | | Run Dry Run | Validates the connector configuration without initiating synchronization. | | Approve & Start | Approves the connector configuration and starts the initial synchronization. | | Proposal Preview | Displays the status of the generated proposal preview and summarizes the connector configuration before approval. | Click **Generate Proposal Preview** to validate the connector configuration, selected scope, permissions, and estimated indexing workload before approval. Perform **Dry Run** to validate the connector configuration without initiating synchronization. Click **Approve & Start** to initiate the synchronization. ## Google Drive connector management After you activate the connector, you can monitor its health, review synchronization activity, verify security status, and update the connector configuration. ### Overview tab The Overview tab provides a summary of the connector status and recent synchronization activity. | Field | Description | | :- | :- | | Health | Displays the current connector status, last synchronization details, and document processing statistics. | | Configuration | Displays the connector strategy, synchronization mode, and route information. | | Objects | Lists the configured crawl objects along with their current state, indexed document count, last synchronization time, and failed documents. | | Recent activity | Displays the latest synchronization run, including its status, processed documents, failures, and skipped documents. | | View documents | Opens the list of documents indexed by the connector. | | Resume Sync | Resumes synchronization if the connector is paused. | | Sync Now | Immediately starts a synchronization, regardless of the configured schedule. | ### Activity tab The Activity tab displays the synchronization history for the connector. | Field | Description | | :- | :- | | Sync activity | Lists synchronization runs with details such as status, synchronization type, start time, duration, processed documents, failures, skipped documents, removed documents, validation status, and processed objects. | | Permission syncs | Displays the history of permission synchronization operations, including the trigger, mode, duration, synchronized documents, and subjects. | | Webhook events | Displays webhook notifications generated by the connector during synchronization. | ### Security tab The Security tab provides the current security posture of the connector and highlights any configuration issues. ### Settings tab The Settings tab lets you modify the connector configuration after you've activated it. The following additional settings are available in this section. | Field | Description | | - | - | | Reconnect | Re-establishes the connection using the selected Google OAuth profile. | | Manage credentials | Opens the OAuth profile associated with the connector for credential management. | | Include trashed files | Includes files from the Google Drive Trash during synchronization. By default, trashed files are excluded. | | Allow advanced Drive query | Enables the use of a custom Google Drive query in addition to the selected folder scope to further filter indexed content. | | Generated Drive Query | Displays the Google Drive query generated from the current connector configuration. | | Run a full sync after saving | Performs a complete re-index of the selected content after the configuration changes are saved. | | Recrawl permissions after saving | Refreshes document permission metadata after the configuration changes are saved without performing a full content synchronization. | | Save Changes | Saves the updated connector configuration. | Changes to the crawl scope, filters, or authentication profile do not automatically trigger a content synchronization or permission recrawl. To apply the changes immediately, manually initiate a Full Sync or a Delta Sync. Otherwise, the changes take effect during the next scheduled synchronization. # Jira connector Source: https://koreai.mintlify.app/agent-platform/connectors/jira [Back to the list of Connectors](/agent-platform/knowledge#connectors) Connect Jira Cloud to Artemis to ingest Jira issues into your knowledge base. The connector supports filtering by project, issue metadata, and modification date, with optional ingestion of issue comments. It also supports Jira permission-aware access control (ACL) to ensure users only retrieve content they are authorized to access. | **Specification** | **Details** | | - | - | | Repository type | Cloud | | Supported content | Issues and Comments, Attachments, Worklogs related to Issues. | | Content filtering | Yes | | Permission-aware ACL | Yes | | Attachment formats | PDF, DOCX, TXT, Markdown, HTML, CSV, JPG/JPEG, PNG, XLSX, and JSON | ## Prerequisites Before configuring the connector, ensure you have: * Administrator access to the Jira Cloud site. * A Jira OAuth application or supported authentication credentials. * A configured Jira Authentication Profile in the platform. * Access to the Jira projects that you want to ingest. ## Configure the Jira connector ## Connection Settings Use the **Connect a Jira Cloud Site** section to establish the connection. | **Field** | **Description** | | - | - | | **Jira auth profile** | Select an existing Jira authentication profile. Click **New auth profile** to create one if required. | | **Auth type** | Authentication mechanism associated with the selected authentication profile. The Jira connector supports three types of authentication: Basic, Bearer Token, and OAuth.

For details on creating an OAuth app in Atlassian, see [Create OAuth 2.0 (3LO) apps](https://developer.atlassian.com/cloud/jira/software/oauth-2-3lo-apps/). The required scopes and callback URLs are provided in the Artemis authentication profile creation UI. | | **Jira site URL** | URL of your Jira Cloud instance. Example: `https://company.atlassian.net`. | | **Discovered Jira site** | Automatically discovers and lists Jira sites associated with the selected authentication profile. | ## Projects and filters Use this section to limit the scope of content ingestion. | **Field** | **Description** | | - | - | | **Project keys** | Comma-separated list of Jira project keys to ingest. Only issues from these projects are synchronized. In **Jira**, a **project key** is a short, unique identifier assigned to a project. It forms the prefix of every issue ID in that project. | | **Issue types** | Restrict synchronization to specific issue types such as Bug, Story, or Task. | | **Statuses** | Synchronize only issues with the specified workflow status. | | **Labels** | Synchronize only issues containing the specified labels. | | **Modified after** | Synchronize issues updated after the selected date. | | **Modified before** | Synchronize issues updated before the selected date. | > **Note:** Project keys define the primary source boundary. Optional filters further reduce the issues that are synchronized. ## Objects and child resources Choose which Jira objects to synchronize. | **Option** | **Description** | | - | - | | **Issues** | Synchronizes Jira issues, including the summary, description, status, priority, assignee, and reporter fields. | | **Comments** | Synchronizes comments associated with each issue. | | **Attachments** | Synchronizes and indexes attachments associated with issues. Supported formats: PDF, DOCX, TXT, Markdown, HTML, CSV, JPG/JPEG, PNG, XLSX, and JSON. | | **Worklogs** | Synchronizes worklog entries associated with each issue. | **Note:** Child resources (Comments, Attachments, and Worklogs) are synchronized only when enabled. ## Advanced JQL Use **Advanced JQL** to define additional Jira Query Language (JQL) filters. The specified JQL is combined with the selected project scope and optional filters during synchronization. Example: ```sql theme={null} assignee = currentUser() AND priority = High AND resolution = Unresolved ``` ## Permission-aware ACL Enable **Permission-aware ACL** to preserve Jira access permissions in the search index. When enabled: * Jira issue visibility is synchronized with the indexed documents. * Search results are filtered based on each user's Jira permissions. * The connector fails closed if document visibility cannot be determined. When available, ACL evidence is projected from Jira entities such as: * Assignee * Reporter * Watchers Only users associated with an issue through one of the entities listed above can view that issue in search results. ## Review and activate 1. Generate a proposal to review the scope of synchronization. 2. Run a **Dry run** to validate the configuration and preview the synchronized content. 3. Click **Approve & Sync** to activate the connector. # Salesforce connector Source: https://koreai.mintlify.app/agent-platform/connectors/salesforce The Salesforce Connector enables Artemis to index Salesforce content and make it searchable. The connector authenticates using OAuth 2.0 Authorization Code (User Delegated) and indexes content accessible to the authorized Salesforce user. The connector supports indexing multiple Salesforce standard and custom objects, configurable content filters, scheduled synchronization, and permission-aware access control. | **Specification** | **Details** | | - | - | | **Repository Type** | Cloud | | **Authentication** | OAuth 2.0 Authorization Code (User Delegated) | | **Supported Content** | Knowledge Articles, Cases, Accounts, Contacts, Leads, Opportunities, Tasks, Salesforce Files, Attachments, Comments, Custom Objects | | **Content Filtering** | Yes | | **Permission-aware ACL** | Yes | | **Scheduled Synchronization** | Yes | ## Prerequisites * Administrator access to the Salesforce application. * A Salesforce Connected App to enable Artemis and Salesforce integration. ## Step 1. Create an External Client App in Salesforce 1. In Salesforce, go to **Setup**, enter **App Manager** in the **Quick Find** box, then select **App Manager**. 2. Click **New External Client App**. 3. Enter an app name and provide the basic details. 4. Enable **OAuth Settings**. 5. Under **App Settings**, provide the **Callback URL**. Copy the Callback URL from the auth profile creation widget in Artemis. 6. Select the following **OAuth Scopes**: 1. Full access (`full`) 2. Perform requests at any time (`refresh_token`, `offline_access`) 3. Manage user data via APIs (`api`) 7. Click **Create**. This generates a consumer key and secret. 8. Copy these and provide them in the auth profile in Artemis. ## Step 2. Create the OAuth Profile in Artemis 1. Open the Salesforce connector. 2. Click **New Salesforce OAuth Profile** under **Salesforce OAuth Profile**. 3. Enter: * Name and description of the profile. * Client ID (Consumer Key) * Client Secret (Consumer Secret) 4. Create the profile and use it for authorization of the Salesforce Connector. 5. After authorization, Artemis can automatically discover the domain and the organization ID. ## Step 3. Configure the Content Scope Select the Salesforce objects to include in the indexing scope. Depending on the selected object type, additional configuration options are available. ### Object-specific Configuration | **Object Type** | **Additional Configuration** | | - | - | | **Knowledge Articles** | Configure the **Knowledge Languages** to index by specifying one or more Salesforce Knowledge locale codes (for example, `en_US`, `fr_FR`, `de_DE`). Optionally specify **Additional Body Fields** as a comma-separated list of Salesforce field API names (for example, `Details__c`, `Resolution__c`) whose contents should be appended to the indexed article body. Specify **Filter Fields** as a comma-separated list of Knowledge Article field API names (for example, `Country_Code__c`, `Product__c`) to publish as filterable metadata in Artemis. | | **Comments** | Select whether to index Case Comments, Chatter Feed Posts, or both. | | **Attachments / Salesforce Files** | Select an **Attachment Format Policy** to control which file formats are indexed. | | **Cases, Accounts, Contacts, Leads, Opportunities, Tasks** | No additional configuration is required. You can filter these objects using the options described in **Configure Filters**. | > **Note:** To find a field's API name in Salesforce, navigate to **Setup → Object Manager → *Object* → Fields & Relationships**, then select the field. The API Name is displayed in the field details. ## Step 4. Configure Filters Each selected object type supports independent filtering. For each object, configure: * Modified After * Modified Before * Parameter Filters Parameter filters translate into Salesforce SOQL `WHERE` clauses, so only matching records are indexed. This enables different filtering criteria for each object type. ## Step 5. Configure Permissions Enable **Permission-aware ACL** to synchronize Salesforce permissions. When enabled, Artemis imports native Salesforce access controls, including: * Sharing Rules * Record Visibility * User Roles * Permission Sets Search results are filtered so users only see content they are authorized to access in Salesforce. ## Step 6. Configure the Synchronization Schedule Specify the reconciliation interval. Artemis periodically synchronizes the selected Salesforce content to keep the index up to date. ## Step 7. Preview and Start Indexing Before indexing begins: 1. Click **Generate Proposal Preview**. 2. Review the selected objects, filters, indexing scope, and the permission configuration. 3. Click **Approve & Start**. # ServiceNow connector Source: https://koreai.mintlify.app/agent-platform/connectors/servicenow [Back to the list of Connectors](/agent-platform/knowledge#connectors) Connect ServiceNow to the platform to index and search content managed in your ServiceNow instance. The connector supports indexing knowledge articles, incidents, catalog tasks, generic tasks, attachments, and associated comments and work notes. You can configure object-specific filters, synchronize ServiceNow access control lists (ACLs), and use encoded queries to precisely control the content that's indexed. ## Specifications | Specifications | Details | | - | - | | Supported content | Knowledge Articles, Incidents, Catalog Tasks, Tasks, Attachments, Comments and Work Notes | | Permission-aware ACL support | Yes | | Content filtering | Yes | Attachments, comments, and work notes are synchronized only for the parent objects that you select during the connector configuration. Permission-aware ACLs ensure that users can access only the content that they're authorized to view in ServiceNow. ## Prerequisites Make sure that: * You have access to the ServiceNow instance. * You have the required permissions to access content in the ServiceNow instance. * If using webhook integration, you must configure and connect the ServiceNow instance. * To use OAuth 2.0, configure an OAuth endpoint in your ServiceNow instance. Skip this step if you are using Basic authentication. Follow the [ServiceNow documentation](https://docs.servicenow.com/bundle/washingtondc-platform-security/page/administer/security/task/t_CreateEndpointforExternalClients.html). The redirect URL required during OAuth configuration is available in the authentication profile. It typically has the following format: `https://agents.kore.ai/oauth/auth-profile-callback`. * Create a ServiceNow authentication profile under **Project Settings** > **Auth Profiles** before configuring the connector. ## Connector configuration ### Connection Specify the ServiceNow instance and authentication settings. | Field | Description | | - | - | | ServiceNow auth profile | Specifies the authentication profile used to connect to the ServiceNow instance. | | New auth profile | Creates a new ServiceNow authentication profile. | | Auth type | Specifies the authentication method used to connect to ServiceNow. | | Instance URL | Specifies the URL of the ServiceNow instance to connect to. | | Security posture | Displays the current authentication and ACL protection status for the connector. | ### Objects and child resources Select the ServiceNow objects to synchronize. | Field | Description | | - | - | | Knowledge articles | Synchronizes knowledge articles. | | Catalog tasks | Synchronizes service catalog task records. | | Attachments | Synchronizes attachments associated with the selected parent records. | | Incidents | Synchronizes incident records from the IT Service Management module. | | Tasks | Synchronizes generic task records. | | Comments and work notes | Synchronizes comments and work notes associated with the selected incidents, tasks, and catalog tasks. | Knowledge articles are enabled by default. Enable other objects based on your indexing requirements. ### Filters and encoded query Configure filters to control which records does the platform synchronize. | Field | Description | | - | - | | Object filters | Selects the ServiceNow object for which you've configured the filters. | | Knowledge article states | Specifies the knowledge article states to include during synchronization. | | Active records only | Synchronizes only active records (state of record = active). | | Modified after | Synchronizes records modified after the specified date. | | Modified before | Synchronizes records modified before the specified date. | | Allow public knowledge ACLs | Includes publicly available knowledge articles when permitted by the source configuration. | | Advanced encoded query | Specifies a ServiceNow encoded query to further filter the records synchronized for the selected object. | ### Permissions Configure permission-aware synchronization. When enabled, ServiceNow ACLs are indexed and enforced during search to ensure users only receive results that they're authorized to access. | Field | Description | | - | - | | Permission-aware ACL | Synchronizes ServiceNow Access Control List (ACL) information and applies source permissions to search results. | Review the proposal and perform a dry run to verify that the connector is configured correctly and fetches the required content. **Approve & Start** sync after review. ## ServiceNow connector management After the initial synchronization, you can monitor the connector status and manage its configuration from the Overview, Activity, Security, and Settings tabs. The Overview tab provides a summary of the connector's overall health and synchronization status. It also displays the indexed content grouped by ServiceNow object type, along with ingestion statistics. Any documents or records that fail during synchronization are listed on this page. The list lets you quickly identify and troubleshoot ingestion issues. The Settings tab lets you update the connector configuration. After saving your changes, initiate a Full Sync or Delta Sync manually for the updates to take effect immediately. Otherwise, the platform applies the changes during the next scheduled synchronization. In addition to the configuration options available during connector setup, the Settings page provides the following options. | Field | Description | | - | - | | Saved encoded query preview | Displays the effective ServiceNow encoded query generated from the configured filters. | | Scheduled delta sync | Enables scheduled incremental synchronization to periodically index changes from the ServiceNow instance. | | Delta cadence | Specifies how frequently the scheduled delta synchronization runs. | | Queue full sync after save | Queues a full synchronization after the updated configuration is saved. | | Queue permission recrawl after save | Queues a permission recrawl after saving the configuration when permission-aware ACL is enabled. | Changes to the connector configuration are applied during the next synchronization. To apply them immediately, manually start a Full Sync or Delta Sync after saving the changes. # SFTP connector Source: https://koreai.mintlify.app/agent-platform/connectors/sftp [Back to the list of Connectors](/agent-platform/knowledge#connectors) The SFTP connector enables Artemis to index content from an SFTP server into a knowledge base. During setup, you authenticate using an SFTP authentication profile, configure the server connection, specify the folders to synchronize, apply file filters, and validate the configuration before starting synchronization. The connector supports **Basic** and **SSH Key** authentication, configurable folder scope, file filtering, and scheduled synchronization. ## Specifications | Specification | Description | | - | - | | Supported content | PDF, DOCX, TXT, CSV, MD, XLSX, JSON, HTML | | Authentication | Basic, SSH Key | | Permission-aware search | No | | Real-time sync | No | | Delta sync | Yes | ## Prerequisites Make sure that, * You have access to the SFTP server you want to index. * You've created a SFTP authentication profile (from **Settings > Auth Profiles**). * You've configured the authentication profile with one of the following: * Username and password for **Basic** authentication. * Username, SSH private key, and optional passphrase for **SSH Key** authentication. * You have read access to the folders you want to synchronize. * You know the SFTP host, port, and, if required, the server host key fingerprint. ## Configure an SFTP connector 1. Navigate to **Knowledge Base** > **Sources**. 2. Click **Add Source**. 3. Select **SFTP**. 4. Enter the following details. ### Connection | Field | Description | | - | - | | SFTP Auth Profile | Authentication profile used to connect to the SFTP server. You can use **Basic** or **SSH Key** authentication for the connection. | | Auth Type | Authentication method. Supported values are **Basic** and **SSH Key**. Select the same authentication method configured in the selected Auth Profile. | | SFTP Host | Hostname or IP address of the SFTP server. | | Port | SFTP server port. The default port is **22**. | | SHA-256 Host Key Fingerprint | Server's SHA-256 host key fingerprint for host identity verification. | | Username | Username used to authenticate with the SFTP server. | ### Folder scope | Field | Description | | - | - | | Full Sync Root | Root directory to scan during a full synchronization. | | Delta Sync Root | Root directory used during delta synchronization. Leave blank to use the Full Sync Root. | | Recursive Listing | Determines whether nested folders under the configured root are included during synchronization. | | Include Hidden Files | Determines whether hidden files are included during synchronization. Hidden files are excluded by default. | ### Filters and schedule | Field | Description | | - | - | | Include Globs | Glob patterns that identify files to include during synchronization. | | Exclude Globs | Glob patterns that identify files to exclude during synchronization. | | Allowed Extensions | File extensions that are eligible for synchronization. | | Modified After | Restricts synchronization to files modified after the specified date. Leave blank to synchronize all eligible files. | | Max File Size (MB) | Maximum file size that can be synchronized. Files exceeding this size are skipped. | | Max Files Per Run | Maximum number of files processed during a synchronization run. | | Concurrent Downloads | Maximum number of files downloaded simultaneously during synchronization. | | Delta Cadence (Minutes) | Frequency of delta synchronization runs. | ## Actions * Click **Generate Proposal Preview** to validate the connector configuration, selected folder scope, filters, and estimated synchronization workload before approval. * Click **Run Dry Run** to validate the connector configuration without initiating synchronization. * Click **Approve & Start** to initiate synchronization with the configured SFTP server. # SharePoint connector Source: https://koreai.mintlify.app/agent-platform/connectors/sharepoint [Back to the list of Connectors](/agent-platform/knowledge#connectors) The SharePoint connector indexes documents, pages, and files from SharePoint Online into your knowledge base, enabling agents to search and retrieve SharePoint content. ## Specifications | Specifications | Details | | - | - | | Supported content | Articles, pages, and files in .doc, .docx, .ppt, .pptx, .html, .xhtml, .txt, .pdf formats | | Permission-aware ACL support | Yes | | Content filtering | Yes | | Delta Sync | Yes | You can configure the connector using either of the following authentication methods: * **Sign in with Microsoft** * **Azure App Registration (OAuth 2.0 Client Credentials)** ## Prerequisites Before configuring the SharePoint connector, ensure that: * You have a Microsoft 365 tenant with SharePoint Online and access to the SharePoint sites that you want to index. * For authentication: * You have permission to create Azure App Registrations and grant administrator consent. * An Azure App Registration has been created with the required Microsoft Graph API permissions. * If Permission-aware Search is enabled, the required Microsoft Entra ID permissions have been granted. ## Create the Azure App Registration Create an application in Microsoft Entra ID. 1. Sign in to the Azure portal. 2. Navigate to **Microsoft Entra ID** > **App registrations** > **New registration**. 3. Enter an application name (for example, **Artemis SharePoint Connector**). 4. Select **Accounts in this organizational directory only**. 5. Enter the **Redirect URI** as per your deployment. 6. Click **Register**. The application is created with: * **Application (Client) ID** - Use this as the **Client ID**. * **Directory (Tenant) ID** - Use this as the **Tenant ID**. ### Create a Client Secret 1. Open the registered application. 2. Navigate to **Certificates & secrets**. 3. Under **Client secrets**, click **New client secret**. 4. Enter a description. 5. Select an expiry period. 6. Click **Add**. 7. Copy the generated secret value immediately. This is your **Client Secret**. ## Configure Microsoft Graph Permissions 1. Open the registered application. 2. Navigate to **API permissions**. 3. Click **Add a permission**. 4. Select **Microsoft Graph**. 5. Select **Application permissions**. 6. Add the required permissions. 7. Click **Add permissions**. 8. Click **Grant admin consent** for your organization. ### Required Permissions The following permissions are required for all SharePoint connectors. | Permission | Purpose | | - | - | | Sites.Read.All | Read SharePoint site content. | | Files.Read.All | Read SharePoint documents and files. | | User.Read | Read user profile information. | These permissions provide read-only access. ### Additional Permissions for Permission-aware Search Enable Permission-aware Search to ensure search results respect SharePoint access permissions. When enabled, the connector retrieves Microsoft Entra group memberships before returning search results. Grant the following additional Microsoft Graph permissions. | Permission | Purpose | | - | - | | GroupMember.Read.All | Read Microsoft 365 group memberships. | | Directory.Read.All | Read Microsoft Entra directory information. | ## Configure the SharePoint Connector The SharePoint connector authenticates using OAuth 2.0 Client Credentials through Microsoft Entra ID. ### Step 1: Connect The **Connect** page establishes the connection between the platform and Microsoft SharePoint. **Connector Name:** Enter a unique name to identify the connector. **Authentication Method:** Select one of the following authentication methods. #### Sign in with Microsoft Authenticates using your Microsoft account through delegated permissions. The generated access token carries the signed-in user's identity and permissions. #### Azure App Registration Authenticates using an Azure App Registration. This is app-only communication method. Currently, only the Client Credentials authentication flow is supported. Provide the following details from your Azure App Registration and click **Connect**. | Field | Description | | - | - | | Client ID | Azure Application (Client) ID | | Tenant ID | Microsoft Entra Directory (Tenant) ID | | Client Secret | Client secret generated for the application | The SharePoint connector requests read-only Microsoft Graph permissions. It never modifies SharePoint content, and all data is encrypted both in transit and at rest within your dedicated tenant partition. ### Step 2: Configure Scope and Filters After a successful connection, the connector discovers the available SharePoint environment and generates an ingestion proposal. Review or update the following settings before proceeding. | Field | Description | | - | - | | Estimated Sync Impact | Displays the estimated number of selected sites, document count, storage size, and synchronization frequency. These values are estimates and may change after the first synchronization. | | What to Sync | Select the SharePoint sites to synchronize. Search by site name, URL, or Site ID. | | File Types to Include | Select the document formats to synchronize. | | Size Limit | Specify the maximum file size to synchronize. Files larger than the configured limit are skipped. | | Date Range | Synchronize only content created or modified within the selected date range. Leave blank to synchronize all eligible content. | | Exclude Patterns | Specify file or folder patterns to exclude from synchronization. | | Sync Schedule | Configure how frequently the platform synchronizes SharePoint content. | | Filter Rules | Define include or exclude rules to control which content is synchronized. | | Reset to Defaults | Restore all scope and filter settings to their default values. | Click **Save and Continue**. ### Step 3: Configure Field Mapping Configure how SharePoint metadata maps to your knowledge base schema. You can: * Select fields to synchronize. * Mark fields for embedding so they participate in vector search. * Map fields to canonical fields for metadata filtering. The connector templates prepopulate the default field mappings. After the first synchronization completes, any additional fields discovered in the indexed SharePoint content appear in the **Fields** tab for review and mapping. ### Step 4: Review and Sync Review the connector configuration and start the initial synchronization. # Zendesk connector Source: https://koreai.mintlify.app/agent-platform/connectors/zendesk The Zendesk connector enables Artemis to index Zendesk support and knowledge content and make it searchable. The connector authenticates using OAuth 2.0 Authorization Code and indexes content accessible to the authorized Zendesk user. It supports indexing tickets, ticket comments, Help Center articles, configurable content filters, scheduled synchronization, and permission-aware access control. | **Specification** | **Details** | | - | - | | **Repository Type** | Cloud | | **Authentication** | OAuth 2.0 Authorization Code (User Delegated) | | **Supported Content** | Tickets, Ticket Comments, Help Center Articles, Draft Articles | | **Content Filtering** | Yes | | **Permission-aware ACL** | Yes | | **Scheduled Synchronization** | Yes | ## Prerequisites * Administrator access to your Zendesk account. * A Zendesk OAuth application for Artemis authentication. ## Step 1. Create an OAuth client in Zendesk 1. Sign in to the Zendesk Admin Center. 2. Navigate to **Apps and Integrations > APIs > Zendesk API > OAuth Clients**. 3. Create a new OAuth client. 4. Enter the application details. 5. Copy the **Callback URL** from the Artemis **Create Auth Profile** dialog and add it as an authorized redirect URL in Zendesk. 6. After creating the OAuth client, copy the following values: * **Client ID** * **Client Secret** These values are required when creating the authentication profile in Artemis. ## Step 2. Create the OAuth profile in Artemis 1. Open the Zendesk connector. 2. Click **New Auth Profile**. 3. Select **OAuth 2.0 App (User Delegated)**. 4. Enter: * Profile Name * Description (optional) * Zendesk Subdomain * Client ID * Client Secret 5. Verify or configure the OAuth endpoints if required. 6. Create the profile. 7. Select the newly created authentication profile in the connector configuration. ## Step 3. Configure the content scope Select the Zendesk content to include in the indexing scope. | **Content Type** | **Description** | | - | - | | **Tickets** | Indexes Zendesk support tickets, including requester and collaborator metadata. | | **Include Ticket Comments** | Indexes ticket comments as child documents linked to their parent ticket. Available only when ticket indexing is enabled. | | **Help Center Articles** | Indexes published Zendesk Help Center articles, including article metadata and source URLs. | | **Include Draft Articles** | Includes unpublished Help Center articles. Enable only when indexing internal or pre-publication content. | ## Step 4. Configure filters Optionally restrict the content that is indexed. Configure the following filters: | **Filter** | **Description** | | - | - | | **Article Locale** | Restricts indexing to a specific Help Center language or locale (for example, `en-us`). | | **Updated After** | Indexes only content (tickets and articles) updated on or after the specified date. | | **Tickets Updated Before** | Indexes only tickets updated before the specified date. | ## Step 5. Configure permissions Enable **Permission-aware ACL** to synchronize Zendesk permissions. When enabled, Artemis imports the native visibility settings associated with indexed content, including: * Ticket access permissions, ensuring tickets are only returned to users who are permitted to view them in Zendesk. * Help Center user segments, which define the users, organizations, groups, or tags that can access Help Center articles. * Article visibility settings, including public, signed-in user, and user segment-based access. Results are filtered so users only see content they are authorized to access in Zendesk. ## Step 6. Preview and start indexing 1. Click **Generate Proposal Preview**. 2. Review the selected content types, filters, indexing scope, and permission configuration. 3. Click **Approve & Start** to begin indexing. ## Step 7. Configure synchronization settings After the connector is configured and the initial indexing completes, you can manage ongoing synchronization from the connector's **Settings** page. ### Schedule delta sync Enable **Scheduled delta sync** to periodically synchronize only the content that has changed since the previous crawl. Configure the following: | **Field** | **Description** | | - | - | | **Scheduled delta sync** | Enables automatic incremental synchronization of changed Zendesk content. | | **Delta cadence** | Specifies how frequently Artemis checks Zendesk for updated content (for example, every 6 hours). | ### Schedule permission sync If **Permission-aware ACL** is enabled, you can independently schedule permission synchronization. | **Field** | **Description** | | - | - | | **Scheduled permission sync** | Enables periodic synchronization of Zendesk permissions without re-indexing content. | | **Permission sync frequency** | Specifies how frequently Artemis refreshes ticket permissions and Help Center user segment visibility from Zendesk. | Permission synchronization ensures that changes to user access, group memberships, ticket visibility, or Help Center audience restrictions are reflected in search results, even when the underlying content has not changed. Content synchronization and permission synchronization are configured independently. Delta sync updates indexed content, while permission sync refreshes only access-control information. # Create agents Source: https://koreai.mintlify.app/agent-platform/create-agents Create, configure, and deploy agents. Agents are the core AI units in Agent Platform that reason autonomously, call tools, and compose responses based on a goal and persona you define. You can optionally add a `FLOW` section with structured steps for deterministic control over parts of the conversation. This guide walks through creating an agent in Studio, writing its ABL definition, and an overview of the complete configuration workflow. ## Before you begin * You must be signed in to Studio with an account that belongs to the target workspace. * You need developer permissions on the project where you are creating the agent. * To use the AI Architect for guided setup, a workspace administrator must configure an LLM provider under **Admin** > **Arch**. ## Agent types Every agent reasons by default. You can optionally add a `FLOW` section for structured, deterministic step execution. | Feature | Agent (default) | Agent with FLOW section | | - | - | - | | Decision making | LLM decides next action. | Steps define explicit transitions. | | Best for | Open-ended conversations, complex reasoning. | Structured workflows, deterministic processes. | | Editor UI | Full-page configuration panel. | Visual flow canvas with step nodes. | | Step control | LLM determines when to use tools. | Each step explicitly defines actions and transitions. | Adding a `FLOW` section does not create a different agent type. The agent continues to use reasoning and tool orchestration, while structured flow steps provide deterministic control for specific workflow paths. ## Create an agent in Studio ### Open the agents page Navigate to your project and select **Agents** from the sidebar. Agents page The Agents page lists all agents in the project. Each entry shows the agent name, flow indicator, deployment status, and last-modified timestamp. ### Start agent creation Click **Create Agent** and fill in the following details: | Field | Required | Description | | - | - | - | | **Agent Name** | Yes | Must start with a letter and contain only letters, numbers, and underscores. Maximum 100 characters. | | **Execution Mode** | Yes | Reasoning (LLM-driven) by default or choose FLOW-based for structured step execution. | | **Description** | No | A brief summary of what the agent does. | Click **Create Agent** to open the agent editor. ## Use the ABL Editor After creating an agent, you can configure it in two ways: * **Visual Editor** - a form-based interface with dedicated sections for identity, tools, memory, flow, and more. * **ABL Editor** - a full code editor where you write or edit the agent's ABL definition directly. Both modes stay in sync. Changes made in the visual editor are reflected in the ABL Editor and vice versa. To switch to the full-screen ABL Editor, click **\< > ABL** icon in the agent's top navigation bar. ABL Editor The ABL Editor provides syntax highlighting, line numbers, and inline error markers. See the top navigation bar for available options. It also supports a slash-command interface. Typing / opens a contextual command menu with available capabilities (for example. tools, gather fields, handoff configurations). Selecting a command inserts the relevant snippet automatically into the editor. ### Save and Validate Click **Save** to save and validate your ABL definition. Studio parses the definition in real time and flags any syntax issues inline. The status bar at the bottom shows **No issues** when the definition is valid. Click **Compile** to run a full structural validation without saving - useful for checking the definition before committing changes. ### Outline Panel The Outline panel on the left side of the editor displays the agent's structure as a navigable tree. It reflects the sections and blocks defined in your ABL code - including `PERSONA`, `MEMORY`, `HANDOFFS`, and sub-agents or steps. ABL Editor outline panel Use the **Filter symbols** search box at the top of the Outline to jump to any section in a large definition. ### Diagnostics Panel The Diagnostics panel appears at the bottom of the editor. It lists all validation issues grouped by type. ABL Editor diagnostics panel | Tab | What It Shows | | - | - | | **Problems** | All issues across syntax, structural, and compile checks. | | **Syntax** | Malformed ABL syntax errors. | | **Structural** | Logical issues such as missing required fields or invalid references. | | **Compile** | Errors that prevent the definition from being compiled and deployed. | ### Insert Tool Signature Instead of typing tool signatures manually, use the **Insert Tool Signature** shortcut to search and insert a pre-built signature from any tool defined in the project. Insert Tool Signature dialog The dialog lists all available tools with their type (HTTP, Knowledge Base, and so on) and a description. Click **Insert** to add the tool's signature directly into the `TOOLS` block at your cursor position. ## Define identity and persona Identity and persona control how an agent communicates - its tone, expertise, and boundaries. Agent identity, goals, and persona ### Core identity fields Define the agent's identity in the `AGENT` block. ```yaml theme={null} AGENT: Policy_Advisor GOAL: | Answer customer policy questions using semantic search over policy documents. Clarify ambiguous queries before searching. PERSONA: | Knowledgeable and patient policy specialist. Cites source documents when answering. Never guesses. LIMITATIONS: - "Cannot modify or override existing policies" - "Cannot process refunds directly" ``` | Field | Purpose | | - | - | | `GOAL` | Defines what the agent accomplishes. | | `PERSONA` | Defines how the agent communicates - tone, style, and expertise. | | `LIMITATIONS` | Prompt-level boundaries the agent can use to refuse requests, explain caveats, or stay in scope. For hard enforcement, pair with `CONSTRAINTS`. | Keep `GOAL` and `PERSONA` concise and specific. Vague instructions lead to inconsistent behavior. ### Add an opening message Use `ON_START` to send a greeting before any user input. ```yaml theme={null} ON_START: RESPOND: | Hello! I am your booking specialist. I can help you view, modify, upgrade, or cancel your reservation. What would you like to do? ``` `ON_START` fires once per session initialization for that agent. You can also call tools in `ON_START` - for example, to check if the user is returning - and set session variables before greeting. ```yaml theme={null} ON_START: CALL: check_returning_user SET: session_initialized = true RESPOND: | Welcome back, {{user_name}}! How can I help you today? ``` `ON_START` only fires when the agent is the session entry point or receives a handoff. If the agent is not being initialized, `ON_START` will not run. Valid Directives inside `ON_START`: | Directive | What it does | | - | - | | `RESPOND:` | Sends an initial message to the user when the session starts. | | `FORMATS:` | Defines rich content for the initial response without a preceding `RESPOND:` block. | | `VOICE:` | Defines voice configuration for the initial response without a preceding `RESPOND:` block. [Learn More](/agent-platform/create-agents#voice-agents). | | `ACTIONS:` | Adds interactive buttons or forms to the initial message. | | `CALL:` | Invokes a tool when the session starts. | | `SET:` | Sets a variable using the syntax `SET: field = value`. | | `DELEGATE:` | Immediately delegate the session to another agent at startup. | | `MESSAGE_KEY:` | Specifies the internationalization key for the response message. | | `BRANCHES:` | Defines conditional branching logic. | ```yaml theme={null} MESSAGES: welcome_default: "Welcome! How can I help you today?" ON_START: SET: user_role = session.user.role BRANCHES: - IF: user_role == "admin" RESPOND: "Welcome, Admin! You can manage users, agents, and system settings." - IF: user_role == "manager" RESPOND: "Welcome! You can review reports and manage your team's work." - ELSE: MESSAGE_KEY: welcome_default ``` ### Supervisor agents For multi-agent projects, use the `SUPERVISOR` block to define the orchestrating agent. ```yaml theme={null} SUPERVISOR: Retail_Supervisor GOAL: "Route customers to the right specialist for product discovery, purchases, orders, returns, loyalty, or human support" PERSONA: | Professional and helpful retail assistant. Friendly, efficient, and knowledgeable about our product catalog. Recognizes returning customers and adapts to their shopping history. Routes requests to the right specialist quickly and transparently. LIMITATIONS: - "Cannot process payments or complete purchases directly" - "Cannot access customer financial information" - "Cannot override pricing, discount policies, or return windows" - "Must not share personal information about other customers" ``` ## Configure execution The execution block controls model selection, token limits, reasoning depth, and timeout behavior. Agent execution details ```yaml theme={null} EXECUTION: model: claude-sonnet-4-5-20250929 temperature: 0.3 max_tokens: 4096 max_reasoning_iterations: 15 tool_timeout: 10000 ``` | Field | Description | | - | - | | `model` | LLM model for this agent. | | `temperature` | Controls response variability. Lower values produce more deterministic output. | | `max_tokens` | Maximum tokens per response. | | `max_reasoning_iterations` | Caps the number of tool-call/response cycles before forcing a final response. | | `tool_timeout` | Milliseconds to wait for a tool call before timing out. | Always set `max_reasoning_iterations` to prevent runaway loops. Start with 10–15 and tune based on observed behavior. ## Add tools Tools let agents call external APIs, retrieve data, or trigger actions. Define tools in the `TOOLS` block with their signature and a description. Create the tool at the project level, then attach it to the agent via UI or DSL. ```yaml theme={null} TOOLS: search_policies(query: string, category: string) -> {results: object[], totalCount: number} description: "Search policy documents by query and category" get_policy_detail(policy_id: string) -> {title: string, content: string, effectiveDate: string} description: "Get full text of a specific policy" ``` Tool descriptions are injected into the LLM context. Clear, accurate descriptions directly influence which tool the agent selects. **Add Instructions for Tool Use** Use numbered `INSTRUCTIONS` when the agent must follow a specific sequence for using tools. ```yaml theme={null} INSTRUCTIONS: | 1. Identify the policy area from the user's question. 2. Search relevant documents with search_policies. 3. If results are ambiguous, ask the user to narrow down. 4. Present the answer with source attribution. ``` If the agent calls the wrong tool, improve the `description` field on each tool definition and add explicit numbered steps in `INSTRUCTIONS`. For full details on different types of tools, see [Tools guide](/agent-platform/tools-overview). ## Capabilities After defining identity, execution, and tools, configure how the agent collects and retains data. **Gather**: Define structured fields the agent should capture from users during a conversation. Use `GATHER` to prompt for required inputs with type validation and required flags. This is available as a dedicated section in both the flow steps and the agent editor. For details, see [Gather Data from Users](/agent-platform/gather-data). **Memory**: Configure session variables (available within a conversation), persistent state (stored across sessions), and remember/recall triggers that automatically store and inject context. ```yaml theme={null} MEMORY: session: - customer_id - recommended_products persistent: - user.risk_profile - user.preferred_products remember: - WHEN: risk_profile IS SET STORE: risk_profile -> user.risk_profile recall: - ON: session:start ACTION: inject_context PATHS: [user.risk_profile, user.preferred_products] ``` For full details, see the [Data Collection and Memory & State guides](/agent-platform/memory-and-state). ## Coordination Configure how the agent hands off work to other agents or to human operators. **Handoffs** - Rules that transfer the conversation to another agent when a condition is met. Handoffs pass context such as session variables to the receiving agent. **Delegates** - Configuration for sub-agents that this agent can invoke to complete subtasks without transferring the conversation. The delegating agent retains control and receives the result. **Escalation** - Triggers and routing logic for escalating to a human agent, including context passing and queue assignment. For full details, see the [Multi-Agent Orchestration](/agent-platform/orchestrate) guide. ## Lifecycle Define what happens at the start and end of each session, and how the agent handles errors. ### On start Actions that fire when a session begins: tool calls, variable initialization, and a greeting response. See [`ON_START` examples](#add-an-opening-message) earlier in this guide. | Field | Description | | - | - | | **Enable ON\_START** | Enables or disables startup actions when a conversation begins. | | **Initial Message** | Sends a welcome or introductory message at the start of the conversation. | | **Tool Call on Start** | Automatically invokes a tool when the conversation starts. | | **Delegate on Start** | Transfers the conversation to a selected delegate agent on startup. | | **Voice** | Configures voice settings for the startup interaction. | | **Rich Content** | Adds structured content such as quick replies, cards, images, lists, or other supported rich media to the startup message. | | **Interactive Actions** | Adds interactive controls such as buttons, dropdowns (Select), and input fields to collect user responses during onboarding. | | **Submit Label** | Defines the text displayed on the submit button for interactive actions. | | **Submit ID** | Specifies the unique identifier associated with the submit action. | **Interactive Actions Types** | Action | Description | | - | - | | **Button** | Displays a clickable button that submits a predefined value. | | **Select** | Displays a dropdown list from which the user can select an option. | | **Input** | Displays a text field for collecting free-form user input. | ### Error handling Recovery strategies for tool failures, timeout errors, and unexpected input. Configure fallback responses and retry logic to keep conversations from dead-ending. Error Handling Use the Error Handling page to define how the agent responds when an error occurs during execution. | Field | Description | | - | - | | **Handler Name** | Unique identifier for the error handler (for example, `default`). | | **Error Message** | Message displayed to the user when the error occurs. | | **Then** | Defines the action the agent takes after displaying the error message. | | Action | Description | | - | - | | **Continue** | Continues the conversation after displaying the error message. | | **Escalate** | Routes the conversation to a configured escalation target. | | **Handoff** | Transfers the conversation to another agent. | | **Complete** | Ends the conversation and marks it as completed. | | **Backtrack** | Returns to the previous step and continues execution from there. | | **Retry Step** | Retries the step that encountered the error. | Example: If a tool call fails, the agent can display "An error occurred. Please try again." and then either retry the step, continue the conversation, or route the user to another agent. ### Completion Conditions that determine when the agent's task is done and the session should close. Configure one or more completion conditions using expressions that evaluate the current conversation state, such as whether all required information has been collected. When a condition evaluates to true, the agent marks the interaction as complete and can optionally send a final response message to the user before ending the conversation. ```yaml theme={null} COMPLETE: - WHEN: policy_answer_provided == true RESPOND: "Is there anything else about our policies I can help with?" ``` #### Templates and messages The Templates & Messages page allows you to create reusable response templates that can be referenced throughout the agent and present consistent agent output formatting. Templates can include multiple content formats such as Default, Markdown, HTML, and Voice. ```yaml theme={null} ON_START: RESPOND: TEMPLATE(welcome) # template name = welcome. ``` * Templates can be referenced anywhere in the agent abl to present responses. * Templates support variable interpolation using placeholders. ## Add a FLOW section Add a `FLOW` section when you need deterministic, step-by-step control, for example, booking flows, identity verification, or data collection wizards. ### Define a flow ```yaml theme={null} AGENT: Order_Tracker GOAL: "Help customers look up and track their orders" PERSONA: | Efficient order specialist. Provides clear status updates. Empathetic when orders are delayed. TOOLS: lookup_order(order_id: string) -> {order_id: string, status: string, tracking_number: string, estimated_delivery: string} description: "Look up an order by ID" get_shipping_details(tracking_number: string) -> {carrier: string, status: string, location: string, history: object[]} description: "Get shipping and tracking information" FLOW: entry_point: ask_order_id steps: - ask_order_id - lookup - show_status - shipping_details ask_order_id: REASONING: false GATHER: - order_id: prompt: "What is your order number? (Format: ORD-XXXXX)" type: string required: true THEN: lookup lookup: REASONING: false CALL: lookup_order(order_id) ON_SUCCESS: SET: tracking_number = result.tracking_number THEN: show_status ON_FAIL: RESPOND: "I could not find that order. Please check the number and try again." THEN: ask_order_id show_status: REASONING: false RESPOND: | Order: {{order_id}} Status: {{status}} Estimated delivery: {{estimated_delivery}} Would you like shipping details? ON_INPUT: - IF: input.contains("yes") OR input.contains("shipping") OR input.contains("track") THEN: shipping_details - ELSE: THEN: COMPLETE shipping_details: REASONING: false CALL: get_shipping_details(tracking_number) ON_SUCCESS: RESPOND: | Carrier: {{carrier}} Current location: {{location}} Status: {{status}} THEN: COMPLETE ON_FAIL: RESPOND: "Shipping details are unavailable right now." THEN: COMPLETE COMPLETE: - WHEN: order_status_shown == true RESPOND: "Is there anything else I can help with?" ``` Steps with `REASONING: false` follow the defined path exactly. The `THEN` keyword defines the next step. `ON_INPUT` branches based on user responses. ### Key flow constructs **Entry point** - specifies which step starts the flow. If omitted, the first step in the `steps` list is used. ```yaml theme={null} FLOW: entry_point: welcome ``` **Step list shorthand** - declares step order. You still define each step separately. ```yaml theme={null} FLOW: welcome -> collect_info -> process -> confirm ``` **Tool call with branching:** ```yaml theme={null} process_payment: REASONING: false CALL: charge_card(card_number, amount) ON_SUCCESS: RESPOND: "Payment of ${{amount}} processed." THEN: confirmation ON_FAIL: RESPOND: "Payment failed. Try a different card?" THEN: collect_payment ``` **Conditional branching on input:** ```yaml theme={null} choose_action: REASONING: false ON_INPUT: - IF: input.contains("track") THEN: tracking_flow - IF: input.contains("cancel") THEN: cancel_flow - IF: input.contains("return") THEN: return_flow - ELSE: RESPOND: "Please choose: track, cancel, or return." THEN: choose_action ``` **SET assignments:** ```yaml theme={null} calculate_total: REASONING: false SET: nights = checkout_date - checkin_date SET: total = room_price * nights RESPOND: "Your total for {{nights}} nights is ${{total}}." THEN: confirm_booking ``` **MAX\_ATTEMPTS** - limits retries on a step before routing to a fallback. ```yaml theme={null} verify_identity: REASONING: false MAX_ATTEMPTS: 3 ON_EXHAUSTED: escalate_to_human GATHER: - ssn_last_four: prompt: "Last 4 digits of your SSN?" type: string required: true CALL: verify_identity(ssn_last_four) ON_SUCCESS: THEN: authenticated ON_FAIL: RESPOND: "That did not match. Please try again." THEN: verify_identity ``` ### Mix reasoning and deterministic steps Within a single flow, combine deterministic steps (`REASONING: false`) with reasoning steps (`REASONING: true`). Use deterministic control for data collection and confirmations; use LLM autonomy for open-ended research or analysis. ```yaml theme={null} FLOW: steps: - collect_preferences - research_options - present_plan - confirm collect_preferences: REASONING: false GATHER: - destination: required prompt: "Where would you like to go?" - travel_dates: required type: date prompt: "What are your travel dates?" - budget: required type: string prompt: "What is your budget range?" THEN: research_options research_options: REASONING: true GOAL: | Research travel options for {{destination}} on {{travel_dates}} within a {{budget}} budget. Search flights, hotels, and check weather. Compile a recommended itinerary with options at different price points. AVAILABLE_TOOLS: [search_flights, search_hotels, get_weather] EXIT_WHEN: itinerary_compiled == true MAX_TURNS: 8 THEN: present_plan present_plan: REASONING: false RESPOND: | Here is your travel plan for {{destination}}: {{compiled_itinerary}} Would you like to proceed with booking, or adjust anything? ON_INPUT: - IF: input.contains("book") OR input.contains("yes") THEN: confirm - IF: input.contains("change") OR input.contains("adjust") THEN: collect_preferences - ELSE: THEN: present_plan confirm: REASONING: false RESPOND: "Booking confirmed. You will receive a confirmation email." THEN: COMPLETE ``` Key properties for reasoning steps: | Property | Purpose | | - | - | | `REASONING: true` | Enables LLM autonomy for this step. | | `GOAL` | Step-specific goal, overrides the agent-level goal. | | `AVAILABLE_TOOLS` | Subset of agent tools available within this step. | | `EXIT_WHEN` | Condition that ends the reasoning loop. | | `MAX_TURNS` | Maximum reasoning cycles before a forced exit. Default is 10. | | `STEP_CONSTRAINTS` | Additional constraints scoped to this reasoning zone. | ## NLU intent classification Use the `NLU` block to define intents, entities, and categories that classify user messages and drive appropriate actions. NLU configuration supports: * Intent classification using keyword patterns and example utterances. * Entity extraction with typed extractors (enum, pattern, location, date, number, free text). * Synonyms for normalizing variant expressions to canonical values. * Embeddings-based matching for semantic similarity when keyword patterns are insufficient. * [Multi-intent handling](/agent-platform/abl/reference/nlu#multi-intent-handling-multi_intent) for detecting and processing multiple intents from a single user message. * [Lookup tables](/agent-platform/abl/reference/data-types-and-utilities#lookup-tables) for validating user input, enabling fuzzy matching, and suggesting corrections based on predefined reference values. * Multi-language support with per-language model configuration. * A glossary for domain-specific terminology. For more details, see [NLU configuration](/agent-platform/abl-reference/nlu). ## Behavior Control how the agent enforces rules, filters content, and adapts its behavior based on context. **Constraints** - Rules and restrictions enforced at runtime. Unlike `LIMITATIONS`, constraints with `REQUIRE` conditions and `ON_FAIL` actions are hard enforcement - the agent cannot proceed without meeting the condition. **Guardrails** - Content safety policies applied to agent inputs and outputs. Guardrails can block, redact, or flag content using built-in checks or external moderation providers. ```yaml theme={null} GUARDRAILS: - NAME: pii_filter KIND: output CHECK: "output NOT CONTAINS ssn_pattern AND output NOT CONTAINS credit_card_pattern" ACTION: redact MESSAGE: "Personal information has been removed from the response." - NAME: content_safety KIND: input PROVIDER: openai_moderation CATEGORY: hate THRESHOLD: 0.7 ACTION: block MESSAGE: "That request contains content I cannot help with." ``` **Behavior profiles** - Conditional behavior configurations that activate based on context, such as user role, session flags, or channel. For full details, see the Constraints & Guardrails and Behavior Profiles guides. ## Multi-language support Build agents that detect the user's language and respond in kind. ### Set up language detection ```yaml theme={null} NLU: languages: ["en", "es", "fr", "de", "pt"] defaultLanguage: "en" allowCodeSwitching: true ``` | Field | Description | | - | - | | `languages` | Supported language codes (ISO 639-1). | | `defaultLanguage` | Fallback when detection is ambiguous. | | `allowCodeSwitching` | Whether the agent follows the user when they switch languages mid-conversation. | ### Route by language ```yaml theme={null} HANDOFF: - TO: Spanish_Support WHEN: detected_language == "es" CONTEXT: pass: [customer_id, query] - TO: English_Support WHEN: detected_language == "en" CONTEXT: pass: [customer_id, query] ``` ### Provide language-specific models Route to different LLM models based on detected language. ```yaml theme={null} NLU: languages: ["en", "es", "ja"] defaultLanguage: "en" languageModels: en: "claude-sonnet-4-5-20250929" es: "claude-sonnet-4-5-20250929" ja: "gpt-4o" ``` ### Add a glossary Inject domain-specific terms to ensure the agent uses correct terminology regardless of language. ```yaml theme={null} NLU: languages: ["en", "es"] defaultLanguage: "en" glossary: - "Agent Platform" - "SearchAI" - "eval set" - "guardrail policy" ``` ### Set language per environment variable For deployments that serve a single market: ```yaml theme={null} NLU: defaultLanguage: "{{env.DEFAULT_LANGUAGE}}" ``` Configure `DEFAULT_LANGUAGE=es` for your Spanish market deployment and `DEFAULT_LANGUAGE=en` for English. ### Write multi-language templates ```yaml theme={null} TEMPLATES: welcome: DEFAULT: "Welcome! How can I help you today?" MARKDOWN: | # Welcome! How can I help you today? bienvenida: DEFAULT: "Bienvenido! Como puedo ayudarte hoy?" ``` ## Customize agent response Agents can respond with more than plain text. Every RESPOND statement supports dynamic variables, reusable templates, rich content, interactive actions, and channel-specific formatting. The platform automatically renders the appropriate response for the active channel. ### RESPOND Use `RESPOND` to return inline text or multi-line messages. Values from variables, memory, and tool outputs can be referenced using `{{ }}` interpolation. **Single response message** ```yaml theme={null} RESPOND: "Hello {{customer_name}}, welcome back!" ``` For **multi-line** content, use pipe symbol. ```yaml theme={null} RESPOND: | Here is your transfer summary: From: {{source_account}} To: {{beneficiary_name}} Amount: {{amount}} {{currency}} ``` For reusable or channel-specific responses, reference a **template** instead. ```yaml theme={null} RESPOND: TEMPLATE(welcome_message) ``` **Multiple Responses in a single agent turn** An agent can send multiple responses during a single user turn. Each response is emitted as an independent message and streamed to the client in the order it is generated. A single turn can include any combination of: * Plain text messages * Template messages * Multiple template messages * A mix of template and text messages Each message is delivered separately and is not merged with subsequent responses. Template messages continue to use the existing client rendering logic. **Example** The following example sends a template message, followed by a plain-text message, and then another template message within the same turn. ```yaml theme={null} TEMPLATES: first_template: DEFAULT: "First template fallback" RENDERABLES: - NAME: com.example.first_template.v1 PAYLOAD_JSON: title: "First template" type: "custom_card" body: heading: "Account summary" status: "Active" TARGETS: [api, sdk_websocket] FALLBACK_TEXT: "First template fallback" second_template: DEFAULT: "Second template fallback" RENDERABLES: - NAME: com.example.second_template.v1 PAYLOAD_JSON: title: "Second template" type: "custom_card" body: heading: "Next actions" actions: - label: "View details" value: "view_details" TARGETS: [api, sdk_websocket] FALLBACK_TEXT: "Second template fallback" FLOW: entry_point: first steps: - first - middle - last first: REASONING: false RESPOND: TEMPLATE(first_template) THEN: middle middle: REASONING: false RESPOND: "This is the plain-text message between the two templates." THEN: last last: REASONING: false RESPOND: TEMPLATE(second_template) THEN: COMPLETE ``` For the preceding flow, the client receives the following messages in order: ```text theme={null} 1. **Template message** (`first_template`) 2. **Plain-text message** 3. **Template message** (`second_template`) ``` ### Templates Use the `TEMPLATES` block to define named, reusable responses that can be referenced throughout the agent using `TEMPLATE(name)`. Templates help eliminate duplication, maintain consistent messaging, and simplify updates by defining a response once and reusing it across multiple flows. Templates support: * Variable interpolation using `{{ }}` to insert dynamic values. * Conditional blocks and loops for rendering dynamic content. * Multiple channel-specific variants, allowing the same response to be rendered differently on web, voice, Slack, WhatsApp, Teams, and other supported channels. * Interactive actions for building rich, interactive experiences. The platform automatically selects the appropriate template variant for the active channel. `DEFAULT` is required and serves as the fallback when no channel-specific variant is available. | Variant | Purpose | | - | - | | `DEFAULT` | Plain text fallback (required) | | `MARKDOWN` | Rich Markdown formatting | | `HTML` | HTML content | | `VOICE_INSTRUCTIONS` | Text-to-speech guidance | | `ADAPTIVE_CARD` | Microsoft Teams/Web cards | | `SLACK` | Slack Block Kit | | `WHATSAPP` | WhatsApp message format | | `AG_UI` | AG-UI event format | Templates can be referenced from `RESPOND`, `ON_START`, and `COMPLETE` blocks using `TEMPLATE(name)`. [Refer to this guide for details and examples](/agent-platform/abl-reference/rich-content#templates). ### Interactive responses Add interactive elements to any response using `ACTIONS`. Supported actions include: * Buttons for predefined choices. * Select menus for choosing from a list. * Input fields for collecting user input. * Forms for grouping multiple inputs with a submit action. [Learn More](/agent-platform/abl-reference/rich-content#interactive-actions). ### Carousels Carousels display a horizontal scrollable collection of cards, each with a title, subtitle, image, and action buttons. Use carousels when presenting multiple options for the user to browse and select from, such as hotel results, product listings, flight options, and similar multi-item responses. Define carousels within the `RICH_CONTENT` block using the `CAROUSEL` key. ```yaml theme={null} RICH_CONTENT: CAROUSEL: - title: "Economy Class" subtitle: "$289 - UA 891" imageUrl: "https://cdn.example.com/economy.jpg" defaultActionUrl: "https://booking.example.com/ua891" buttons: - id: select_economy type: button label: "Select" value: "ua891_economy" - id: learn_more type: button label: "Learn More" url: "www.example.com" - title: "Business Class" subtitle: "$1,249 - UA 891" imageUrl: "https://cdn.example.com/business.jpg" buttons: - id: select_business type: button label: "Select" value: "ua891_business" ``` [For the complete list of carousel card properties and additional examples, see Carousel Rich Content](/agent-platform/abl-reference/rich-content#carousels) ## Customize system messages Use `MESSAGES` to customize system-generated responses to match the agent's voice. ```yaml theme={null} MESSAGES: error_default: "I am sorry, something went wrong. Let me try again or connect you with a team member." constraint_blocked: "I am unable to proceed due to a policy restriction." escalation_format: "Let me connect you with a team member who can help further." conversation_complete: "Thank you for contacting us. Have a great day!" gather_prompt: "I need a bit more information to help you." ``` ## Test your agent Studio provides an integrated chat interface for testing agents in real time. 1. Open the agent from the Agents list. 2. Switch to the **Chat** tab. 3. Type messages in the input field to interact with the agent. The chat interface sends messages to the runtime and displays the agent's responses in real time. ### Debug Panel The chat view includes a split-pane debug panel. | Panel item | What it shows | | - | - | | **Trace events** | Every action the agent takes: LLM calls, tool invocations, state changes. | | **Timing information** | Latency for each operation. | | **Variable state** | Current values of session variables. | Use trace events to understand why an agent made a particular decision. They show the full chain of reasoning, tool calls, and responses. ### Session management Each test conversation creates a session. You can: * Start a new session to begin a fresh conversation. * View session history from the **Sessions** page. * Copy the session ID for reference or debugging. ## Voice Agents Agents you build can also be deployed to voice channels for real-time phone or SIP conversations. Add a telephony provider, a Voice Gateway, and a speech-to-speech or STT/TTS pipeline to the same agent runtime to enable voice interactions. See [Enabling Voice Agents](/agent-platform/enable-voice) for setup steps, including voice architecture selection and channel configuration. ## Use The AI Architect AI Architect is a context-aware AI assistant built into Studio. Access it from the **Architect icon** in the Studio header bar. The panel adapts to your current context. When editing a specific section (for example, Identity or Tools), the Architect shows section-specific suggestions and proposes code changes as a diff you can accept, reject, or refine. AI Architect requires an LLM provider configured at the workspace level under **Admin** > **Arch**. If no provider is configured, a warning banner appears in the panel. ## Troubleshooting | Symptom | Resolution | | - | - | | **Agent loops endlessly** | Set `max_reasoning_iterations` in the `EXECUTION` block to cap tool-call/response cycles. | | **Agent calls the wrong tool** | Improve `INSTRUCTIONS` with numbered steps and add clear `description` fields to each tool definition. | | **Agent responds when it should use a tool** | Strengthen the `GOAL` to explicitly state when tool use is required (for example, "Always search before answering"). | | **Agent ignores limitations** | `LIMITATIONS` are prompt-level boundaries - the LLM may not always enforce them. For hard enforcement, use `CONSTRAINTS` with `REQUIRE` conditions and `ON_FAIL` actions. | | **Agent tone drifts in long conversations** | Keep the persona description concise and specific. Use `MESSAGES` to control system-generated text. | | **Flow skips a step** | Verify every step has a `THEN` pointing to the correct next step. A missing `THEN` ends the flow early. | | **User input not matched in `ON_INPUT`** | Conditions are evaluated top-to-bottom. Place more specific conditions before general ones, and always include an `ELSE` branch. | | **Reasoning step runs too long** | Set `MAX_TURNS` lower: 3–5 for focused tasks, 8–10 for research tasks. | | **Reasoning step produces inconsistent results** | Add `STEP_CONSTRAINTS` to narrow the reasoning scope and use a specific step-level `GOAL`. | | **Low NLU classification accuracy** | Add 10 or more examples per intent and enable embeddings for semantic matching. | | **Intent confusion between similar intents** | Make patterns more distinct. Increase the confidence threshold. | | **Agent responding in the wrong language** | Verify `allowCodeSwitching` is enabled. Add explicit instructions in `PERSONA` to respond in the user's language. | *** **Related articles:** * [Studio Overview](/agent-platform/projects#studio) - navigation, project management, and workspace layout. * [Tools, Knowledge & Connections](/agent-platform/tools-overview) - managing tools and resources used by agents. * [Testing, Deployment & Operations](/agent-platform/evals) - evaluations and deployment. * [Multi-Agent Orchestration](/agent-platform/orchestrate) - coordinating multiple agents. * [Safety & Guardrails](/agent-platform/safety-and-guardrails) - content safety and constraints. * [Memory & State](/agent-platform/memory-and-state) - session and persistent memory. * [FLOW reference](/agent-platform/abl-reference/flow) - complete FLOW syntax reference. * [Tools reference](/agent-platform/abl-reference/tools) - tool definition syntax. * [Enabling Voice Interactions](/agent-platform/enable-voice) - voice related configurations. # Deploy agents Source: https://koreai.mintlify.app/agent-platform/deployment A deployment makes an Agentic project live so users can reach your agents. The platform organizes every part of going live under four tabs in the Deployments workspace: **Environments**, **Channels**, **Endpoints**, and **API Keys**. This page covers each tab in the order you use it. Before any user reaches an agent, three things must exist together. You need an active deployment in the target environment, environment variables that resolve every reference your ABL uses, and a channel bound to that environment with an API key that authorizes access. The following terms recur throughout this guide. | Term | Definition | | - | - | | **Version** | A frozen snapshot of a compiled agent. | | **Deployment** | A manifest that maps agents and workflows to versions for one environment. | | **Environment** | One of the deployment slots: development, staging, or production. | | **Channel** | A user-facing surface that connects a deployment to end users. | | **SDK key** | A public, client-side credential that SDK applications use, prefixed `pk_`. | | **Platform key** | A scoped, server-to-server credential for backend integrations, prefixed `abl_`. | ## Environments The Environments tab is where you create deployments, manage the variables they depend on, and move a release from development to production. It lists a shared Base variable set, one card for each environment, and a history log of retired deployments. The tab header shows how many deployments are currently active and provides the **New Deploy** button. ### Deployment lifecycle A deployment always scopes to a single environment, and only one deployment stays active per environment at a time. When you activate a new deployment, the platform retires the previous one automatically. In-progress sessions finish on the old version while new sessions route to the latest deployment. The lifecycle follows a fixed path. You build agent versions, configure variables, deploy to development, promote to staging, promote to production, then connect channels so users can reach the agents. ### Environment cards Each environment card represents one deployment slot. When a deployment is active, the card shows its ID, status, label, the number of agents and workflows it contains, and the pinned component versions. The three environments follow a fixed promotion order. | Environment | Description | | - | - | | **Development** | The first slot in the promotion chain, where you validate a new build. | | **Staging** | The middle slot, where you verify the release against production-like settings. | | **Production** | The final slot, which serves live users. | Each card provides **Snapshot**, **Promote**, **Rollback**, and **Retire** controls. **Snapshot** captures the current state, **Promote** pushes the manifest to the next environment, **Rollback** restores the previous deployment, and **Retire** decommissions the deployment. Production omits **Promote** because it's the final stage in the chain. ### Environment variables Configure variables before you deploy, so that every reference resolves at runtime. The Base set defines fallback values shared across all environments, and any environment can override a Base value with its own. The panel also lets you export, import, and copy variables between environments. To add an environment variable: 1. Open **Base (Default)**, **Development**, **Staging**, or **Production**. 2. Click **Add**, then enter the key and value. 3. Select **Secret** when the value is sensitive. The platform stores secrets write-only, so no one can read them back after you save. 4. Click the confirm icon to save the variable. Variable changes affect new sessions only. In-progress sessions keep their original values until they end, and runtime execution fails when an agent reads an unresolved variable. You can also set global variables under **Admin Settings -> Env variables**. When keys collide, the environment-specific variable takes priority. ### Create a deployment Click **New Deploy** to open the **Create Deployment** dialog. You choose the target environment, name the release, decide how the platform selects agent versions, and confirm the agents, workflows, and variables the deployment includes. Complete the fields in order. | Field | Description | | - | - | | **Environment** | The deployment slot you target. Start every release in development. | | **Label** | A short, readable name for the release. | | **Description** | Optional notes about the deployment. | | **Version Strategy** | How the platform selects each agent's version. | | **Entry Agent** | The agent that receives the first message in a session. | | **Workflows** | The workflow versions the deployment includes. | | **Config Variables** | Optional. The config variable set version to bind. | | **Environment Variables** | Optional. Deployment-scoped variable overrides. | ### Version strategy The version strategy controls which build of each agent the deployment uses. Choose the approach that matches how much control you need over individual agents. | Option | Description | | - | - | | **Use latest active** | Deploys the best available version for every agent, the fastest path to production. | | **Customize per agent** | Pins a specific version for each agent and lets you configure model overrides individually. | For workflows, pin each one to a published version or select **Auto-create from draft** to generate a version from the current working copy. A running summary confirms the payload before you deploy. When you click **Deploy**, the platform validates your ABLs, resolves configuration placeholders, activates the deployment, and retires any previous deployment in that environment. ### Promote through environments Promotion moves the exact manifest you tested into the next environment, so you never rebuild a release by hand. This keeps development, staging, and production in sync and reduces configuration drift. To promote a deployment: 1. On the **Development** card, click **Promote** to push the manifest to staging. 2. Confirm the deployment behaves as expected in staging. 3. On the **Staging** card, click **Promote** to push the same manifest to production. ### Roll back a deployment A rollback reverses an active deployment when it causes problems. The platform retires the current deployment and reactivates the previous one, so users recover quickly. To roll back a deployment: 1. Locate the environment running the problem deployment. 2. Click **Rollback**. Sessions on the failed deployment finish naturally, and new sessions reach the restored version immediately. ### Deployment history The **Deployment History** log records every retired deployment for audit and recovery. Each entry lists the deployment ID, source environment, retired status, date, and component versions, along with a **Snapshot** link. ### Deployment status Every deployment moves through a small set of states as it goes live and later steps down. The status tells you whether a deployment serves new sessions, is winding down, or is fully decommissioned. | Status | Description | | - | - | | **active** | Serves live requests and receives new sessions. | | **draining** | Accepts no new sessions while existing sessions finish. | | **retired** | Fully decommissioned, though active sessions continue until they end. | ## Channels Channels connect your agents to the places your users already are—messaging apps, voice systems, or your own web and mobile apps. Open the Channels tab, pick a channel card, and configure it. Each channel can either follow an environment automatically or stay locked to a specific deployment version. This means you can publish the same agent across many surfaces without rebuilding it each time. For the full list of supported channels and setup steps, see [Channels](/agent-platform/channels). ## Endpoints The Endpoints tab gives you the direct call surface for each agent in a chosen deployment. You select a deployment, then copy the ready-made request for any agent it contains. Each agent exposes three request modes: **Chat**, **Stream**, and **WebSocket (SDK)**. Open **Select deployment** and choose the deployment you want, such as the active staging, production, or dev deployment. The tab then lists each agent with its Chat URL and the three request modes. A Chat URL follows this structure. The **Chat** tab supplies a copy-ready request. ```bash theme={null} curl -X POST 'https://example.com/api/v1/project/smartdesk-enterprise-assistant/staging/agent/emailassistant/chat' \ -H 'x-api-key: ' \ -H 'Content-Type: application/json' \ -d '{"message": "Hello", "sessionMetadata": {}}' ``` The **Stream** and **WebSocket (SDK)** tabs use the same base URL with a different request format. Capture and confirm those request bodies before publishing. ## API keys The API Keys tab creates and manages the credentials external applications use to reach your agents. It separates keys into two types for two audiences, each on its own sub-tab. The type you create determines the format of the key and the permissions it can carry. | Key type | Description | | - | - | | **SDK key** | A public, client-side credential that SDK applications use to start a session. Requests stay constrained by the session token. Prefixed `pk_`. | | **Platform key** | A scoped credential for server-to-server platform API access, used by integrations, workflows, and backend automation. Prefixed `abl_`. | ### SDK keys The SDK Keys sub-tab lists every SDK key by name and masked value. Each row includes a delete control, and rows show a last-used date once the key has been called. Click **Create Key** to add a new one. ### Platform keys The Platform Keys sub-tab lists every platform key with fuller metadata and edit and delete controls. Each key also carries a status, such as revoked, and a **View scopes** expander that reveals the permissions it grants. | Field | Description | | - | - | | **Masked key** | The truncated key value shown after creation. | | **Created by** | The user who created the key. | | **Created at** | The date and time the key was created. | | **Lifecycle** | The key's current state and the time it last changed. | | **Scopes** | The permissions granted to the key. | ### Create a platform key A platform key carries explicit scopes, so you grant only the permissions the integration needs. You name the key, select its scopes, and set an expiration before you create it. To create a platform key: 1. Click **Create Key**, then enter a key name. 2. Select one or more scopes from the groups below. 3. Choose an expiration: **No expiration**, **30 days**, **90 days**, or a **Custom date**. 4. Click **Create Key**. ### Scopes Scopes define exactly what a platform key can do. The platform groups them by function, so you can grant read access, write access, or execution rights independently. | Group | Scope | Description | | - | - | - | | **Execution** | Execute Workflows | Execute workflows through the Process API. | | | Invoke Draft Workflows | Invoke draft, unpublished workflow versions, intended for CI/CD pipelines and automated testing. | | | Read Workflows | Read workflow definitions and status. | | | Execute Chat | Send messages to agents through the Chat API. | | **Management** | Read Agents | List and inspect agent configurations. | | | Write Agents | Create and update agent configurations. | | | Read Channel Connections | Read project channel connection details for service integrations. | | | Write Channel Connections | Create, update, and deactivate or delete project channel connections for service integrations. | | | Read Deployments | List deployment status and history. | | | Write Deployments | Create and promote deployments. | | | Read Sessions | Read session history and transcripts. | | | Write Sessions | Attach external provider metadata to sessions and trigger transcript sync. | | | Close Sessions | Close sessions with an explicit disposition. | | **Knowledge base** | Query Knowledge Base | Execute search queries against knowledge bases. | | | Read Knowledge Base | Read knowledge base metadata and configurations. | | | Ingest Documents | Upload files and ingest data into knowledge bases. | | | Write Document Permissions | Set document-level ACL permissions during ingestion. | | **Analytics** | Read Analytics | Read analytics dashboards and metrics. | | **Admin** | Read Workspace | Read workspace settings and usage data. | # DTMF support in Artemis voice agents Source: https://koreai.mintlify.app/agent-platform/dtmf-support Dual-Tone Multi-Frequency (DTMF) enables callers and voice agents to communicate using telephone keypad tones during an active voice call. Artemis provides built-in support for both inbound DTMF (caller-to-agent) and outbound DTMF (agent-to-external IVR), allowing developers to build complete IVR experiences without implementing custom telephony logic. It also supports **secure DTMF collection** for sensitive workflows such as payment processing. ## DTMF architecture DTMF is supported for pipeline voice agents through the Kore Voice Gateway. Currently, it is not available for realtime voice agents. The platform exposes DTMF functionality through three built-in system tools: | Tool | Direction | Purpose | | - | - | - | | `ivr_menu` | Inbound | Present a menu and collect a single keypad selection. | | `ivr_digit_input` | Inbound | Collect multi-digit keypad input such as account numbers or PINs. | | `send_dtmf` | Outbound | Send keypad tones into an active call to control an external IVR. | These tools are implemented by the platform runtime and require no HTTP endpoint or custom tool implementation. ## How DTMF works When a caller presses a key during a voice call, the tone is detected by the voice gateway and delivered directly to the Artemis runtime. The process follows these steps: ```text theme={null} Caller presses a key │ ▼ KoreVG detects DTMF │ ▼ KoreVG sends an event containing the digit data (data.digits) to the runtime │ ▼ Runtime processes the event │ ▼ Digit string delivered to the agent | ▼ Redacted status recorded in traces / eval artifacts / Studio inspection ``` Unlike speech recognition, DTMF input does not use Automatic Speech Recognition (ASR). Every valid keypress is considered deterministic and is assigned a confidence score of 1.0. ## DTMF capabilities ### Enable DTMF capabilities As discussed above, the DTMF feature is built-in and can be implemented by the system tools. To use these tools within your agent, add the tools definition to the Tools section of the ABL file. ## DTMF Tools ### Presenting an IVR Menu Use `ivr_menu` when callers need to select from a numbered menu. The tool plays a prompt, waits for a single keypad press, and maps the selected digit to a workflow step or intent. **Tool Definition** ```yaml theme={null} TOOLS: ivr_menu( prompt: string, dtmfMappings: object[], noInputConfig: object, noMatchConfig: object, bargeIn: boolean = false, language: string = "en" ) -> {digit: string, matched: boolean, mappedIntent: string, branch: string} description: "Present a DTMF menu to the voice caller. The caller presses a digit to select an option. Use for voice IVR flows with numbered choices." ``` **Input Parameters** | Parameter | Type | Required | Description | | - | - | - | - | | `prompt` | string | Yes | TTS message played to the caller | | `dtmfMappings` | array | Yes | 1–12 mappings of `{key, nextStep, intent?}`. Keys: 0–9, \*, # | | `noInputConfig` | object | Yes | `{timeout: 1-120s, maxRetries: 0-10, message: string}` | | `noMatchConfig` | object | Yes | `{maxRetries: 0-10, message: string}` | | `bargeIn` | boolean | No | Allow caller to press key while prompt plays. Default: false | | `language` | string | No | Language code for TTS playback (for example, `en`, `es`) | **Example** ```yaml expandable=true theme={null} CALL: ivr_menu WITH: prompt: "Press 1 for Billing, 2 for Support, or 0 for an agent." dtmfMappings: - key: "1" nextStep: billing intent: billing - key: "2" nextStep: support intent: support - key: "0" nextStep: escalate intent: agent ``` ### Collect multi-digit input Use `ivr_digit_input` when callers must enter a sequence of digits. The tool continues collecting digits until one of the following occurs: * Maximum digits reached * Ending key pressed * Inter-digit timeout expires **Tool definition** ```yaml expandable=true theme={null} ivr_digit_input( prompt: string, maxDigits?: number, endingKeyPress?: string, interDigitTimeout?: number, noInputConfig: object, noMatchConfig: object, language?: string ) -> object description: "Collect multiple DTMF digits from the caller." ``` **Input Parameters** | Field | Type | Required | Description | | - | - | - | - | | `prompt` | string | Yes | TTS message played to the caller asking them to enter digits. | | `maxDigits` | number (integer) | No | Maximum digits to collect before finalizing. Default: 10. If `endingKeyPress` is also set, internally increments by 1 to account for the terminator key. | | `endingKeyPress` | string | No | Key the caller presses to signal they're done (for example, #). Collection stops early when this key is pressed. | | `interDigitTimeout` | number (integer, milliseconds) | No | How long to wait after each keypress before assuming input is complete. Default: 2000 ms. | | `noInputConfig` | object | Yes | What to do when the caller presses nothing. | | `noMatchConfig` | object | Yes | What to do when the input doesn't satisfy conditions. | | `language` | string | No | BCP 47 language code for TTS rendering (for example, "en" or "es"). | **noInputConfig Sub-fields** | Field | Type | Required | Description | | - | - | - | - | | `timeout` | number (integer, seconds) | Yes | Seconds to wait for the first digit before triggering no-input. | | `maxRetries` | number (integer) | Yes | How many times to re-play the prompt on silence. | | `message` | string | Yes | TTS message played when the caller doesn't press anything. | **noMatchConfig Sub-fields** | Field | Type | Required | Description | | - | - | - | - | | `maxRetries` | number (integer) | Yes | How many times to re-play the prompt when input is invalid. | | `message` | string | Yes | TTS message played when input doesn't match expectations. | **Example** ```yaml expandable=true theme={null} CALL: ivr_digit_input WITH: prompt: "Please enter your 6-digit PIN followed by the pound key." maxDigits: 6 endingKeyPress: "#" interDigitTimeout: 3000 noInputConfig: timeout: 15 maxRetries: 2 message: "I didn't receive any input. Please enter your PIN." noMatchConfig: maxRetries: 1 message: "Entry incomplete. Please enter your full PIN." language: "en" ``` ### Send DTMF to external IVRs Use `send_dtmf` when Artemis needs to navigate another IVR after a call has been established. Typical examples include: * Selecting a department * Navigating hospital booking systems * Banking IVRs * Telecom IVRs * Legacy call routing systems Unlike the inbound tools, `send_dtmf` transmits keypad tones rather than collecting them. **How it works** During an active voice call, the agent can decide to send DTMF digits, for example after recognizing it has reached an IVR menu: ```text theme={null} Agent reaches IVR menu ↓ Agent sends DTMF digits ↓ Platform validates digits and call state ↓ Digits dispatched in order to the voice provider ↓ Redacted status recorded in traces / eval artifacts / Studio inspection ``` Digits are dispatched one at a time, in order, with a configurable pause between digits. Raw digits are never written to logs, traces, eval artifacts, or Studio evidence — only a digit count and a status are recorded. **Tool Definition** ```yaml theme={null} TOOLS: send_dtmf( digits: string, durationMs?: number, interDigitDelayMs?: number, reason?: string ) -> object ``` Once declared, `send_dtmf` behaves like any other tool available to the LLM: during an active call, the model decides when to call it and what `digits` to pass, based on the conversation so far and your agent's `GOAL`, `PERSONA`, and `INSTRUCTIONS`. For example, an `INSTRUCTIONS` line such as "When the IVR menu asks you to select a department, call `send_dtmf` with the digit for that option" steers the model without hardcoding the digits at author time. **Example** ```yaml theme={null} CALL: send_dtmf WITH: digits: "{{booking_code}}" durationMs: 300 interDigitDelayMs: 500 reason: "Selecting doctor queue" ``` The runtime validates the request before dispatching digits sequentially to the voice provider. **Input Parameters** | Field | Required | Notes | | - | - | - | | `digits` | Yes | Only 0-9, \*, and #. 1–64 characters. | | `durationMs` | No | Tone duration per digit, in milliseconds. Range: 50-5000. Default: 250. | | `interDigitDelayMs` | No | Delay between digits, in milliseconds. Range: 50-5000. Default: 300. | | `reason` | No | Up to 256 characters. Do not include PINs, account numbers, or the | Pauses inside a call flow (for example, waiting for an IVR menu to finish playing before sending digits) are handled as separate wait steps, not by embedding non-DTMF characters in \`digits. **Call-State Behavior** | Call State | Result | | - | - | | Active, connected call | Digits dispatch immediately | | Active playback/agent speaking | Digits dispatch immediately, without queuing behind playback | | Inactive, closed, on hold without media, dialing, or already transferred | Fails closed before dispatch | | Channel/provider without outbound DTMF support (VXML IVR, Genesys Audio Connector, AudioCodes) | Fails closed before dispatch | | Invalid digits or out-of-range timing | Fails closed before dispatch | **Status Codes** | Code | Meaning | | - | - | | `VOICE_DTMF_DISPATCHED` | Digits were sent to the provider in order. | | `VOICE_DTMF_INVALID` | `digits` failed validation (empty, too long, or disallowed characters), or `durationMs`/`interDigitDelayMs` was out of range. | | `VOICE_DTMF_UNSUPPORTED` | The active channel/provider does not support outbound DTMF. | | `VOICE_DTMF_INACTIVE` | The call was not in a state that allows dispatch (see table above). | | `VOICE_DTMF_PROVIDER_ERROR` | The platform could not deliver a digit command to the provider. | A successful `VOICE_DTMF_DISPATCHED` result means the platform sent the tones — it does not confirm that the far-end IVR accepted or acted on them. There is currently no delivery acknowledgment or automatic retry. ## Privacy and evidence DTMF digits can carry sensitive information such as PINs or account numbers. Runtime traces, eval run artifacts, and Studio inspection show only a redacted digit count and status — never the raw digits sent during a call. ## Secure DTMF input Use Secure DTMF Input when collecting sensitive information, such as payment card numbers, PINs, or other confidential data that must not be processed through speech recognition. During secure input collection, platform temporarily disables voice input and accepts only DTMF key presses. Spoken audio is not forwarded to the Automatic Speech Recognition (ASR) service, ensuring that sensitive information is not transcribed or processed by language models. After the secure input flow completes, normal voice interaction resumes automatically. This capability is intended for PCI-compliant payment and authentication workflows. **Example** ```json expandable=true theme={null} AGENT: SecurePaymentDemo GOAL: "Collect a card payment (PAN, expiry, CVV) via PCI SECURE_INPUT and charge it, without ever exposing the raw digits to the model or any log/trace/session surface." TOOLS: validate_card(pan) charge_card(pan, exp, cvc) FLOW: welcome -> offer_payment -> collect_card -> thank_you welcome: REASONING: false RESPOND: "Welcome. I can take your card payment securely over the phone. Your details are handled securely." THEN: offer_payment offer_payment: REASONING: false GATHER: - ready_to_pay: type: string required: true infer: true prompt_mode: ask description: "The caller's acknowledgement to begin payment. Set this to the caller's response verbatim. ANY non-empty caller utterance or key press satisfies this — 'ready', 'yes', 'ok', 'sure', 'go ahead', 'yeah', a number, or a single word all count. Only leave it empty if the caller said nothing at all." prompt: "When you're ready to pay, say 'ready' or press any key. I'll then ask for your card number, expiry, and security code." THEN: collect_card collect_card: REASONING: false SECURE_INPUT: MODE: [dtmf, speech] FIELDS: - card_number: PROMPT: { en: "Please enter or say your card number followed by the pound key.", es: "Por favor ingrese o diga el numero de su tarjeta seguido de la tecla numeral." } COLLECT: { digits: 13..19, finishOnKey: "#", interDigitTimeoutMs: 5000, asrTimeoutMs: 5000 } VALIDATE: [ luhn, { length: 13..19 }, CALL validate_card WITH { pan: "{{secure.card_number}}" } ] - expiry: PROMPT: { en: "Please enter the 4 digit expiry date, month then year, followed by the pound key." } COLLECT: { digits: 4..4, finishOnKey: "#", interDigitTimeoutMs: 3000, asrTimeoutMs: 2500 } VALIDATE: [ { date_mmYY: not_past }, { length: 4..4 } ] - cvv: PROMPT: { en: "Please enter your card security code, followed by the pound key." } COLLECT: { digits: 3..4, finishOnKey: "#", interDigitTimeoutMs: 3000, asrTimeoutMs: 2500 } VALIDATE: [ { length: 3..4 }, { regex: "^[0-9]{3,4}$" } ] MAX_ATTEMPTS: 3 CANCEL: { dtmf_key: "*", keywords: [ "cancel", "stop" ] } CALL charge_card WITH: { pan: "{{secure.card_number}}", exp: "{{secure.expiry}}", cvc: "{{secure.cvv}}" } ON_SUCCESS: thank_you ON_TIMEOUT: human_agent ON_CANCEL: cancelled ON_FAIL: human_agent THEN: thank_you thank_you: REASONING: false RESPOND: "Thank you. I've securely captured your card details and your payment has been processed successfully. Is there anything else I can help you with?" human_agent: REASONING: false RESPOND: "I'm sorry, I wasn't able to complete that. Let me transfer you to a human agent for help with your payment." cancelled: REASONING: false RESPOND: "Okay, I've cancelled the payment collection. Is there anything else I can help with?" ``` [Learn More](/agent-platform/abl/reference/flow#secure_input). ## Known limitations * Kore.ai Voice Gateway (KoreVG) is the only supported provider. * No provider delivery acknowledgment or automatic retry is available for this method. * No pause syntax inside `digits`; model pauses as separate wait steps. **Example** The following workflow combines inbound and outbound DTMF. ```text theme={null} Caller │ ▼ ivr_menu │ Caller presses 1 │ booking_code = "1" │ ▼ Connect to hospital IVR │ ▼ send_dtmf("1") │ ▼ Hospital routes caller ``` This pattern is commonly used for appointment booking and external IVR automation. ## Best practices * Use `ivr_menu` for single-choice menus. * Use `ivr_digit_input` for collecting structured numeric data. * Use `send_dtmf` only after the external IVR is ready to receive keypad input. * Enable barge-in where faster caller interaction is desired. * Validate all tool results before continuing the workflow. * Always provide a fallback or escalation path for invalid input or DTMF failures. * Include DTMF tools only in voice-enabled agents. # Dual-brain architecture Source: https://koreai.mintlify.app/agent-platform/dual-brain-architecture The Agent Platform runtime executes every interaction using two cognitive engines in parallel: the **Agentic Brain** and the **Deterministic Brain**. A shared memory layer called the Shared Spine coordinates state between them. Both engines are defined in a single Agent Blueprint Language (ABL) definition, compiled to the same intermediate representation (IR), and executed by the same runtime. This split-brain model lets a single agent definition combine autonomous reasoning with deterministic, auditable execution — without maintaining two separate runtimes or frameworks. Dual-Brain Architecture Dual-Brain Architecture ## The Agentic Brain The Agentic Brain **reasons, explores, and decides**. It handles the open-ended parts of an interaction where the correct path is not known in advance. The Agentic Brain runs: * A **multi-intent orchestrator** that decomposes compound user requests into separate intents. * **Specialist agents** scoped to domains such as billing, network operations, or compliance. * **Remote agents** reached via the A2A protocol, including agents hosted outside the platform. * **Tool calls** against CRM systems, BSS platforms, knowledge bases, and payment systems. * An **LLM reasoning engine** that holds context across turns. The Agentic Brain produces reasoning traces — structured records of every agent step, LLM call, and tool result — that are captured by the runtime for observability. ## The Deterministic Brain The Deterministic Brain **validates, enforces, and executes**. It handles the parts of an interaction where the path must be predictable, auditable, and policy-compliant. The Deterministic Brain runs: * **Entity collection** with deep validation rules. * **Authorization profile checks** against the requesting user. * A **routing engine** that combines intent, customer segment, and entitlement. * **Constraint and guardrail enforcement** on both inputs and outputs. * **BSS, OSS, and billing integrations** with known inputs and auditable outputs. * A **flow-and-escalation engine** for human handoff and supervisor queues. The Deterministic Brain produces decision records — structured records of every routing choice, constraint check, and guardrail evaluation — that are captured by the runtime for compliance and audit. ## The Shared Spine The Shared Spine is the runtime coordination layer. Neither brain owns it. Both brains read from and write to it, and neither can act without going through it. The Shared Spine holds: * Session memory. * Threaded conversation context. * The entity store. * Authentication state. * Active guardrails. Every read and write to the Shared Spine is recorded as part of the **Context Audit Trail** — a structured record of what each brain was working with at the moment of every decision. ## Compilation and execution Agent definitions move through three stages before they execute. Both brains share the same compiler, the same IR format, and the same runtime. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A["ABL Definition\n(YAML artifact)"] --> B["ABL Compiler\n(validates + compiles)"] B --> C["IR\n(intermediate representation)"] C --> D["ABL Runtime"] D --> E["Agentic Brain"] D --> F["Deterministic Brain"] E & F --> G["Shared Spine"] ``` | Stage | What Happens | | - | - | | **ABL Compiler** | Validates the agent definition. Catches missing tool references, broken handoffs, and contract violations before deployment. Output is a portable IR artifact. | | **IR** | The compiled artifact. It is versioned and pinned to a deployment, and you can download it. | | **ABL Runtime** | Executes the IR. Handles reasoning, flow control, multi-agent orchestration, and guardrail enforcement. Runs both execution paths defined in the ABL definition. | Because both brains compile from the same ABL definition to the same IR, there is no drift between the authored agent intent and the deployed behavior. Compile-time validation catches errors before they reach any downstream environment. ## Observability outputs The runtime emits three categories of structured trace data. Together, they make every agent decision reconstructable after the fact. | Trace Category | Produced By | What It Captures | | - | - | - | | **Reasoning Traces** | Agentic Brain | Every agent step, LLM call, and tool result. | | **Context Audit Trail** | Shared Spine | Every read and write to shared memory — what each brain was working with at the moment of every decision. | | **Decision Records** | Deterministic Brain | Every routing choice, constraint check, and guardrail evaluation. | These trace categories are complementary. Reasoning Traces answer *what the agent did*. Decision Records answer *what rules were applied*. The Context Audit Trail answers *what data each brain had access to*. All three are required for full session reconstruction. ## Why the split matters A single-engine design forces a trade-off: either the agent reasons flexibly and sacrifices auditability, or it follows a fixed deterministic path and loses the ability to handle open-ended input. The dual-brain model avoids this trade-off by separating the concerns while keeping both engines under one governance layer. | Property | Agentic Brain | Deterministic Brain | | - | - | - | | **Execution style** | LLM-driven reasoning | Rule-based, constraint-enforced | | **Handles** | Open-ended input, multi-intent, tool calls | Entity validation, routing, policy enforcement | | **Output** | Reasoning traces | Decision records | | **Auditability** | Step-level reasoning chain | Deterministic, replay-exact decisions | | **Shared resource** | Shared Spine | Shared Spine | Because both brains are defined in ABL and compiled to the same IR, the runtime can enforce guardrails uniformly across both execution paths. There is no policy gap between the agentic and deterministic sides of an interaction. *** # Create voice agents Source: https://koreai.mintlify.app/agent-platform/enable-voice Voice agents enable real-time spoken interactions between users and AI agents. A typical voice interaction involves a telephony provider, Voice Gateway, voice models, and the agent runtime. At a high level: ```text theme={null} Caller ↔ Telephony Provider ↔ Voice Gateway ↔ Voice Processing ↔ Agent Runtime ``` The voice processing flow depends on the agent's voice architecture. ## Core components | Component | Role | | - | - | | Telephony Provider | Establishes inbound or outbound calls and provides phone or SIP connectivity. | | Voice Gateway | Connects telephony infrastructure with the agent and manages voice sessions and audio routing. | | Voice Models | Process incoming speech and generate spoken responses. | | Agent Runtime | Applies agent instructions, maintains context, accesses knowledge, and invokes tools or workflows. | ## Voice architectures Agent Platform supports different voice processing architectures, including: **Pipeline Voice** — Processes speech through separate speech-to-text (STT), agent/LLM, and text-to-speech (TTS) stages. ```text theme={null} Audio → STT → Agent/LLM → TTS → Audio ``` **Realtime LLM Voice** — Uses a speech-to-speech (S2S) model for real-time audio interaction with agent and tool integration. ```text theme={null} Audio → Realtime S2S Model → Audio ``` ## Steps to enable voice interaction ### Step 1: Configure voice service credentials Configure the credentials required by the voice models you plan to use. * Go to: **Workspace Settings > AI Configuration > Voice Services** * Voice service credentials are managed at the workspace level and can be reused across projects. * Configure credentials based on the voice architecture: * For **Realtime LLM Voice**, configure credentials for a supported S2S provider, such as OpenAI Realtime, Google Gemini Live, or Grok Realtime. * For **Pipeline Voice**, configure the required STT and TTS service credentials. For configuration instructions, see [Configure Voice Services](/agent-platform/administration/ai-configuration#voice-services). ### Step 2: Create a voice channel Create a voice channel to make the agent available via voice. * Go to **Deployments > Channels** * Under **Voice**, select one of the following: * **Realtime LLM Voice** — For real-time conversations using S2S models. * **Pipeline Voice** — For voice interactions, using separate STT, agent/LLM, and TTS stages. * Other voice integrations, such as VXML IVR, Genesys Audio Connector, and AudioCodes, are also available. [Learn more](/agent-platform/channels). For configuration instructions, see: * [Configure Realtime LLM Voice](/agent-platform/channels/voice#set-up-realtime-llm-voice) * [Configure Pipeline Voice](/agent-platform/channels/voice#set-up-pipeline-voice) #### Configuring Realtime LLM Voice When creating a Realtime LLM Voice channel, configure the channel, call routing, and S2S model settings. [Learn More](/agent-platform/channels#set-up-realtime-llm-voice). 1. Provide the basic details. * **Display Name** — Enter a name to identify the channel. * **Provider Verification Strength** — Specify how strongly the platform trusts identity assertions received from the channel or provider. 2. Select the **Voice Provider** used to connect the voice channel with the agent. The Voice Gateway manages the voice session and routes audio between the call connection and the configured voice processing path. Default Voice Provider: **Kore.ai Voice Gateway**. 3. Configure **Call Routing**. Choose how calls reach the voice agent. You can enable one or both of the following routes. * **Soft Phone** — Enable Soft Phone to call the agent directly from the browser. Select **Launch dialer** to start a test call. * **Phone Number (DID)** — Enable Phone Number (DID) to connect a real phone number to the voice agent. Select the telephony provider and configure the phone number. For example, when using Twilio, you can purchase and assign a phone number to the channel. * Configure the supported call directions: **Inbound calls** route calls received on the configured number to the voice agent, and **Outbound calls** allow the configured number to be used for campaign dialout. 4. Select the **S2S Provider** that processes the voice interaction. Configure provider-specific settings. Supported providers include: * Google Gemini Live (S2S) * Grok Realtime (S2S) * OpenAI Realtime Available settings vary by S2S provider. [Find more details here](/agent-platform/administration/ai-configuration#voice-services). #### Configuring Pipeline Voice Create a Pipeline Voice channel when you want speech processing to use separate stages: ```text theme={null} Audio → STT → Agent/LLM → TTS → Audio ``` Configure the required: * **STT service** to convert incoming speech to text. * **Agent/LLM processing** to interpret the request and generate a response. * **TTS service** to convert the generated response into speech. Pipeline Voice allows speech recognition and speech generation services to be configured independently. For detailed configuration instructions, see [Configure Pipeline Voice](/agent-platform/channels/voice#set-up-pipeline-voice). ### Step 3: Test the voice agent Before using a production phone number, test the end-to-end voice interaction with the built-in Soft Phone. For a Realtime LLM Voice channel: 1. Enable Soft Phone. 2. Select Launch dialer. 3. Start a call with the agent. 4. Verify that the agent can receive speech and return audio responses. 5. Verify tool execution and agent behavior via traces. After successful testing, configure a Phone Number (DID) when the agent needs to support real inbound or outbound calls. ## Agent level voice configurations By default, `RESPOND:` blocks in ABL work as-is; the runtime automatically strips markdown and passes clean text to TTS. The `VOICE:` block is optional and only needed when you want to override the default behavior for a specific step: ```yaml theme={null} RESPOND: "Your appointment is confirmed." VOICE: ssml: "Your appointment is confirmed." plain_text: "Your appointment is confirmed." ``` Use it when you need: * SSML markup — prosody, pauses, emphasis, phonemes * A different spoken version than the text — for example `RESPOND:` has a formatted table but you want a short spoken summary * Voice-specific instructions for real-time providers (OpenAI Realtime / Gemini Live) [Learn More](/agent-platform/abl-reference/rich-content#voice-configuration) ### Agent-level voice defaults Use a `BEHAVIOR_PROFILE` to set voice behavior defaults for the whole agent without changing every `RESPOND:` block. ```yaml expandable=true theme={null} # voice-optimized.behavior_profile.abl BEHAVIOR_PROFILE: voice-optimized PRIORITY: 10 WHEN: context.channel == "voice" INSTRUCTIONS: "Keep responses under 2 sentences. No bullet points or markdown formatting." VOICE: instructions: "Speak in a warm, conversational tone." RESPONSE_RULES: max_buttons: 0 fallback_format: plain_text max_response_length: 200 ``` To reference it in your agent: ```yaml theme={null} AGENT: SupportAgent USE BEHAVIOR_PROFILE: voice-optimized ``` This applies the voice defaults to every response on that channel. [Learn More](/agent-platform/administration/agent-behavior-settings#behavior-profiles). *** **Related articles:** * [Configure Real Time Voice Channel](/agent-platform/channels/voice#set-up-realtime-llm-voice) * [Configure Pipeline Voice Channel](/agent-platform/channels/voice#set-up-pipeline-voice) * [Pass Custom SIP Headers on Voice Transfer](/agent-platform/sending-custom-sip-headers) * [Handle Incoming SIP Headers in Agents](/agent-platform/handle-incoming-sip-headers) # Escalation to human agent from an agent Source: https://koreai.mintlify.app/agent-platform/escalate Use `ESCALATE` when an agent cannot safely or confidently continue execution and requires human intervention. Escalation transfers the conversation to a human operator or handling system, ensuring continuity while preventing incorrect or risky automated responses. Unlike agent-to-agent routing (`HANDOFF` / `DELEGATE`), escalation is specifically designed for human involvement and should not be modeled as a handoff to a human agent. ## When to use escalation Typical scenarios include: * User frustration * Customer complaints * Policy validation requirements * System failures * Repeated routing failures * High-risk or high-value operations ## How escalation works 1. Agent evaluates escalation triggers. 2. When a trigger condition is met: * A new task is created in the Inbox for a human agent to review and proceed. * Agent execution is suspended. * A transfer payload is constructed from: * `context_for_human` * `routing.metadata` * any provider-specific routing fields. 3. The payload is sent to the configured human agent provider. 4. A human agent reviews and completes the task. 5. After the human interaction completes, the configured `on_human_complete` actions determine whether the workflow resumes, hands off to another agent, or completes. Configure Escalation using the following fields. * **Triggers**: Each trigger defines a condition under which the flow escalates to a human, along with metadata used to prioritize and route the escalation. * **Context for Human**: Lets the flow designer specify which context variables are passed to the human agent when a conversation is escalated. * **On Human Complete**: Defines handler actions the agent should take after a human agent resolves the escalated issue (for example, resuming the flow, updating variables). * **Routing**: Routing controls where an escalated conversation is delivered and how it is presented once it reaches a human agent desktop. * **Voice Settings**: Voice Settings govern how the active call leg is handed off when the escalation routes over a voice channel. It is only relevant for voice interactions. | **Field** | **Description** | | - | - | | **Move the Call Leg** | Determines how the call is handed to the agent desktop. **Relay to agent desk** keeps the caller on the current leg and proxies the call to the agent desktop. **SIP** or **PSTN** moves the call itself while keeping the destination project-configured. The **Transfer Method** and **SIP Headers** settings apply only when **SIP** or **PSTN** is selected. | | **Transfer Method** | Specifies the call transfer mechanism used when the call leg is moved using **SIP** or **PSTN**. | | **SIP Headers** | Defines custom SIP headers to attach to transferred or relayed calls. Headers can be added one at a time using the **+ Add Header** option. | ## DSL configuration ```yaml expandable=true theme={null} ESCALATE: triggers: - WHEN: intent = transfer to customer support REASON: "requested" PRIORITY: medium context_for_human: - customer_id - summary routing: connection: "152f48d0-290e-4293-87e9-d8d9138ad58d" routing: queue: queue_name skills: - skill1 - skill2 priority: 1 metadata: sentiment_score: "{{sentiment_score}}" attempt_count: "{{retry_count}}" customer_tier: "{{customer.tier}}" on_human_complete: - IF human.resolved == true: COMPLETE ``` ## Trigger properties | Property | Type | Required | Description | | - | - | - | - | | WHEN | string | Yes | Condition that triggers escalation. | | REASON | string | Yes | Human-readable reason for the escalation. | | PRIORITY | string | Yes | Priority level: low, medium, high, or critical. | | TAGS | string\[] | No | Tags for routing and categorization in the human agent queue. | ## Priority levels | Level | Use case | | - | - | | low | Non-urgent requests (for example. general feedback). | | medium | Standard requests (for example. user requests human assistance). | | high | Urgent issues (for example. repeated failures, outages). | | critical | Immediate attention required (for example. compliance violations, fraud). | ## Context for human The `context_for_human` block defines what data is sent to the human agent. This ensures the human agent has full context without asking the user to repeat information. ```yaml theme={null} context_for_human: - customer_id - customer_name - amount - conversation_history ``` ### Structured context ```yaml theme={null} context_for_human: - NAME: case_summary TEMPLATE: "Customer {{customer_name}} requesting wire of {{amount}} {{currency}}" INCLUDE: [fraud_score, sanctions_match_score] ``` ## Routing configuration Controls how escalation is routed to the human system. | Property | Type | Required | Description | | - | - | - | - | | id | string | No | Optional label for authoring/observability. Defaults to `entry_` if omitted. | | when | string | No | Used for conditional routing. [Learn More](/agent-platform/abl/reference/multi-agent-and-supervisor#conditional-routing-entries). | | queue | string | No | Target queue in the human agent system. You can use expressions to define dynamic values for the queues. | | skills | string\[] | No | Required skills for the human agent. | | priority | number | No | Numeric routing priority for the queue entry. | | metadata | object | No | Key value pairs for passing metadata to the receiving agent. | | voice | - | No | Optional block controlling how the voice call is handed off at the SIP/transport layer when escalating on a voice channel | | voice.transfer\_method | - | No | SIP signaling method used to transfer the call: `invite`, `refer`, or `bye`. | | voice.call\_transfer\_type | - | No | Whether to move the call over `sip` or `pstn`. Omit to infer from project configuration. The target is always project-owned — this selects a transport, not a destination. | | voice.sip\_headers | - | No | Key-value map of custom SIP headers to forward on the transfer to the receiving SBC or contact center. | * Use `context_for_human` for passing existing session variables to agents. * Use `routing.metadata` for additional information like computed values, routing hints, summaries, that improves the human agent's context or routing abilities. * During agent transfer, `ESCALATE.routing.metadata` is passed to the external agents as sip headers in case of voice interactions and as session level tags in chat. ### Metadata propagation During an escalation, metadata is propagated differently depending on the channel: * **Voice channels(SIP)**: Metadata configured in the ESCALATE block is automatically converted into SIP headers and sent to the receiving voice platform during the transfer. You can also use routing.voice.sip\_headers to define additional custom SIP headers or override the header mapping when needed. * **Digital channels**: Metadata is forwarded to the receiving desktop or contact-center platform as key-value pairs through the provider's handoff API or WebSocket connection. The exact format depends on the integration and the capabilities of the receiving platform. ## Post-completion behavior Defines what happens after the human agent completes the task. ```yaml theme={null} on_human_complete: - IF human.resolved == true: COMPLETE - IF human.needs_agent == true: HANDOFF to specified_agent - IF human.needs_followup == true: CONTINUE ``` | Action | Behavior | | - | - | | COMPLETE | End the workflow | | HANDOFF | Transfer back to an agent | | CONTINUE | Resume the current agent | ## Examples ### Escalation from supervisor ```yaml expandable=true theme={null} SUPERVISOR: Customer_Service_Hub VERSION: "2.0" GOAL: "Route customers to the right specialist" PERSONA: | Professional customer service coordinator. Routes requests efficiently and preserves context across agent transfers. ESCALATE: triggers: - WHEN: routing_failures >= 3 REASON: "Multiple routing failures" PRIORITY: high - WHEN: user.wants_human_agent == true REASON: "Customer requesting human specialist" PRIORITY: medium context_for_human: - user_id - conversation_summary - routing_failures - conversation_history routing: queue: "customer_support_l2" skills: [support, escalation] priority: 1 on_human_complete: - IF human.resolved == true: COMPLETE - IF human.needs_agent == true: HANDOFF to Support_Agent ``` ### Escalation via ON\_ERROR `THEN: ESCALATE` triggers the escalation flow after retry attempts are exhausted. ```yaml expandable=true theme={null} ON_ERROR: tool_error: RESPOND: "I encountered an issue. Let me connect you with a specialist." RETRY: 1 THEN: ESCALATE routing_failure: RESPOND: "I am having trouble routing your request." RETRY: 1 THEN: HANDOFF Live_Agent_Transfer ``` ## Escalation vs handoff | Aspect | ESCALATE | HANDOFF | | - | - | - | | Target | Human operator | Another agent | | Visibility | User interacts with human | User interacts with agent | | Use case | Failure / escalation scenarios | Multi-agent routing | | Control flow | Suspends agent execution | Transfers execution | ## Troubleshooting | Issue | Resolution | | - | - | | Escalation never triggers | Verify the `WHEN` condition references variables that are actually set during the conversation. Test with explicit values. | | Human agent lacks context | Include `conversation_history` in `context_for_human`. List all relevant session variables-IDs, collected data and current state. | | Conversation stalls after human completes | Define `on_human_complete` handlers for all possible human outcomes (`resolved`, `needs_agent`, `needs_followup`). | # Evaluations Source: https://koreai.mintlify.app/agent-platform/evals Evaluations provide a structured framework to test, measure, and improve your agents by simulating conversations, scoring responses, and analyzing the results. Create evaluation suites to test how your agent behaves across different scenarios, personas, and evaluators. Review the results to identify issues, validate changes, and continuously improve agent performance. The platform supports both AI-assisted and manual evaluation workflows. It is recommended to use **Arch AI** to automatically generate evaluation suites based on your project. You can review and customize the generated suite before running it. **Navigation**: Go to your project and select **Evaluate** > **Evals**. ## Evaluation workflow Create an evaluation suite manually or let Arch generate one based on your project. Review and customize the suite before running it. Run the evaluation suite to simulate conversations and measure your agent’s performance across the configured scenarios, personas, and evaluators. Review evaluation scores, conversations, traces, and execution details to identify issues and opportunities for improvement. Use the evaluation results to identify recommended improvements. Depending on your Arch configuration, you can review changes manually or allow Arch to apply eligible improvements automatically. Run the evaluation suite again to verify that the applied changes improve the agent's perfomance. ## Key concepts | Concept | Description | | :- | :- | | **Evaluation Suite** | A collection of scenarios, personas, evaluators, and run settings that defines how an agent is evaluated. | | **Eval Library** | Stores reusable personas, scenarios, and evaluators that can be shared across multiple evaluation suites within a project. | | **Arch AI** | Generates evaluation suites, analyzes evaluation results, and recommends improvements as part of the evaluation workflow. | ## How Arch AI optimizes your agent Arch AI helps you continuously improve your agents by analyzing evaluation results, generating repair recommendations, and validating improvements through repeated evaluation cycles. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A[Evaluate] B[Analyze] C[Recommend] D[Apply] E[Validate] A --> B --> C --> D --> E ``` | Stage | What Happens | | :- | :- | | **Evaluate** | Run the evaluation suite to measure your agent's performance across the configured scenarios and personas. | | **Analyze** | Arch AI analyzes evaluation scores, conversation traces, and execution details to identify failed or low-scoring conversations. | | **Recommend** | Arch AI generates recommendations to improve prompts, workflows, tool usage, or agent behavior based on the identified issues. | | **Apply** | Review and apply recommended changes based on your configured Arch autonomy level. | | **Validate** | Re-run the evaluation suite to verify that the applied changes improve the evaluation results. | **Learn more**: To see where Evaluations fit in the Arch AI lifecycle, see [Arch AI](/agent-platform/arch-ai#lifecycle-phases). ### Example: Improve tool call accuracy using Arch Suppose an evaluation shows that your agent has a low Tool Call Accuracy score. Arch AI analyzes the evaluation results and conversation details to understand why the agent is failing. It may identify that the agent is selecting the correct tool but providing incorrect parameters because the instructions do not clearly specify the required inputs. Based on this analysis, Arch AI recommends an improvement to the agent’s instructions. After the change is reviewed and applied, the evaluation suite is run again to validate the improvement. If the agent now provides the correct parameters and the Tool Call Accuracy score improves, the evaluation cycle is complete. **Learn more:** For more information about the Arch AI reinforcement loop and continuous optimization, see [Optimize with Arch AI](/agent-platform/optimize). *** ## Create evaluation suites Evaluation suites define how your project is evaluated. Each suite combines scenarios, personas, evaluators, and run settings to measure your agent's performance. You can create an evaluation suite in one of the following ways: | Option | Description | | :- | :- | | **Create with Arch** (Recommended) | Let Arch generate an evaluation suite based on your project. | | **Create Test Suite** | Create and configure an evaluation suite manually. | ### Create eval suite with Arch Use **Create with Arch** to automatically generate an evaluation suite based on your project. 1. Go to **Evaluate** > **Evals**. 2. Select **Create with Arch**. 3. Wait for Arch to generate the evaluation suite. 4. Review the generated evaluation suite. 5. (Optional) Select **Edit Configuration** to customize the generated components. 6. Select **Create & Run Suite** to create the suite and start the evaluation. Arch analyzes your project and automatically generates the evaluation suite components, including scenarios, personas, evaluators, and run settings. You can review and modify the generated configuration before running the evaluation. ### Create eval suite manually To create an evaluation suite manually, from the **Evals** page, select **Create test suite** or **+ New suite**. Both options open the **Create eval suite** configuration screen. Configure the suite by defining its: * **Basics** — Specify the suite name, description, scope, and modality. * **Scenarios** — Select or add the scenarios to evaluate. * **Personas** — Select or add the personas that simulate users. * **Evaluators** — Select evaluators and configure their weights and passing score. * **Arch autonomy** — Select how much autonomy Arch has when applying improvements and set the maximum number of runs per loop. * **Run settings** — Configure how conversations are generated and when the suite runs. Select **Create & run suite** to create the evaluation suite and start the evaluation. *** ## Evaluation suite components Every evaluation suite, whether created manually or with Arch, contains the following components. Together, they define what is evaluated, how the evaluation is executed, and how the results are scored. When you create an evaluation suite with **Arch**, these components are generated automatically based on your project. You can review and modify them before running the evaluation. | Section | Description | | :- | :- | | **Scenarios** | Defines the user tasks or conversations used to evaluate your agent. | | **Personas** | Defines the characteristics and behavior of users participating in the evaluation. | | **Evaluators** | Defines the metrics and criteria used to score each conversation. | | **Run Settings** | Configures how the evaluation is executed, including conversation variations and execution options. | ### Basics Use the Basics section to define what the evaluation suite covers and which project state it evaluates. * **Suite name** — Enter a name for the evaluation suite. * **What this suite covers** — Describe the purpose or focus of the evaluation suite. * **Test scope** — Choose whether to evaluate the entire project or specific agents. * **Version to test** — Select the project version, deployment, or environment to evaluate. Depending on the selected scope, the available options may include the Working copy, an Environment, a specific Deployment, or a published Agent version. Select **Change scope** to change the project or agent scope and configure the corresponding evaluation target. ### Arch's autonomy level Arch's autonomy level setting determines how much control Arch has to apply improvements and re-run evaluations when a run does not meet the passing score. You can change this setting at any time. * **Autopilot** — Arch fixes and re-runs the evaluation on its own. Arch patches the agent and keeps re-running until the score clears the target or the configured loop limit is reached. * **Copilot** — Arch automatically applies low-risk fixes. Arch diagnoses regressions, applies low-risk changes, and stages higher-risk changes for approval. * **Advisor** — Arch recommends fixes only. Arch explains what regressed and proposes changes without applying them. **Max runs per loop**: Sets the maximum number of fix-and-rerun attempts Arch can make, even if the score has not cleared the target. This limits the number of attempts Arch can make in a single loop. *** ## Scenarios Scenarios define the conversation flow, user intent, and expected outcomes used during evaluations. Each scenario represents a conversation flow used to evaluate how the agent handles specific tasks, behaviors, or outcomes. To create a scenario: 1. In the **Scenarios** section, select **Add Scenario**. 2. Complete the scenario details. 3. Select **Create**. | Field | Description | | :- | :- | | **Name** | Unique name for the scenario. | | **Description** | Brief description of what the scenario tests. | | **Category** | Logical grouping used to organize scenarios, such as Billing, Customer Support, Technical, or Onboarding. | | **Difficulty** | Complexity level of the scenario: **Easy**, **Medium**, or **Hard**. | | **Entry Agent** | Agent that starts the conversation. Primarily used in multi-agent projects. | | **Initial Message** | First user message that starts the conversation. | | **Max Turns** | Maximum number of conversation turns before the evaluation stops. | | **Agent Path** | Expected sequence of agent handoffs during the conversation. Applicable to multi-agent projects. | | **Expected Milestones** | Key checkpoints the conversation should achieve. | | **Expected Outcome** | Describes what a successful conversation should accomplish. | | **Session Variables (JSON)** | Optional session variables used to provide contextual data for the scenario. Scenario session variables are merged with the persona's session variables when an evaluation runs. If the same key is defined in both, the scenario value takes precedence unless the key is selected as a **Priority key** for the persona. | | **Tags** | Labels used to organize and filter scenarios by feature area, regression suite, priority, or other categories. | Evaluate ### Example scenario ```yaml theme={null} SCENARIO: name: "Flight Rebooking After Cancellation" category: booking difficulty: medium initial_message: > My flight was cancelled and I need to rebook for tomorrow. expected_outcome: > Agent identifies the cancelled booking, offers alternatives, and confirms a new flight. max_turns: 15 expected_milestones: - "Identify cancelled flight" - "Present rebooking options" - "Confirm new booking" agent_path: - "Supervisor" - "Booking_Manager" ``` ### Troubleshoot scenarios | Issue | Recommendation | | - | - | | Duplicate name error | Persona and scenario names must be unique within a project. Use a more specific name or delete the existing one. | | Persona not behaving as expected in evals | Refine the **Goals** and **Constraints** fields. These are used as system prompt instructions for the simulated user LLM. | | Scenario timing out | Increase the **Max Turns** value or simplify the expected conversation path. | *** ## Personas Personas represent different types of users who interact with your agent. Each persona simulates unique communication styles, domain expertise, goals, behaviors, and constraints to help test how the agent performs across varied user interactions. To create a persona: 1. In the **Personas** section, select **Add Persona**. 2. Complete the persona details. | Field | Description | | - | - | | **Name** | Unique name for the persona. | | **Description** | Brief description of the persona and the type of user it represents. | | **Communication Style** | Defines how the persona communicates, for example, casual, terse, or formal. | | **Domain Knowledge** | Defines the persona's familiarity with the subject, such as beginner, intermediate, or expert. | | **Behavior Traits** | Characteristics that influence how the persona behaves during the conversation. For example, asks follow-up questions, polite, or impatient. | | **Goals** | Defines what the persona is trying to accomplish during the interaction. | | **Constraints** | Rules or limitations that influence the persona's behavior during the conversation. | | **Adversarial Type** | Specifies whether the persona behaves as a normal user or intentionally challenges the agent to test its robustness. | | **Session Variables (JSON)** | Optional session variables that provide contextual data for the persona. | | **Priority keys** | Select session variable keys whose persona values take precedence over values defined in a scenario. | 3. Select an adversarial behavior type if you want to simulate edge cases or malicious interactions. 4. Click **Create**. Evaluate ### Example persona ```yaml theme={null} PERSONA: name: "Impatient Business Traveler" communication_style: terse domain_knowledge: expert behavior_traits: - impatient - asks_follow_up_questions goal: "Rebook a cancelled flight quickly" constraint: "Avoid unnecessary conversation" ``` ### Adversarial persona types You can simulate adversarial or edge-case user behaviors using the **Adversarial Type** field. To test agent safety and robustness: 1. Enable **Adversarial** while creating a persona. 2. Select the adversarial type. | Type | Purpose | | - | - | | Prompt Injection | Attempts to override agent instructions | | Social Engineering | Attempts to extract sensitive information | | Off-topic Derailer | Redirects conversations away from the intended agent goal | | Abusive User | Uses hostile or inappropriate language | | Edge Case Explorer | Sends unusual or unexpected inputs (empty, very long messages) | ### Troubleshoot personas | Issue | Recommendation | | - | - | | The persona's behavior is inconsistent | Refine the goals and constraints fields | | The persona's responses are unrealistic | Add more specific behavioral traits and communication styles | | The persona is too passive or aggressive | Adjust goals, constraints, and adversarial settings | *** ## Evaluators Evaluators define how conversations are assessed during an evaluation. Each evaluator measures a specific aspect of the conversation, such as response quality, safety, task completion, or compliance, and assigns a score based on the configured evaluation criteria. To configure an evaluator, follow these steps: 1. In the **Evaluators** section, select **Add Evaluator**. 2. Enter the evaluator name and description. 3. Select the **Type** and **Category**. 4. Configure the evaluator based on the selected type. 5. Select **Create**. Lower evaluator temperatures typically produce more consistent scoring results. ### Evaluator types Supported evaluator types include: | Type | Description | | :- | :- | | LLM Judge | Uses an LLM to evaluate conversations. | | Code Scorer | Uses deterministic programmatic scoring. | | Trajectory | Evaluates conversation flow and milestones. | | Human Review | Flags conversations for manual review. | ### LLM Judge evaluators An LLM Judge evaluator uses a separate LLM to assess the quality of agent responses based on a scoring rubric you define. | Field | Description | | :- | :- | | **Judge Model** | Language model used to evaluate the conversation. | | **Temperature** | Controls the randomness of the judge model's responses. Lower values produce more consistent evaluations. | | **Judge Prompt** | Instructions that define the evaluation criteria for the judge model. | | **Chain-of-Thought Reasoning** | Enables the judge model to perform intermediate reasoning before assigning a score. | | **Scale Type** | Specifies the scoring method: **Pass/Fail** or **1–5 Scale**. | | **Bias Mitigation** | Applies techniques to help reduce bias and improve evaluation consistency. | Evaluate #### Write effective judge prompts The judge prompt is one of the most important evaluator configurations. Well-defined prompts produce more consistent and reliable evaluation results. Effective judge prompts: * Clearly define evaluation criteria * Focus on observable behavior * Avoid ambiguous language * Include examples when possible #### Example judge prompt ```yaml theme={null} JUDGE_PROMPT: You are evaluating an AI agent's response quality in a customer support context. Evaluate the conversation on these criteria: 1. Did the agent correctly identify the customer's intent? 2. Did the agent provide accurate information? 3. Did the agent follow the expected conversation flow? 4. Was the agent's tone appropriate and professional? Score each conversation using the provided rubric. Focus on the agent's responses, not the simulated user's messages. ``` #### Configure bias mitigation LLM judges can exhibit scoring biases. Use bias mitigation settings to improve evaluation consistency and reliability. | Setting | Description | Default | | :- | :- | :- | | Position Swap | Evaluates the conversation in both original and reversed order to reduce positional bias. | On | | Blind Evaluation | Removes agent or persona identity information before judging. | On | | Cross-Model Judge | Uses a different model family than the agent being evaluated. | Off | | Evidence-First (RULERS) | Requires the judge to cite evidence before assigning scores. | On | ### Trajectory evaluators Trajectory evaluators assess the agent's execution behavior rather than response quality. Use them to validate: * Milestone completion -- did the conversation hit expected checkpoints? * Handoff correctness -- did the supervisor route to the right agent? * Path efficiency -- how many unnecessary steps did the agent take? * Tool sequence -- did the agent call tools in the right order? ### Code Scorer evaluators Use Code Scorer evaluators for deterministic validations that do not require an LLM. Typical use cases include: * Regex matching * Keyword validation * Latency or response-time thresholds * Structured output validation Code Scorer evaluators execute custom scoring logic to validate agent responses and runtime behavior using deterministic rules. ### Human Review evaluators Use Human Review evaluators for subjective or manual quality assessments. Human Review evaluators flag conversations for manual inspection when evaluation scores fall below configured thresholds, allowing reviewers to validate agent behavior, response quality, or policy compliance before approval or release. ### Scoring scale types The scoring rubric defines how the evaluator assigns scores to conversations. It supports Likert and Binary scales. #### Likert scale Use a 1 to 5 scale to define detailed evaluation criteria for each score level. | Score | Description | | - | - | | 5 - Excellent | Addresses the user's request with accurate and complete information. | | 4 - Good | Addresses the request with minor omissions. | | 3 - Adequate | Partially addresses the request but misses important details. | | 2 - Poor | Mostly misses the request or provides inaccurate information. | | 1 - Failing | Fails to address the request or provides harmful information. | #### Binary scale Use pass or fail scoring for binary evaluation criteria. | Score | Description | | - | - | | 1 - Pass | The agent completes the task within the expected flow. | | 0 - Fail | The agent fails to complete the task or deviates from expected behavior. | ### Troubleshoot evaluators | Issue | Recommendation | | :- | :- | | Inconsistent scores across runs | Lower evaluator temperature (try `0.1`) and enable evidence-first mode. Run multiple variants per evaluation to get statistical confidence. | | Judge ignores rubric criteria | Make rubric instructions more specific using examples. | | The Judge model is too expensive | Use a smaller model for initial screening and reserve larger models for detailed analysis. Set appropriate `maxTokens` limits. | | The Evaluation cost is too high | Use smaller judge models during development. | | Scores appear random | Increase statistical sample size using variants. | *** ## Configure run settings Run settings determine how the evaluation suite is executed. To configure, follow these steps: 1. In the **Run Settings** section, configure the run settings. 2. Select **Create & Run Suite**. ### Run settings | Setting | Description | | - | - | | **Variations per pair** | Specifies the number of conversation variations to generate for each scenario and persona combination. Multiple variations help evaluate the consistency of your agent's responses across different interactions. | | **Conversation model** | Specifies the model used to generate the conversations that are graded. | | **Conversation budget** | Sets the spending limit for the conversation model. You can set the budget in dollars or tokens, or leave it unlimited. | | **When to run** | Specifies whether the evaluation suite runs manually or on a recurring schedule. For scheduled runs, configure the frequency (Daily, Weekly, or Monthly), day and time, start date, and end condition. The next scheduled run is shown in the eval suite, and scheduled runs are identified in the run history. | | **Max parallel calls** | Specifies the maximum number of conversations that can run concurrently. | | **Retries on failure** | Specifies the number of times to retry a conversation when it fails. | The total number of conversations in an evaluation is calculated as: **Scenarios × Personas × Variations** ### How evaluations are executed During execution: * Every selected Persona interacts with every selected Scenario * Each conversation is independently executed * All configured Evaluators score the resulting conversations This creates a full evaluation matrix across personas, scenarios, and evaluators. **Example Evaluation Matrix** ```yaml theme={null} EVAL_SET: personas: 3 scenarios: 4 evaluators: 2 variants: 2 TOTAL_CONVERSATIONS: formula: "3 Personas × 4 Scenarios × 2 Variants" result: 24 TOTAL_EVALUATIONS: formula: "24 Conversations × 2 Evaluators" result: 48 ``` Each conversation is executed as an independent multi-turn session where the persona LLM simulates the user according to the scenario definition. *** ## View evaluation results After you create and run an evaluation suite, the Evals page displays all evaluation suites in your project. Use this page to monitor suite performance, review evaluation coverage, and open a suite to view detailed results. Each evaluation suite displays: * **Behavior Score** – Overall evaluation score for the latest run. * **Coverage** – Number of scenarios, personas, variations, and generated conversations included in the evaluation. * **Evaluators** – Number of evaluators configured for the suite. * **Last Run** – Shows when the suite was last run and whether it runs manually or on a schedule. Select an evaluation suite to view its detailed results. The evaluation suite page includes the following tabs: Overview, Latest run, and History. ### Overview Provides a high-level summary of the evaluation suite and its latest execution. Use it to monitor overall performance, review evaluation coverage, and identify areas that require attention. | Overview item | Description | | - | - | | **Run status and progress** | Shows the status of the latest run and its progress. | | **Behavior score and trend** | Shows the latest behavior score and how the score changes across runs. | | **Latest run summary** | Provides details about the most recent evaluation run. | | **Arch recommendation** | Shows the status of recommendations from Arch based on the evaluation results. | | **Evaluators below target** | Highlights evaluators whose results are below the configured passing score. | *** ### Latest run Displays the results of the most recent execution of the evaluation suite. Use this page to: * Review the overall run score and evaluator results. * Review the evaluator breakdown. * View individual evaluation conversations and their results. * Search and filter conversations. * Retry conversations that encountered errors using Retry all errors. *** ### History The History tab displays previous evaluation runs for the suite. Use it to review how the suite's results change across runs and understand the changes associated with each run. Includes information such as: * Run date and duration * Changes associated with the run * Behavior score and source metric * Source of the run * View conversations to review conversations from a specific run *** ## Analyze evaluation results After an evaluation completes, review the results to understand how your agent performed and identify opportunities for improvement. You can: * Review conversation transcripts. * Analyze evaluator scores and reasoning. * Inspect execution traces and tool usage. * Compare expected and actual conversation outcomes. * Identify patterns across successful and failed conversations. ### Analyze conversations Select a conversation from the **Latest Run** or **History** tab to review the evaluation details. For each conversation, you can inspect: | Information | Description | | - | - | | **Transcript** | Complete conversation between the persona and the agent. | | **Evaluator Scores** | Scores assigned by each evaluator, including reasoning when available. | | **Execution Trace** | Tool calls, handoffs, workflow execution, and other runtime details. | | **Milestones** | Expected milestones that were completed or missed. | | **Tool Usage** | Tool invocations, inputs, outputs, and any execution failures. | ### View evaluator reasoning When **Chain-of-Thought Reasoning** is enabled for an LLM Judge evaluator, the evaluation results include reasoning that explains how the score was determined. Use evaluator reasoning to identify improvements in: * Agent instructions * Workflows * Tool configuration An example evaluator reasoning: ```yaml theme={null} evaluation_result: score: 3/5 reasoning: > The agent correctly identified the customer's intent but failed to provide complete rebooking options before requesting additional information. ``` ### Act on results Use the evaluation results to prioritize improvements to your agent. | Finding | Recommended Action | | :- | :- | | **Low quality scores** | Refine agent instructions, goals, personas, or workflow logic. | | **Low safety scores** | Strengthen guardrails and evaluate with adversarial personas. | | **Low tool accuracy** | Review tool configuration, input parameters, and agent instructions. | | **Handoff issues** | Review handoff conditions and validate the expected agent path. | ### Troubleshoot | Issue | Recommendation | | :- | :- | | **Scores appear inconsistent** | Increase the number of **Variations per Scenario × Persona** and lower the judge model temperature for more consistent results. | | **Unexpectedly high scores** | Review the evaluator prompt and scoring criteria to ensure they accurately reflect the expected behavior. | | **Higher execution costs** | Reduce the number of variations or use smaller judge models during development. | *** **Related articles:** * [Arch AI](/agent-platform/arch-ai) * [Create Agents](/agent-platform/create-agents) * [Optimize with Arch AI](/agent-platform/optimize) # External agent Source: https://koreai.mintlify.app/agent-platform/external-agent Register and use external agents in Agent Platform using the A2A (Agent-to-Agent) protocol. This enables cross-service agent invocation over standard HTTP — including agents owned by external organizations — supporting delegation, sub-task handoff, and multi-agent orchestration across service boundaries. The routing executor handles remote handoffs transparently, making agent calls location-agnostic: external agents are invoked the same way as local ones. ## Register an external agent Register an external agent to make it available within the Agent Platform. **Prerequisites** * The external agent supports the A2A protocol. * The agent endpoint is reachable over HTTP or HTTPS. * You have the endpoint URL for the external agent. * Authentication details are available if required. **To register an external agent:** 1. Navigate to **External Agents**. 2. Click **Register Agent**. 3. Enter the required details: * **Agent Name** — Name of the external agent. * **Endpoint URL** — URL of the external agent endpoint. * **Protocol** — Communication protocol for the external agent. Supported protocols: * A2A * REST * **Authentication Type** — Authentication mechanism required by the external agent endpoint. Supported types: * API Key * Bearer Token 4. Click **Register**. The platform validates the endpoint and adds the agent to the **External Agents** list. Use the **Test Connection** action to verify that the endpoint is reachable. On success, the platform fetches and stores the agent card, which defines the agent's capabilities, skills, and streaming support. ## Access external agents via handoff or delegation Once registered, external agents are available as tools within Agent Platform agents. You can combine local and external agents in the same multi-agent workflow. To use an external agent, add it to the **Handoff** or **Delegate** section of your agent. The platform handles communication through the A2A protocol. **In ABL**, add the external agent under the `HANDOFF` or `DELEGATE` section and set `LOCATION` to `REMOTE`: ```yaml theme={null} HANDOFF: - TO: RemoteAgentName # must match ExternalAgentConfig.name in the DB WHEN: LOCATION: REMOTE # marks this as an external call ENDPOINT: "https://remote.example.com/a2a" # inline URL (optional if DB config exists) PROTOCOL: A2A # only 'a2a' supported today ASYNC: true # optional: suspend + push-notification callback mode TIMEOUT: 300 # optional: seconds CONTEXT: pass: [field1, field2] # session/context variables to forward summary: "Why we're handing off" history: full # none | summary_only | full | last_n | auto RETURN: true # true = parent thread waits for response ``` ### Remote-specific properties | Property | Values | Description | | - | - | - | | `LOCATION` | `REMOTE` | Marks the tool as a remote agent. | | `ENDPOINT` | URL string | The A2A endpoint. Supports `{{config.*}}` and `{{env.*}}` interpolation. This property is optional. If provided, it overrides the endpoint URL configured during agent registration. | | `PROTOCOL` | `A2A` or `REST` | The communication protocol used to invoke the remote agent. | | `ASYNC` | `true` / `false` | Enables asynchronous dispatch with push-notification barriers. When set to `true`, configure the `CALLBACK_BASE_URL` environment variable. This value must be a publicly accessible HTTPS URL where the remote agent can post execution results. | | `TIMEOUT` | Integer (seconds) | Maximum time to wait for a response from the remote agent before the request times out. | | `CONTEXT` | — | Copies named fields from the current session's gathered or set data values into the request metadata sent to the external agent. | | `RETURN` | `true` / `false` | Whether the parent thread waits for the response. | ### Operating modes #### Sync mode This is the default mode. The agent waits for a response from the external agent. The default timeout is 30 seconds. If the request times out, the `ON_FAILURE` event is triggered. ```yaml theme={null} - TO: Risk_Analyzer WHEN: intent contains "risk" LOCATION: REMOTE ENDPOINT: "http://localhost:4002" PROTOCOL: A2A CONTEXT: pass: [contract_id, contract_type] summary: "User needs risk analysis" RETURN: true ``` #### Streaming Streaming is auto-detected when the remote agent card has `streaming: true` and the user is connected. No DSL changes are required; the runtime handles it automatically. ```yaml theme={null} - TO: Risk_Analyzer WHEN: intent contains "risk" LOCATION: REMOTE ENDPOINT: "http://localhost:4002" PROTOCOL: A2A RETURN: true ``` #### Async Async mode uses a suspend-and-callback mechanism. The current session suspends, and the remote agent pushes the result when done. Use this mode for long-running operations. Requirements for async remote calls: * The `CALLBACK_BASE_URL` environment variable must be set to a public HTTPS URL reachable by the remote agent. * The remote agent's card must have push notification capabilities. * Default async timeout is 300 seconds. Override with the `TIMEOUT` value in ABL. ```yaml theme={null} - TO: Async_Risk_Analyzer WHEN: intent contains "deep analysis" LOCATION: REMOTE ENDPOINT: "http://localhost:4002" PROTOCOL: A2A ASYNC: true # critical — forces suspend + callback path TIMEOUT: 300 # max wait in seconds CONTEXT: pass: [contract_id] summary: "Background deep analysis" RETURN: true ``` 1. **Case sensitivity**: `TO: ` must exactly match the name provided during registration. The value is case-sensitive. 2. **Config variable**: When using a config variable for the URL, `{{config.EXTERNAL_AGENT_URL}}` resolves at compile time, not runtime. The config variable must exist in the project before you save and deploy the agent. This approach is recommended for multi-environment setups. ```yaml theme={null} - TO: Hosted_External_Agent WHEN: true LOCATION: REMOTE ENDPOINT: "{{config.EXTERNAL_AGENT_URL}}" # set this in project config vars PROTOCOL: A2A CONTEXT: history: full RETURN: true ``` 3. **Async callback**: Push-notification callbacks require a public HTTPS URL set in the `CALLBACK_BASE_URL` environment variable. 4. **Inline endpoint**: An inline `ENDPOINT` in DSL overrides the endpoint provided during registration. Authentication details must still be configured during registration—credentials cannot be added to the DSL. 5. **Remote agent state**: Remote agents are stateless by default and only see the current message. Use `history: full` in `CONTEXT` to forward the conversation. The history is stored in `message.metadata.history` in the A2A payload. 6. **Context tracking**: A `contextId` is sent on every turn to the same remote agent, allowing the remote side to associate turns if it maintains state. # Frequently asked questions Source: https://koreai.mintlify.app/agent-platform/faq *** ## Get started ### How do I access the Agent Platform? To sign up, use your email address or use Google or GitHub login via single sign-on (SSO). A free tier is available so you can [start building agents](/agent-platform/getting-started) immediately. To reset a forgotton password, click **Forgot password** on the login page and enter the email address associated with your account. Follow the password reset link delivered in your inbox. If you signed up using Google or GitHub SSO, you don't have a platform password. Instead, sign in using your identity provider. ### How do I invite team members to my workspace? Open **Settings** > **Team** > **Members** and click **Invite member**. Enter their email address and select a [role](/agent-platform/glossary#role). Invited members receive an email with a link to join your workspace. ### How do I switch between workspaces? Click the workspace name in the upper-left corner of Studio. A popup list contains the names of all the workspaces that you can access. Select a workspace to switch to it. Each workspace has its own projects, members, and billing. ### Can I change my workspace name? Yes. Go to **Settings** > **Workspace** > **General** and update the workspace name. This is a display name only and does not affect your workspace URL or project data. ### Where do I find my projects? After signing in, Studio opens to your **Dashboard**. All projects in your current workspace are listed here. Use the search bar to filter by name, or use the sidebar to browse by category. ### How do I create a new agent? 1. Open an existing project or create a new one from the Dashboard by clicking **New project**. 2. Inside the project, click **New agent**. 3. Choose a starting point: **Blank agent**, **From template**, or use the **Project Wizard** to generate an agent from a natural-language description. 4. Give your agent a name and click **Create**. ### How do I open the agent editor? Click on any agent name within a project to open it. Agents without a FLOW section open in a full-page configuration panel. Agents with a FLOW section open in the visual flow editor with a configuration sidebar. You can also edit the raw ABL definition by clicking **Code view** in the editor toolbar. ### What execution modes does an agent support? Every agent reasons by default -- you provide instructions, tools, and guardrails, and the LLM decides what to do at each turn. Optionally, you can add a `FLOW` section with structured steps to control the exact order of operations, branching, and data collection. Within each step, the `REASONING: true/false` flag controls whether the LLM reasons at that step. Use the default reasoning mode for flexible, conversational tasks. Add a FLOW section when you need predictable, repeatable workflows. You can also combine both: use a FLOW for the main path and enable `REASONING: true` on specific steps that benefit from LLM flexibility. ### How do I add my LLM API key? Go to **Settings > AI Configuration > LLM Providers** and click **Add provider**. Select the provider (for example. OpenAI, Anthropic, Azure OpenAI, Google Gemini), paste your API key, and click **Validate and save**. The platform will verify that the key works before saving it. ### Which LLM models are supported? Agent Platform supports models from: * **OpenAI** -- GPT-4o, GPT-4o mini, o1, o3, and other chat completions models. * **Anthropic** -- Claude Opus, Claude Sonnet, Claude Haiku. * **Google** -- Gemini 2.5 Pro, Gemini 2.5 Flash, Gemini 2.0 Flash. * **Azure OpenAI** -- Any model deployed on your Azure OpenAI resource. * **Custom endpoints** -- Any OpenAI-compatible API via a custom base URL. The available models update as providers release new versions. Check **Settings > AI Configuration** for the current list. ### How do I change the default model for my agents? Go to **Settings > AI Configuration > Model assignments**. Models are assigned to tiers: **Powerful**, **Balanced**, and **Fast**. Each agent definition specifies which tier it uses (default is Balanced). Change the model assigned to a tier and all agents using that tier will pick up the new model. You can also override the model for a specific agent in the agent's configuration panel under **Model settings**. ### My API key validation fails -- what should I check? 1. Copy the API key directly from the provider's dashboard -- avoid extra whitespace. 2. For Azure OpenAI, confirm the endpoint URL includes the correct deployment name and API version. 3. Confirm your API key has not expired and has sufficient quota or credits. 4. If your organization uses a network proxy or firewall, ensure outbound HTTPS traffic to the provider (for example. `api.openai.com`, `api.anthropic.com`) is allowed. ### My agent does not respond when I send a message 1. **Check LLM configuration.** Go to **Settings > AI Configuration > LLM Providers** and verify that at least one credential is active and validated. 2. **Check model assignment.** Ensure a model is assigned to the tier your agent uses (default: Balanced). 3. **Check the trace.** Open the session trace in the Test panel to see where execution stopped. Common error codes: * `MODEL_NOT_FOUND` -- No model is configured for the requested tier. * `CREDENTIAL_INVALID` -- The API key is expired or incorrect. * `TIMEOUT` -- The LLM request timed out. Try again or switch to a faster model. ### How do I use the Test panel? Click the **Test** button (play icon) in the editor toolbar. This opens a chat panel where you can interact with your agent in real time. Each conversation creates a test session with a full execution trace. ### How do I read a trace? After sending a message in the Test panel, click the **Trace** tab to see the execution timeline. Each row represents a span: * **Agent span** -- The top-level execution of the agent. * **LLM span** -- A call to the language model, showing the prompt and response. * **Tool span** -- A tool invocation, showing input and output. * **Step span** -- An individual step in an agent's flow. Click any span to see its details, including timing, input/output data, and any errors. ### Where can I see my usage? Go to **Settings > Billing > Usage**. This page shows your current billing period, total sessions, LLM token consumption broken down by provider and model, and how much of your plan's quota you have used. ### What counts as a session? A session is a single conversation between a user and an agent, from the first message to the end of the conversation. Test sessions in the Studio Test panel also count toward your usage. Each session is billed regardless of how many messages it contains. ### How do I upgrade my plan? Go to **Settings > Billing > Plan** and click **Upgrade**. Compare available plans and select the one that fits your usage. Plan changes take effect immediately and are prorated for the current billing period. ### What happens if I exceed my plan limits? You will receive a warning email when you reach 80% of your session or token quota. If you exceed 100%, new agent sessions will be paused until you upgrade your plan or the next billing cycle begins. Existing active sessions are not interrupted. *** ## ABL language ### What is the difference between ABL and YAML? ABL is a purpose-built domain-specific language for defining agent behavior. While you can also define agents using YAML in the visual editor, ABL provides richer syntax for flow control (`GOTO`, `GATHER`, conditional transitions), a dedicated parser with descriptive error messages, IDE support (VS Code extension with syntax highlighting and diagnostics), and more concise declarations for complex multi-agent setups. YAML mode is available in the Studio visual editor and is convenient for simple agents. For advanced orchestration, ABL is recommended. ### My agent has a syntax error but I cannot find it Check for these common causes: 1. **Indentation errors.** ABL is indentation-sensitive. Use consistent indentation (2 or 4 spaces, no tabs). 2. **Missing keywords.** Every block requires its opening keyword (for example. `AGENT CustomerSupport`, not just `CustomerSupport`). 3. **Unclosed strings.** Multi-line strings must use the pipe (`|`) or triple-quote syntax. 4. **Use the Studio code editor,** which shows real-time parser diagnostics with line-specific error messages. ### When should I add a FLOW section to my agent? Choose based on the conversation pattern: | Approach | Use When | Example | | - | - | - | | **No FLOW (default)** | The agent needs to decide its own path based on context | Open-ended Q\&A, research assistant | | **With FLOW** | The conversation follows a predictable flow with defined steps | Order placement, form filling, onboarding | | **FLOW + REASONING: true** | Some steps are deterministic, others need LLM flexibility | Structured intake flow with reasoning-enabled steps for handling complex issues | ### How do expressions work in ABL? Expressions are used in conditions, variable interpolation, and computed values. **Variable interpolation** uses double curly braces in strings: `"Hello, {{user_name}}!"`. **Conditional transitions** use `IF` clauses on `GOTO` statements: ```yaml theme={null} GOTO premium_support IF user.tier == "premium" GOTO standard_support IF user.tier == "standard" GOTO fallback ``` **Common expression operators:** `==` (equal), `!=` (not equal), `>`, `<`, `>=`, `<=` (comparison), `AND`, `OR`, `NOT` (logical). ### Can I use ABL with the visual editor? Yes. The Studio visual editor works with both modes: **Code mode** for writing ABL directly with syntax highlighting and diagnostics, and **Visual mode** for using the drag-and-drop flow editor. Changes in one mode are reflected in the other, and you can switch between them at any time. ### What happens when ABL parsing fails at runtime? If the agent was working in Studio but fails when deployed, verify the agent compiles cleanly in Studio before deploying. Check for template variables that resolve at deploy time -- if a variable reference is invalid, parsing succeeds but execution fails. Common runtime parse errors include `UNKNOWN_STEP` (a `GOTO` references a step that does not exist), `DUPLICATE_STEP` (two steps have the same name), and `INVALID_EXPRESSION` (a condition has a syntax error). ### How do I define a multi-line instruction? Use the pipe character for block scalars or triple-quoted strings: ```yaml theme={null} AGENT Support INSTRUCTIONS """ You are a customer support agent for Acme Corp. Guidelines: - Always verify the customer's identity before making account changes. - Escalate billing disputes over $500 to a supervisor. - Be empathetic but efficient. """ ``` Single-line strings use regular quotes: `DESCRIPTION "Handles customer support inquiries"`. ### How do I add comments to ABL? Use `#` for line comments. ABL does not support block comments. Use multiple `#` lines instead. ```yaml theme={null} # This agent handles onboarding AGENT Onboarding DESCRIPTION "Guides new users through setup" FLOW # First step: collect the user's name STEP welcome SAY "Welcome! What is your name?" GATHER user_name GOTO setup ``` *** ## Agent development ### My agent is not using tools correctly 1. **Verify tools are bound to the agent.** Tools must be declared in the agent definition with `TOOLS [order_lookup, ticket_create, knowledge_search]`. 2. **Check tool descriptions.** The LLM uses tool descriptions to decide when and how to call them. Vague descriptions like "Looks up stuff" lead to poor tool selection. Write specific, actionable descriptions. 3. **Check tool parameters.** Ensure parameter names, types, and descriptions are clear so the LLM knows what to pass. 4. **Check the trace.** Open the session trace to see tool selection decisions. If the agent calls the wrong tool, refine its instructions or tool descriptions. ### GATHER is not collecting user input 1. **Verify the GATHER syntax.** GATHER needs a variable name to store the collected value. 2. **Check for missing SAY before GATHER.** The agent must prompt the user before gathering input. 3. **Validate the collected value.** Use constraints to ensure the input meets requirements (for example. `CONSTRAINT email "Please provide a valid email address"`). 4. **Check field types.** Ensure the field type matches what you expect the user to provide. 5. **Look at the trace** to see if the agent asked the user for input. If the GATHER step was skipped, check the transition conditions leading into it. ### Flow transitions are not working as expected 1. **Check conditional order.** Conditions are evaluated top to bottom. The first matching condition wins, so check specific conditions first. 2. **Check for missing default transitions.** If no condition matches and there is no unconditional `GOTO`, the flow stalls. Always provide a default. 3. **Check step names.** `GOTO` targets must match step names exactly (case-sensitive). 4. **Avoid circular references** without exit conditions. If steps form a loop, ensure at least one path breaks out. ### How does agent memory work? The Agent Platform provides multiple memory scopes: | Scope | Lifetime | Use Case | | - | - | - | | **Session** | Current conversation | Turn-by-turn context, gathered values | | **User** | Across sessions for the same user | Preferences, past interactions | | **Project** | Shared across all sessions in a project | Lookup data, shared configuration | Reference session variables directly in ABL with `{{user_name}}`. Memory size is bounded by the session configuration, and large payloads should be stored as attachments. ### How do I use constraints for input validation? Constraints validate user input during GATHER steps. If validation fails, the agent re-prompts with the error message. Built-in types include `email`, `phone`, `number`, `range(min, max)`, and `one_of("option1", "option2")`. For complex validation, use `llm_validate` with a natural-language description of the expected input format. ### How do I handle errors in my agent? Use error handling at the flow level with `ON_ERROR GOTO error_handler` on tool calls. For tool-level errors, the agent receives the error in its context and can decide how to proceed. Agents without a FLOW section handle errors automatically through LLM reasoning. Agents with a FLOW section need explicit `ON_ERROR` transitions to define error recovery paths. ### How do I pass context between agents in a multi-agent setup? When a supervisor delegates to a sub-agent, context is passed through the handoff. The delegated agent receives the current message, the conversation history (configurable depth), and any session variables set by the supervisor. Configure routing instructions to specify what context should be included for each specialist agent. ### My agent's responses are too long or too short Add specific instructions about response style in the agent's `INSTRUCTIONS` block (for example. "Keep responses under 3 paragraphs unless the user asks for detail"). For more control, use `MAX_RESPONSE_TOKENS` and `TEMPERATURE` settings in the agent configuration. ### My agent gives wrong or unexpected answers 1. **Review your instructions.** Vague instructions produce vague answers. Be specific about what the agent should and should not do. 2. **Check the model.** More capable models (for example. Claude Sonnet, GPT-4o) follow complex instructions better than smaller models. 3. **Add guardrails.** Use `GUARDRAILS` to define boundaries for the agent's behavior. 4. **Use the trace.** The trace shows the full prompt sent to the LLM, including system instructions, conversation history, and tool results. Review it to understand why the model made a particular decision. ### My agent's tools are not working 1. Verify the tool definition in your agent has no compilation errors (check the **Problems** panel in the editor). 2. Open the session trace and expand the tool call span. The trace shows the input the agent sent to the tool and the output (or error) returned. 3. For HTTP tools, confirm the target URL is reachable and returns the expected response format. 4. For custom code tools, check that the code does not throw an unhandled exception -- the trace will show the stack trace. ### How do I debug agent behavior? 1. **Start with the trace.** It shows exactly what happened, in what order, and what data flowed between steps. 2. **Check the LLM prompt.** Expand an LLM span to see the full prompt. If the instructions are unclear, the model's response will be too. 3. **Check tool outputs.** If a tool returned unexpected data, the agent's next decision will be based on that bad data. 4. **Use multiple test sessions.** Try different user inputs to see how the agent handles edge cases. The Test panel preserves session history so you can compare runs. *** ## Tools and integrations ### Can I call an external API at conversation startup, without using a regular tool call, to load context? Yes — using ABL `CALL`, specifically a top-level `ON_START`. This is the supported pattern for fetching context before the agent responds. There isn't a way to call an external API that fully bypasses the tool execution pipeline; `CALL` still runs through the runtime's tool executor under the hood. See [External API Call on Start](/agent-platform/faq/external-api-call-on-start). ### My HTTP tool returns a connection error If the tool call fails with `ECONNREFUSED`, `ENOTFOUND`, or `ETIMEDOUT`, try the following troubleshooting steps: 1. **Verify the endpoint URL.** Check for typos, missing protocol (`https://`), and correct port. 2. **Check allowed domains.** If `allowedServiceDomains` is configured in your workspace security settings, the target domain must be in the list. 3. **Check timeout.** The default service timeout is 30 seconds. For slow APIs, increase the tool-level timeout in the tool definition. If the tool returns a 401 unauthorized error, then try the following: 1. **Check authentication configuration.** Verify the API key or token is set correctly in the tool definition or project secrets. 2. **For OAuth 2.0:** Verify the OAuth connection is configured and the token has not expired. Go to **Project > Settings > Auth Profiles** to check credential status. 3. **Check the API key scope.** Some APIs require specific scopes or permissions for each endpoint. ### OAuth token refresh fails 1. **Verify the refresh token is present.** If the OAuth provider don't issue a refresh token during the initial authorization, re-authorize with the `offline_access` scope. 2. **Check the OAuth redirect URI.** The configured redirect URI must exactly match what is registered with the OAuth provider (including trailing slashes). 3. **Check token expiration.** Go to **Project > Settings > Integrations** and check the OAuth connection status. If it shows "Expired," re-authorize. 4. **Verify the OAuth provider configuration:** Client ID, client secret, token endpoint URL, and required scopes must all be correct. ### Code tool execution times out If a Python or JavaScript code tool fails with `TIMEOUT` or `EXECUTION_TIMEOUT`: 1. **Check the timeout setting.** The default sandbox timeout is 60 seconds. For compute-intensive tasks, increase it in the tool definition. 2. **Optimize the code.** Common causes of slow execution include large data processing without streaming, synchronous HTTP calls inside the sandbox, and unbounded loops. 3. **Check sandbox resource limits.** The sandbox has memory and CPU constraints. Very large datasets may need to be processed outside the sandbox. ### MCP server connection fails If connecting to an MCP (Model Context Protocol) server fails with "Connection refused" or "Timeout": 1. **Verify the MCP server URL.** The URL must be reachable from the runtime. 2. **Check the transport type.** MCP supports HTTP/SSE and Stdio transports. 3. **Check authentication.** If the MCP server requires authentication, configure the credentials in the tool connection settings. 4. **Check for CORS issues** When connecting from the browser, MCP servers must allow the platform's origin. 5. **Verify tool discovery.** After connecting, the platform queries the MCP server for available tools. If tools are not appearing, check the MCP server logs for errors. ### Tool call returns unexpected results 1. **Check the tool response format.** The agent receives the tool's response as structured data. If the response shape changed, the agent may misinterpret it. 2. **Add response processing instructions** in the tool description so the agent knows how to interpret the returned fields. 3. **Check the trace.** Open the session trace and expand the tool call to see the raw request and response. 4. **Test the tool independently.** Use **Project > Tools > Test** to call the tool with specific parameters and inspect the response. ### How do I store tool credentials securely? Store sensitive values such as API keys, access tokens, and credentials as **Config Variables** with the type set to Secret. Secrets are encrypted and referenced by name in your tool configuration. 1. Go to **Project** > **Settings** > **Config Variables**. 2. Click **Add Variable** and enter a name (for example. `WEATHER_API_KEY`) , set the type as secret and add the value. 3. Reference the secret in your tool with `{{config.WEATHER_API_KEY}}`. All credentials are encrypted at rest and never exposed in traces or logs. ### How do I limit which domains tools can call? Configure the allowed service domains in your workspace security settings at **Settings > Security > Allowed service domains**. Add the domains your tools need to access. When this list is non-empty, tool HTTP calls to any domain not in the list are blocked. An empty list allows all domains. ### My tool works in testing but fails during agent execution 1. **Check session context.** During agent execution, tool parameters may be populated from session variables. If a variable is missing or has an unexpected value, the tool call fails. 2. **Check parameter types.** The agent may pass a string where the API expects a number. Ensure proper types in the tool definition. 3. **Check concurrent call limits.** If your agent calls many tools simultaneously, you may hit the `maxConcurrentServiceCalls` limit (default: 10). *** ## Multi-agent ### Context is lost during agent handoff 1. **Check routing instructions.** The supervisor's routing instructions control what context is passed during handoff. Be explicit about what to include for each specialist. 2. **Check conversation history depth.** By default, the sub-agent receives recent conversation history. If the conversation is long, important early context may be outside the window. 3. **Use session variables** for critical context that must persist across handoffs. 4. **Check the trace.** Open the session trace and inspect the handoff event to see exactly what context was passed to the sub-agent. ### Delegation times out 1. **Check the sub-agent's configuration.** If the sub-agent uses tools that take a long time, the delegation may exceed the timeout. Ensure `maxServiceTimeoutMs` is sufficient. 2. **Check for infinite loops.** If the sub-agent's flow loops without reaching a terminal state, it never returns a response. 3. **Check LLM availability.** If the sub-agent uses a different model tier than the supervisor, that model may be unavailable or rate-limited. 4. **Check the trace.** The trace shows the delegation start time, sub-agent execution, and where the timeout occurred. ### The supervisor routes to the wrong agent 1. **Improve agent descriptions.** The supervisor uses agent descriptions to decide routing. Avoid overlapping descriptions; make each agent's domain distinct. 2. **Refine routing instructions.** Add explicit routing rules with keyword-to-agent mappings and examples. 3. **Use a faster model for routing.** The `coordination` operation type can use the **Fast** tier for quicker, more focused routing decisions at **Project > Settings > LLM Configuration**. ### How do I implement fan-out (parallel agent execution)? Fan-out sends the same request to multiple agents in parallel and aggregates their responses. Configure the supervisor with `ORCHESTRATION fan_out` and list the agents to invoke. The supervisor waits for all agents to respond, then combines the results. Note that all agents must respond within the delegation timeout, fan-out multiplies LLM usage, and if one agent fails, the supervisor receives partial results. ### How do I implement agent chaining (sequential delegation)? Chain agents by having the supervisor route sequentially through routing instructions that specify the order. Each agent in the chain receives the output of the previous agent as part of its context. ### How do I handle escalation to a human? Use the `ESCALATE` action to transfer the conversation to a human agent. Provide a `REASON`, optional `QUEUE` name, and `CONTEXT` with relevant session data. The escalation pauses the automated agent, sends conversation context to the configured escalation channel (Genesys, Zendesk, or your contact center), and optionally keeps the session open for post-transfer agent assistance. ### Can sub-agents use different models? Yes. Each agent can have its own model configuration set in the agent definition or agent settings. Different operations (tool selection, response generation) can use different tiers. The model resolution chain applies independently to each agent in the multi-agent setup. ### My supervisor sometimes responds directly instead of delegating 1. **Add explicit delegation instructions** telling the supervisor to NEVER answer user questions directly and to always delegate to the appropriate specialist. 2. **Reduce the supervisor's context.** If the supervisor has too much knowledge, the LLM may decide it can answer directly. Keep the supervisor's instructions focused on routing. 3. **Use the coordination tier.** Set the `coordination` operation to use a **Fast** model, which tends to follow routing instructions more strictly. *** ## Publishing and deployment ### How do I publish an agent? 1. Open your project in **Studio** and navigate to the agent you want to publish. 2. Click the **Publish** button in the top-right corner of the editor. 3. Select the target environment (Development, Staging, or Production). 4. Review the summary of changes since the last publish. 5. Click **Confirm** to publish. The agent becomes live on all channels connected to that environment within a few seconds. ### How do I roll back to a previous agent version? Go to **Project Settings > Versions**, find the version you want to restore, click the **three-dot menu**, and select **Publish this version**. Choose the target environment and confirm. Rollbacks take effect within seconds. Active conversations in progress complete using the version they started with; new conversations use the rolled-back version. ### What channels are available? Agent Platform supports the following channels out of the box: | Channel | Setup Location | Notes | | - | - | - | | **Web Widget** | Project Settings > Channels > Web | Embed a chat widget on any website | | **REST API** | Project Settings > Channels > API | Programmatic access for custom integrations | | **Slack** | Project Settings > Channels > Slack | Requires Slack app installation in your workspace | | **WhatsApp** | Project Settings > Channels > WhatsApp | Requires a WhatsApp Business account | | **Microsoft Teams** | Project Settings > Channels > Teams | Requires Azure Bot registration | | **SMS** | Project Settings > Channels > SMS | Requires a Twilio or compatible provider account | Each channel can be bound to a specific environment so you can test integrations without affecting live users. ### How do I add the Web Widget to my website? Go to **Project Settings > Channels > Web**, click **Create Web Channel**, configure appearance options, and copy the embed snippet. Paste the snippet into your website's HTML just before the closing `` tag. The widget appears immediately. You can customize the widget's theme, position, launcher icon, and behavior from the channel settings page -- changes apply without updating the embed code. ### How do I connect Slack? Go to **Project Settings > Channels > Slack**, click **Connect Slack**, follow the OAuth flow to install the ABL bot into your Slack workspace, select which channels the agent should listen in, choose the target environment, and save. You can configure whether the agent responds to all messages or only when mentioned with `@`. ### How do I connect WhatsApp? Go to **Project Settings > Channels > WhatsApp**, enter your WhatsApp Business API credentials, configure the webhook URL in your WhatsApp Business account, select the target environment, and save. You need an approved WhatsApp Business account through Meta. ### How do I use the REST API channel? Go to **Project Settings > Channels > API**, click **Create API Channel**, and copy the generated API Key. Use the key to authenticate requests to `https://agents.kore.ai/api/v1/conversations`. Full API reference documentation is available at **Project Settings > Channels > API > View API Docs**. ### What are the different environments? Agent Platform provides three environments for each project: | Environment | Purpose | | - | - | | **Development** | Build and test your agent. Only accessible to project members through Studio's built-in test chat. | | **Staging** | Pre-production validation. Connect test channels here to verify behavior before going live. | | **Production** | Live traffic. End users interact with agents through channels connected to this environment. | Each environment maintains its own published agent version, channel connections, and conversation history. ### How do I promote an agent between environments? Publish and test your agent in Development, then go to **Project Settings > Versions**, find the version, click the three-dot menu, and select **Promote to Staging**. After testing in Staging, repeat and select **Promote to Production**. You can also skip Staging and promote directly to Production. ### How do agent versions work? Every time you publish an agent, Agent Platform creates an immutable version snapshot that includes the agent definition, knowledge base contents, and connected tool configurations. **Drafts** are the working copy in the editor (edits do not affect live environments). **Published versions** are immutable snapshots deployed to a specific environment. Versions are auto-numbered sequentially and retained indefinitely. ### How do I check if my agent is healthy? The **Project Dashboard** shows a status indicator (green/yellow/red), active conversations, messages processed, and average response time. For detailed metrics, go to **Project Settings > Monitoring** for error rate, latency percentiles (p50/p95/p99), tool execution success rates, and LLM token usage. ### Where do I see error rates and logs? Go to **Project Settings > Monitoring > Errors** to see a timeline of errors grouped by type. You can filter by environment, date range, and error type. Click any error to see the full conversation trace including the agent's reasoning steps and tool calls leading up to the failure. ### How do I set up alerts? Go to **Project Settings > Monitoring > Alerts**, click **Create Alert**, and configure the metric (error rate, latency, or conversation volume), threshold, evaluation window, and notification channel (email, Slack webhook, or PagerDuty). Alerts evaluate continuously and can be muted temporarily during planned maintenance. ### Does Agent Platform auto-scale? Yes. As a SaaS platform, Agent Platform handles scaling automatically. Conversation handling, LLM requests, and knowledge base indexing all scale based on demand. You do not need to provision servers, configure load balancers, or manage infrastructure. ### What about self-hosted deployment? Agent Platform offers an enterprise self-hosted option for organizations that require deploying the platform within their own infrastructure. This is available on Enterprise plans and includes dedicated support for installation, configuration, and ongoing operations. For details, see [Billing, quotas, and operations](/agent-platform/administration/workspace-account). *** ## Security ### How do I add guardrails to my agent? Guardrails protect against harmful, off-topic, or non-compliant agent behavior. The platform supports **input guardrails** (check user messages before the agent processes them) and **output guardrails** (check agent responses before delivery). Configure input guardrails with `BLOCK_TOPICS`, `DETECT_PII`, and `MAX_INPUT_LENGTH`. Configure output guardrails with `BLOCK_PATTERNS`, `REQUIRE_CITATION`, and `TONE_CHECK`. For workspace-wide guardrails, configure policies at **Settings > Security > Guardrail Policies** with enforcement modes: **Block** (reject), **Warn** (log only), or **Redact** (remove violating content and continue). ### How does PII detection and redaction work? The platform includes built-in PII detection for email addresses, phone numbers, Social Security numbers, credit card numbers, IP addresses, and custom patterns. In traces, PII is replaced with `[REDACTED:type]`. In analytics, PII is stripped before storage. In conversations, PII flows through the agent normally -- detection is for logging and storage, not blocking. Use input guardrails to block messages containing specific PII types. Add custom PII patterns at **Settings > Security > PII Patterns**. ### How do I rotate API keys? For workspace API keys: go to **Settings > API Keys**, create a new key, update your applications, deactivate the old key after a transition period, then delete it. For LLM provider credentials: go to **Settings > AI Configuration > LLM Providers**, add a new credential, update models to use it, then deactivate the old one. The `apiKeyMaxAgeDays` setting (default: 90 days) enforces maximum key age with administrator alerts. ### How does tenant isolation work? The Agent Platform enforces strict multi-tenant isolation at every layer: * **Data isolation:** Every database query includes `tenantId`. Cross-tenant access returns 404 (not 403) to prevent existence leaking. Encryption keys are per-workspace. * **Runtime isolation:** Sessions are scoped to `tenantId`. Redis keys include the tenant prefix. BullMQ jobs include `tenantId` and are validated before processing. * **API isolation:** JWT tokens include `tenantId` baked in from authentication. The runtime reads the tenant from the JWT, not from request headers. API keys are scoped to a workspace. ### How do I restrict outbound network access? Go to **Settings > Security** and configure **Allowed service domains** with the domains your tools need to access. When this list is non-empty, any HTTP tool call to a domain not in the list is blocked. You can use wildcard patterns (for example. `*.internal.example.com`). ### How do I audit who did what? The platform records audit logs for user login/logout, API key management, credential changes, agent deployments, configuration changes, and channel connection changes. Access audit logs at **Settings > Security > Audit Logs** or via the API. Business and Enterprise plans support exporting audit logs to external SIEM systems. Retention is configurable via `auditLogRetentionDays` (default: 90 days). ### How do I enable SSO? SSO is available on Business and Enterprise plans and supports **SAML 2.0** (Okta, Azure AD, OneLogin, PingOne) and **OIDC** (Google Workspace, Azure AD, Auth0). Go to **Settings > Security > Authentication**, click **Configure SSO**, select the protocol, enter the provider configuration, configure attribute mapping, and test the connection before enabling. ### How do I enable MFA? Go to **Settings > Security > Authentication** and enable **Multi-factor authentication**. Choose enforcement: **Optional** (members can enable in their profile) or **Required** (all members must enable at next login). Supported methods: TOTP (authenticator app) and WebAuthn (hardware key). ### My agent leaks internal information 1. **Add output guardrails** to block patterns that match internal information. 2. **Restrict tool output.** If tools return raw internal data, add post-processing to strip sensitive fields before the agent sees them. 3. **Refine agent instructions** to explicitly prohibit revealing internal system details, API endpoints, database names, or internal error messages. 4. **Enable PII redaction** to prevent sensitive data from appearing in traces and analytics. *** ## Workspace administration ### What roles are available? | Role | Description | Key Permissions | | - | - | - | | **Owner** | Full workspace control | All permissions, manage billing, delete workspace | | **Admin** | Workspace administration | Manage members, configure models, manage channels | | **Operator** | Production operations | Monitor sessions, analytics, and deployments | | **Member** | Agent development | Create/edit agents, run tests, manage resources | | **Viewer** | Read-only access | View agents, sessions, analytics | Assign roles at **Settings > Team > Members**. Projects also have fine-grained permissions including `agent:read`, `workflow:*`, `channel:create`, `analytics:read`, and `deployment:read`. ### How do I invite team members? Go to **Settings > Team > Members**, click **Invite member**, enter the email address, select a role, and send the invitation. Invitations expire after 7 days. Project access is added separately from **Settings > Members** using existing workspace members. ### How do I set up model configuration? Quick setup: add credentials at **Settings > AI Configuration > LLM Providers**, register models at **Settings > AI Configuration > Models**, assign each model to a tier (Fast, Balanced, Powerful, Voice), and set defaults. See [AI model configuration](/agent-platform/administration/ai-configuration) for complete details. ### How do I control which providers my team can use? Use tenant LLM policies at **Settings > AI Configuration > Policies**. Configure a provider allowlist, model allowlist, and token budgets per project or per user. Policies apply to all projects in the workspace and can't be overridden at the project level. ### How does billing work? | Plan | Billing Model | | - | - | | **Free** | No charge, usage limits apply | | **Team** | Per-seat monthly subscription | | **Business** | Per-seat + usage-based (tokens, messages) | | **Enterprise** | Custom pricing, annual contract | View usage at **Settings > Usage**. Upgrade at **Settings > Billing > Upgrade plan**. ### How do I manage environment variables for my project? Go to **Project > Settings > Environment Variables**, add variables with scope (all environments or specific environment), and reference them in ABL with `{{env.VARIABLE_NAME}}`. For sensitive values (API keys, tokens), use **Auth Profiles** or mark the environment variable as secret -- both are encrypted at rest. ### How do I manage deployments? Create deployments at **Project > Deployments** with a description and target environment. Promote tested deployments to higher environments. Roll back by selecting a previous deployment and clicking **Rollback to this version**. ### How do I export data from my workspace? * **Agent definitions:** Export ABL files from **Project > Agents > Export**. * **Conversations and sessions:** Export from **Project > Sessions > Export** or via the API. * **Analytics:** Export from **Project > Analytics > Export** (Business and Enterprise plans). * **Full workspace export:** Available on Enterprise plans -- contact your account team. ### How do I delete a project? Click the three-dot menu on the project card, select **Delete project**, type the project name to confirm, and click **Delete permanently**. This action is permanent and removes all agents, sessions, channel connections, and analytics data. ### How do I transfer workspace ownership? Go to **Settings > Team > Members**, find the new owner, change their role to **Owner**, and confirm. The previous owner is downgraded to Admin. A workspace must always have exactly one owner. *** ## Troubleshooting ### How do I read an execution trace? Every agent execution generates a trace -- a structured record of what happened, in what order, and how long each step took. Access traces in Studio at **Project > Sessions** or via the API. A trace contains a tree of spans: | Span Type | Description | | - | - | | `session.message` | Top-level span for a user message | | `agent.execute` | Agent execution (with or without flow) | | `llm.call` | LLM API call with model, tokens, latency | | `tool.call` | Tool invocation with parameters and result | | `channel.send` | Outbound message delivery | | `guardrail.check` | Guardrail evaluation | | `handoff` | Agent-to-agent delegation | ### What should I look for in a trace? | Problem | Where to Check | | - | - | | Slow response | Look at `llm.call` durations and `tool.call` durations | | Wrong tool selected | Expand `llm.call` to see the tool selection reasoning | | Tool error | Expand `tool.call` to see the error message and response | | Wrong agent routing | Check `handoff` spans for routing decisions | | Guardrail block | Check `guardrail.check` spans for blocked content | | Missing response | Look for errors in `agent.execute` or `channel.send` | ### How do I debug tool calls? Each tool call in the trace includes input parameters, the raw HTTP request and response (for HTTP tools), duration, and any errors. Common tool errors: * **`TOOL_TIMEOUT`** -- Increase the tool timeout or optimize the external API. * **`ECONNREFUSED`** -- Verify the endpoint URL and network connectivity. Check `allowedServiceDomains`. * **`HTTP 401`** -- Check the API key or OAuth token. Verify the credential is active in Auth Profiles. * **`INVALID_TOOL_RESPONSE`** -- The API may be returning an error page. Check the URL and verify the API is operational. Test tools independently at **Project > Tools > Test** to isolate issues from the LLM. ### How do I resolve LLM errors? * **`MODEL_NOT_FOUND`** -- Register a model for the requested tier at **Settings > AI Configuration > Models**. * **`CREDENTIAL_INVALID`** -- Go to LLM Providers, click Validate, and replace the key if expired. * **`RATE_LIMITED` (429)** -- Check your provider's rate limits, configure fallback models, or reduce the model tier for high-volume operations. * **`CONTEXT_OVERFLOW`** -- Use a model with a larger context window, reduce conversation history depth, or summarize older messages. * **`LLM_TIMEOUT`** -- Increase `llm.timeoutMs`, use a faster model, or check the provider's status page. ### How do I inspect session state? Go to **Project > Sessions** and click a session to see messages (full conversation history), variables (current session variable values), trace (execution timeline), and metadata (agent, deployment, channel, and timing information). ### What do the session states mean? | State | Description | | - | - | | `active` | Session is open and processing messages | | `idle` | Session is waiting for user input | | `expired` | Session exceeded `sessionIdleSeconds` or `sessionMaxAgeSeconds` | | `ended` | Session was explicitly ended by the agent or user | | `error` | Session encountered an unrecoverable error | ### Why does my session show "not found"? The `SESSION_NOT_FOUND` error has three common causes: the session expired and was cleaned up (check `sessionIdleSeconds`), the JWT tenant does not match the session's tenant, or the session was created in a different deployment environment. ### My session state is stuck If the session shows as "active" but does not respond to messages, check the trace for a hanging `tool.call` or `llm.call`. The session cleanup job will eventually timeout and close it. For immediate resolution, end the session from the **Project > Sessions** page. ### What are the common error codes? **Agent execution errors:** | Code | Resolution | | - | - | | `AGENT_NOT_FOUND` | Verify the agent exists and is deployed | | `COMPILE_ERROR` | Fix syntax errors in the agent definition | | `EXECUTION_ERROR` | Check the trace for the specific error | | `MODEL_NOT_FOUND` | Register a model for the tier | | `CREDENTIAL_INVALID` | Replace or revalidate the credential | | `CONTEXT_OVERFLOW` | Use a larger model or reduce history | **Tool errors:** | Code | Resolution | | - | - | | `TOOL_TIMEOUT` | Increase timeout or optimize the tool | | `TOOL_ERROR` | Check the tool's response in the trace | | `TOOL_NOT_FOUND` | Verify the tool is defined and bound to the agent | | `SANDBOX_TIMEOUT` | Optimize code or increase sandbox timeout | | `DOMAIN_BLOCKED` | Add the domain to `allowedServiceDomains` | **Channel errors:** | Code | Resolution | | - | - | | `CHANNEL_NOT_CONFIGURED` | Create a channel connection for the channel type | | `SIGNATURE_INVALID` | Check channel credentials (signing secret, auth token) | | `DELIVERY_FAILED` | Check channel API status and credentials | **Session errors:** | Code | Resolution | | - | - | | `SESSION_NOT_FOUND` | Create a new session or check timeouts | | `SESSION_LIMIT` | Upgrade plan or close idle sessions | | `RATE_LIMIT_EXCEEDED` | Wait for the rate limit window to reset | **Authentication errors:** | Code | Resolution | | - | - | | `UNAUTHORIZED` | Provide a valid JWT or API key | | `FORBIDDEN` | Check role and permission assignments | | `TOKEN_EXPIRED` | Refresh the token | ### How do I check the platform health and status? Visit [status.kore.com](https://status.kore.com/) for real-time service status, uptime metrics, active incident details, and scheduled maintenance windows. Subscribe to status updates via email, RSS, or webhook. ### How do I increase log verbosity for debugging? Set `observability.loggingLevel` to `debug`, reproduce the issue, search logs for the session ID or error code, and then return the log level to `info`. In production, use trace sampling (`observability.traceSamplingRate`, default `1.0`) to control how many traces are stored -- set to `0.1` or `0.01` for high-traffic environments and use error-based sampling to capture all error traces. ## Voice configurations ### How do I set the language for a voice agent? For Pipeline Voice, configure the **Language** field in the voice channel settings under **Speech Recognition**. For Realtime LLM Voice, a separate language configuration is not required. The S2S model automatically detects the language from the caller’s speech. ### Does Artemis support keypad or DTMF input during voice calls? Yes. Artemis supports DTMF for pipeline voice agents through the Kore Voice Gateway. It can collect keypress input from callers like PINs, or account numbers, and send DTMF tones into an active call to navigate to external IVRs like banking or booking systems. [Learn More](/agent-platform/dtmf-support). # External API call at conversation startup Source: https://koreai.mintlify.app/agent-platform/faq/external-api-call-on-start [Back to FAQs](/agent-platform/faq#tools-and-integrations)

Can I invoke an external API at conversation startup or immediately after the welcome message, without using a regular tool call?

**Use Case:** Retrieve data based on custom metadata or SIP headers and make it available in context, so the agent can reference it throughout the conversation. ## Answer Yes. Use a top-level `ON_START` `CALL` in ABL. This is the supported pattern to fetch context before the agent responds. You cannot fully bypass the tool execution pipeline. A `CALL` still runs through the runtime's tool executor. ### Recommended pattern ```yaml theme={null} ON_START: CALL: lookup_context WITH: metadata: _metadata AS: startupContext RESPOND: "Welcome..." ``` In this pattern: * `lookup_context` is a declared or bound tool, typically a read-only HTTP tool, that performs the external API call. * `AS: startupContext` captures the result and writes it to session state, so it stays available for the rest of the conversation. * The call is deterministic and startup-triggered because it runs in `ON_START`. Unlike a normal in-conversation tool call, it is not an LLM decision. ### How it works The startup sequence works as follows: 1. On startup, the platform captures `sessionMetadata` from the URL and from `session:new.data.sessionMetadata` or `session_metadata`, then passes it into `createSessionFromResolved(... metadata ...)`. The platform stores this under `session.data.values._metadata`. 2. SIP headers are projected and allowlisted separately, for example `x-custom-data`, `user-to-user`, and `x-contact-id`. The platform applies them into `_metadata.sip` before the greeting and init path runs. 3. `initializeSession()` runs the startup lifecycle, which includes `ON_START`. 4. Within `ON_START`, the execution order is fixed: `SET` → `CALL` → `RESPOND`. Because of this order, a top-level `ON_START` `CALL` runs before the welcome message is sent, which meets the "before welcome" requirement. To fetch data slightly later, an initial flow step can also run a `CALL` after startup and welcome. That step is also backed by the tool executor. ### Caveat `CALL` is an ABL construct, but the runtime still executes it as a bound tool invocation through the tool I/O pipeline. This means: * You **can** make the call deterministic and startup-triggered instead of LLM-decided. * You **cannot** fully avoid the tool execution path. A declared or bound tool, typically a read-only HTTP tool, is still required. This pattern removes the overhead of an LLM deciding to call a tool, but not the tool-invocation mechanism itself. ## Summary | Requirement | Supported? | How | | - | - | - | | Fetch external data before the welcome message | Yes | Top-level `ON_START` → `CALL` → `RESPOND` | | Fetch external data after welcome, early in the flow | Yes | `CALL` in an initial flow step | | Make data available throughout the conversation | Yes | Capture with `AS: `; stored in session state | | Use custom metadata / SIP headers as input | Yes | Available via `_metadata` (and `_metadata.sip` for SIP headers) | | Fully bypass the tool execution pipeline | No | `CALL` always runs through the tool executor and requires a declared/bound tool | *** # Create and manage feedback templates Source: https://koreai.mintlify.app/agent-platform/feedback-template Create, configure, and manage feedback templates in Studio. A feedback template defines a survey that your agents present to end users, so every agent that asks the same question produces comparable results. ## What's a feedback template A feedback template is a project-level definition of one survey. It holds a single question, a single rating control, and the supporting text end users see. It also defines whether the survey runs on voice calls, and whether it includes a follow-up question or a comment box. Because the template owns the question and the rating control, every agent that references it collects the same measurement. Agents reference the template by name; they don't redefine it. A template controls the following. | Element | What it defines | | - | - | | **Survey type** | The rating control and the scoring range. Fixed after creation. | | **Survey design** | The question, submit button, thank-you message, and scale labels. | | **Follow-up question** | An optional second question, asked only when a condition matches. | | **Comment box** | An optional free-text field shown alongside the rating. | | **Voice behavior** | Whether the survey runs on voice calls, and how the caller responds. | Creating a template doesn't start collecting feedback. To collect responses, you reference the template from an agent and deploy the project, or you submit responses through the public API. The [Feedback Insights](/agent-platform/analytics-insights/customer-voice#feedback-insights) page displays reports only after you create at least one template. ## Before you begin Before creating or managing feedback templates, confirm the following. * **Project access.** You must be signed in to Studio and have access to the project where you want the survey. * **Agent access.** To collect responses from a conversation, you need an agent you can edit and a project you can deploy. * **API key (optional).** To submit responses from an external system, you need a project API key with the feedback scopes. See [Collect responses from an external system](#collect-responses-from-an-external-system). ## Access the Feedback Templates page Open the required project, and in the sidebar, select **Resources** > **Feedback Templates**. The page lists every template in the project. Use the search bar to filter the list by name, and select a template to open it. ## Create a template Create a template using Studio's three-step wizard: 1. On the Feedback Templates page, click **Create Template**. 2. Complete [basic info](#step-1-enter-basic-information), [configuration](#step-2-design-the-survey), and [review](#step-3-review-and-create) steps below. 3. On the **Review** step, click **Create Template**. ### Step 1: Enter basic information Enter the survey details, then choose the survey type and the voice call behavior. | Field | Required | Description | | - | - | - | | **Name** | Yes | The name agents use to reference the survey. Studio references it verbatim in the agent's `FEEDBACK` block, so it must be unique within the project. | | **Description** | No | A short summary of what the survey measures. | Select a survey type. The type determines the available fields and the scoring range. | Survey type | Rating control | Use it to | | - | - | - | | **NPS** | Scale of 0 to 10 | Measure how likely users are to recommend the experience. | | **CSAT** | Five stars | Measure satisfaction with a specific interaction. | | **Like/Dislike** | Thumbs up or thumbs down | Capture a quick positive or negative signal. | | **Comment only** | None | Collect free text without asking for a rating. | You can't change the survey type after you create the template, because scores are only comparable within the same type. To ask on a different control, create a new template. Select the voice call behavior. Surveys don't run on voice calls by default, and this setting doesn't affect chat conversations. | Option | Behavior on a voice call | | - | - | | **Not on voice** | The platform skips the survey entirely. | | **Keypad** | The caller responds using keypad input. | | **Speech** | The caller responds by speaking. | Click **Next**. ### Step 2: Design the survey The Configuration step controls what end users see. A live preview on the right shows the survey as it renders, with separate **Text** and **Voice** tabs. Select a rating in the preview to see any conditional follow-up or comment box. Complete the **Survey design** fields. | Field | Required | Description | | - | - | - | | **Question** | Yes | The question end users answer. | | **Submit button** | No | The button label. Keep it to a few words so it fits the button. | | **Thank-you message** | No | The message that replaces the survey once an answer is sent. Keep it to one line. | | **Lowest value label** | No | The label at the low end of the rating scale, for example, Not likely. | | **Highest value label** | No | The label at the high end of the rating scale, for example, Very likely. | #### Add a follow-up question Turn on **Follow-up question** to ask for more context when a rating alone doesn't explain why — for example, after a low score. Follow-up answers are reported separately and never affect the main score. Add one or more conditions that determine when the follow-up appears. The platform asks only one follow-up question. It evaluates conditions from top to bottom, asks the question attached to the first match, and skips the rest. Put your most specific condition first. #### Add a comment box Turn on **Comment box** to collect free text alongside the rating. Set the **Prompt** and the **Placeholder** text end users see. By default, the comment box appears after every rating. Add conditions to show it selectively. The platform checks every comment box condition when a score comes in, and shows the box if any condition matches. Order doesn't matter here. Click **Next**. ### Step 3: Review and create The Review step shows the live preview, the reference snippet, and a **What you are creating** summary of the name, question, rating control, voice behavior, follow-up, and comment box settings. Review the summary, then click **Create Template**. Saving a template doesn't publish the survey. To start collecting feedback, reference the template from an agent and deploy the project. ## Reference a template from an agent An agent presents a survey when its response includes a `FEEDBACK` block that references the template by name. Copy the snippet from the template's **Usage** tab and paste it into the agent's response block in the ABL editor. Where you place the block depends on how the agent is built. A flow-based agent attaches the survey to a specific step. A reasoning agent has no fixed step to attach to, so it attaches the survey to a `COMPLETE` branch. Both forms use the same `FEEDBACK` block. The `FEEDBACK_TEMPLATE` value must match the template **Name** verbatim. If the names don't match, the survey doesn't render. After you add the block, deploy the project to start collecting responses. ### Default rendering Everything the end user sees comes from the template. The block carries the reference and, optionally, the agent-owned guard. ```yaml theme={null} RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: Testing_feedback # the reference ``` ### Add a survey to a flow-based agent In a flow-based agent, add the `FEEDBACK` block to the step where you want to ask for feedback. The following agent greets the caller, moves to a dedicated `ask_feedback` step, and presents the survey there. ```yaml theme={null} AGENT: midflow_survey_test GOAL: "Verify a survey on a mid-flow step" PERSONA: "Calm and brief" FLOW: entry_point: greet steps: - greet - ask_feedback greet: REASONING: false RESPOND: "Hello, this is a survey test call. Please say anything to continue." ON_INPUT: - ELSE: THEN: ask_feedback ask_feedback: REASONING: false RESPOND: "Feel free to let us know how you feel" RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: test_npse THEN: COMPLETE ``` Note the following about this example. * The step's `RESPOND` text introduces the survey. The `RICH_CONTENT` block renders the survey itself below that message. * `THEN: COMPLETE` ends the conversation once the step runs, so the survey is the last thing the end user sees. * Placing the survey on its own step keeps the ask separate from the work the agent was doing. You can attach the block to any step instead, including one in the middle of a flow. ### Add a survey to a reasoning agent A reasoning agent decides its own path, so there's no fixed step to attach the survey to. Add the `FEEDBACK` block to a `COMPLETE` branch instead, and let the branch conditions decide when the agent asks. ```yaml theme={null} FLOW: steps: - execute_confirmed_action execute_confirmed_action: REASONING: true available_tools: - create_replacement - issue_refund - apply_goodwill_credit instruction: | Validate the action request first. Execute only the requested confirmed action. # instruction trimmed for brevity THEN: COMPLETE COMPLETE: - WHEN: status IS SET AND actions IS SET AND follow_up IS SET RESPOND: "" RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: Post_Chat_LikeDislike # the reference - WHEN: failures IS SET RESPOND: "" ``` Note the following about this example. * The agent evaluates `COMPLETE` branches in order and runs the first branch whose `WHEN` condition matches. * The first branch represents a successful outcome, so the survey attaches there. The agent asks for feedback only when it completed the work. * The failure branch carries no `FEEDBACK` block, so the agent doesn't ask for a rating on an interaction that failed. * `RESPOND: ""` leaves the response text empty, so the survey is the only thing the branch renders. The agent has already said what it needed to say during the reasoning step. Attach the survey to outcome branches rather than to the reasoning step itself. A reasoning step can run several times in one conversation, and each pass through a `FEEDBACK` block presents the survey again. ### Override wording Override any individual string on the agent side — for example, to supply another language or channel-specific phrasing using the agent's existing locale files. Anything you leave out still comes from the template. ```yaml theme={null} RICH_CONTENT: FEEDBACK: FEEDBACK_TEMPLATE: Testing_feedback prompt: "¿Qué probabilidad hay de que nos recomiende?" submit_label: "Enviar" # type, range and the conditions stay with the template ``` The response records which fields the agent overrode, so wording differences stay traceable rather than silent. Use the **Rendered language** segment on the [Feedback Insights](/agent-platform/analytics-insights/customer-voice#feedback-insights) page to compare scores across the languages your overrides produce. ## Edit a template Open a template from the Feedback Templates page and use the **Content** tab to change the survey design, the follow-up question, the comment box, and the voice call behavior. Click **Save** to apply your changes. The **Rating control** field shows the control you chose at creation; you can't change it here. The rating control is fixed after creation. Changing it would invalidate every score already collected against the survey. To ask on a different control, create a new template. ## Track where a template is used Open a template and select the **Usage** tab. The **Agents using this survey** panel lists every agent that currently triggers the survey. Click **Refresh** to re-check. If no agent references the template, the panel reports that no agent is linked — the survey isn't collecting feedback from conversations. ## Collect responses from an external system Use the public feedback API to submit responses from a host app with its own logic, an existing IVR, or a follow-up email. The **External submissions** panel on the **Usage** tab shows the request for the current template. ```json theme={null} POST /api/public/feedback/projects/ x-api-key: { "survey": "Testing_feedback", "session_id": "sess_01hL_", "rating": 10, "comment": "optional open text", "channel": "email", "response_source": "optional, your own capture source", "message_id": "optional, your own message/turn id" } ``` Note the following behavior. * The platform validates the payload against the template. It rejects a rating outside the template's range rather than clamping it. * The caller can't supply a score. You choose how the platform reads recorded ratings later, in Insights. * `session_id` is optional. The platform attributes the response later if the session isn't resolved yet. * Every response records its source, so external feedback counts toward the same number and stays separable from feedback captured at runtime. Create the API key under **Settings > API Keys** with the scopes you need. | Scope | Allows | | - | - | | `feedback.surveys.submit` | Submitting responses. | | `feedback.surveys.read` | Retrieving responses. | Retrieval returns comment text to any caller that holds the read scope. Issue the read scope only to systems that are allowed to see open text. # Forms Source: https://koreai.mintlify.app/agent-platform/forms Forms let you collect structured information from users using reusable, project-level forms. Use a form in a [Human Task node](/agent-platform/workflows#human-task-node) to collect information from an assignee, or use it as an [Intake form trigger](/agent-platform/workflows#intake-form-trigger) to start a workflow when someone submits the form. You can create a form once and reuse it across workflows instead of creating the same fields separately in each workflow. This reduces duplication and simplifies form management. Create and manage forms under **Resources > Forms**, where you define their structure. Use forms in the **Human Task** node to configure how they are used in the workflow, including field values and access. ## Create a form 1. In the left navigation, select **Resources > Forms**. 2. Select **New form**. 3. Enter a name and description for the form. 4. Select **Create**. The form opens in the form builder. 5. Configure the fields you want to include in the form. 6. Preview the form to verify how it appears to users. 7. Publish the form. Form builder ## Configure a form in the form builder Use the form builder to add fields and configure their settings. Select a field from the field palette to add it to the form, then select the field to configure its settings. | **Setting** | **Description** | | :- | :- | | **General settings** | Configure the field label, variable name, description, tooltip, placeholder, access, and whether the field is required. | | **Field-specific settings** | Configure settings specific to the selected field type. | | **Default value** | Specify a value to use when the form is initially displayed. | | **Validation** | Define validation rules to ensure that submitted values meet the requirements of the form. Depending on the field type, you can configure options such as:

- Minimum and maximum values
- Regular expressions
- Timing
- Validation messages | | **Security** | Configure how field data is protected. Use security settings when a field contains sensitive information. You can configure secure fields for **redaction**, **replacement**, or **masking**.

Secure-field protection applies at the data layer, not just when the field is displayed. | | **Visibility** | Configure conditions that determine when a field is displayed. You can use **AND** and **OR** conditions to control field visibility based on other field values. | ## Configure form settings Use the form settings to configure the form details, appearance, and reuse options. | **Setting** | **Description** | | :- | :- | | **Form details** | Configure the form name, description, variable prefix, and tags. | | **Theme & branding** | Customize the form appearance, including the header image, accent color, fonts, font sizes, and background. | | **Reuse** | View where the form can be reused, such as a workflow intake trigger or in a Human Task node. | ## Preview a form Select **Preview** to view how the form appears to users. Use the desktop and mobile options to preview the form on different screen sizes. You can also select **Preview link** to generate a shareable preview link for others to review the form. ## Publish and version a form Forms use draft and published versions so that you can update a form without unexpectedly changing forms already used by workflows. When you publish a form: * The first published version is **v1**. Subsequent publications create new versions, such as **v2** and **v3**. * Published versions remain unchanged after they are published. * A workflow can use a specific published version or the current draft. * When a newer version is available, you can refresh the form configuration in the workflow and review the changes before adopting the new version. * A workflow run uses the form version that was pinned when the run started. This versioning ensures that changes to a form do not unexpectedly affect in-flight workflow runs. ## Use a form in a workflow You can use a published form in a workflow to collect information from users or assignees. 1. Open the workflow and add the **Human Task** node. 2. In the **Task** tab, select a form. The fields defined in the form are presented to the assignee. 3. Select the form version to use. 4. Configure the field values and access for the task. The selected form version is used by the workflow until you refresh the form configuration to adopt a newer version. The form must have at least one published version before you can attach it to a Human Task node. If the form has not been published, publish it in the Form Builder first. For information about using forms in a Human Task node, see [Human Task node](/agent-platform/workflows#human-task-node). ## Use a form as a trigger Use a form as an **Intake form** trigger to start a workflow when a user submits the form. For information about configuring an Intake form trigger, see [Configuring triggers](/agent-platform/workflows#intake-form-trigger). ## View form responses The Responses tab lists submissions received for the form. You can search and filter responses and view individual submissions. Select a response to view its submitted values. You can view the submission in Form or JSON format. Form responses ## Import and export a form You can export a form definition as JSON to reuse it in another project, or import a form definition into a project. * **Export:** Select **Export** from the form's actions menu to export the form definition as JSON. The exported definition includes the form fields, validation, visibility, theme, and form settings. * **Import:** Select **Import** and select the form definition to import it into the project. When you import a form into another project, project-specific settings, such as domain configuration, must be configured again after import. # Gather data from users Source: https://koreai.mintlify.app/agent-platform/gather-data Use `GATHER` to collect structured data from users through natural conversation. Instead of rigid form-filling, it lets the agent extract field values from user input in natural language — a user saying "I want to fly to Paris on March 10 for two people" can fill three fields at once. Fields can be validated, corrected, and inferred from context without restarting the flow. You can configure `GATHER` using the **Gather Fields** section in Studio or by editing the `GATHER` block directly in the ABL Editor. ## Before you begin * You must have an agent created in your project. See [Create Agents](/agent-platform/create-agents). * To use inference and LLM-based validation, an LLM provider must be configured under **Admin > Models**. ## Configure gather fields ### In Studio Navigate to your agent and select **Gather Fields** from the **Capabilities** group in the agent editor sidebar. Gather Fields in Studio The Gather Fields page lists all defined fields. Click any field to expand its configuration, or click **+ Add Field** to define a new one. Each field has the following properties. | Property | Description | | - | - | | **Field name** | The variable name used to reference the collected value in ABL and templates. | | **Type** | The data type to collect. See [Field types](#field-types). | | **Required** | Whether the agent must collect this field before proceeding. | | **Prompt** | The question the agent asks the user to collect this field. | | **Default Value** | The default value for this field. | | **Extraction hints** | Instructions that guide the LLM on how to interpret ambiguous input for this field. | | **Infer from context** | When enabled, the LLM extracts the value from prior conversation without asking the user again. | | **Sensitive (PII)** | When enabled, the field value is masked in logs and traces. | | **Extraction pattern (regex)** | A regex pattern for strict extraction. The field value must match this pattern. | Click **Save changes** after configuring fields. ### In the ABL Editor Define `GATHER` at the agent level for fields the LLM collects through natural conversation, or inside a flow step for deterministic collection. **Agent-level GATHER:** ```yaml theme={null} AGENT: Hotel_Search GOAL: "Help users find hotels" GATHER: destination: prompt: "Where would you like to stay?" type: string required: true checkin: prompt: "When is your check-in date?" type: date required: true checkout: prompt: "When is your check-out date?" type: date required: true guests: prompt: "How many guests?" type: number required: false default: 2 ``` **GATHER inside a flow step:** ```yaml theme={null} collect_trip_info: REASONING: false GATHER: - destination: required prompt: "Where would you like to stay?" - checkin_date: required type: date prompt: "Check-in date?" - checkout_date: required type: date prompt: "Check-out date?" - num_guests: type: number default: 2 prompt: "How many guests?" COMPLETE_WHEN: destination AND checkin_date AND checkout_date THEN: search_hotels ``` Agent-level `GATHER` uses a named field syntax. Flow step `GATHER` uses a compact list syntax. `COMPLETE_WHEN` defines when the step has enough data to advance. ## Field properties | Property | Type | Required | Default | Description | | - | - | - | - | - | | prompt | string | Yes | -- | The question or instruction shown to the user to collect this field | | type | string | No | "string" | Data type for the field value | | required | boolean | No | true | Whether the field must be collected before proceeding | | default | any | No | none | Default value used if the user does not provide one | | validate | string | No | none | Validation expression | | validation\_process | "REGEX" \| "CODE" \| "LLM" | No | none | How validation is performed | | retry\_prompt | string | No | none | Custom prompt shown when validation fails | | max\_retries | number | No | none | Maximum validation retry attempts | | infer | boolean | No | false | Allow the LLM to infer the value from context | | infer\_confidence | number | No | 0.8 | Minimum confidence threshold for accepting inferred values (0.0--1.0) | | infer\_confirm | boolean | No | true | Whether to confirm inferred values with the user | | extraction\_hints | `string[]` | No | none | Hints to guide the LLM's value extraction | | prompt\_mode | "ask" \| "extract\_only" | No | "ask" | Whether the prompt is shown to the user or used only for LLM extraction | | range | boolean | No | false | Collect as a range `{low, high}` instead of a scalar | | list | boolean | No | false | Collect as an array instead of a scalar | | preferences | boolean | No | false | Categorize into accept/desire/avoid/refuse preference sets | | activation | string \| `{when: string}` | No | "required" | Activation mode | | depends\_on | `string[]` | No | none | Fields that must be collected before this field activates | | sensitive | boolean | No | false | Whether this field carries PII | | sensitive\_display | "redact" \| "mask" \| "replace" | No | none | How sensitive values are displayed outside the gather context | | mask\_config | `{showFirst, showLast, char}` | No | none | Masking configuration when sensitive\_display is "mask" | | transient | boolean | No | false | Whether PII auto-cleans after gather completes | | extraction\_pattern | string | No | none | Custom regex pattern for value extraction | | extraction\_group | number | No | 0 | Capture group index for extraction\_pattern | | semantics | object | No | none | Supplemental metadata about the value | ## Field types | Type | Example values | | - | - | | `string` | Any text. | | `number` | `42`, `3.14` | | `date` | `2026-03-15`, `next Friday` | | `email` | `user@example.com` | | `phone` | `+1-555-0123` | | `boolean` | `yes`, `no`, `true` | ## Collection strategies The collection strategy controls how the agent extracts field values from user input. ```yaml theme={null} collect_trip_info: REASONING: false GATHER: FIELDS: - checkin_date: required - checkout_date: required - num_guests: required STRATEGY: llm THEN: search_and_show ``` | Strategy | Behavior | | - | - | | `llm` | LLM extracts values from natural language. This is the default. | | `pattern` | Strict pattern matching only. | | `hybrid` | Tries pattern matching first, then falls back to LLM for unresolved fields. | Use `hybrid` when some fields have a predictable format (such as order IDs or phone numbers) but others are open-ended. ## Field validation Add validation to enforce data formats, value ranges, and business rules before the agent proceeds. ### Validate with an expression ```yaml theme={null} GATHER: email: prompt: "What is your email address?" type: email required: true validate: pattern(^[\\w.+-]+@[\\w-]+\\.[\\w.-]+$) age: prompt: "How old are you?" type: number required: true validate: "value >= 18 AND value <= 120" ``` The `validate` expression is evaluated at runtime. If it fails, the agent re-prompts the user. ### Set the validation process ```yaml theme={null} GATHER: order_id: prompt: "What is your order number? (Format: ORD-XXXXX)" type: string required: true validation: "^ORD-[A-Z0-9]{5}$" validation_process: REGEX address: prompt: "What is your shipping address?" type: string required: true validation: "Verify this is a valid US mailing address with street, city, state, and ZIP" validation_process: LLM ``` | Process | Behavior | | - | - | | `REGEX` | Matches the value against a regular expression pattern. | | `CODE` | Runs a code snippet for validation. | | `LLM` | Asks the LLM to evaluate whether the value is valid. When multiple gathered fields in the same extraction pass use `VALIDATION_PROCESS: LLM`, the runtime batches them into one validation call by default and returns per-field failures. A custom field-validation prompt override preserves legacy single-field validation calls so tenant-specific prompt contracts remain exact. | ### Customize Retry Behavior ```yaml theme={null} GATHER: phone_number: prompt: "What is your phone number?" type: phone required: true validation: "^\\+?[1-9]\\d{1,14}$" validation_process: REGEX retry_prompt: "That does not look like a valid phone number. Please enter a number like +15551234567." max_retries: 3 ``` `retry_prompt` replaces the default re-prompt message when validation fails. `max_retries` caps how many times the agent asks before moving on or escalating. ### Validate enum values ```yaml theme={null} GATHER: cancellation_scope: prompt: "Would you like to cancel the entire order or specific items?" type: string required: true validate: enum(full, partial) infer: true extraction_hints: - "If user says 'all', 'everything', or 'whole order', infer 'full'" - "If user mentions specific items or says 'some', infer 'partial'" ``` `enum()` restricts accepted values to a defined set. Combined with `infer: true`, the LLM maps natural language input to the valid enum values. ### Validate across fields Validation expressions can reference other gathered fields for cross-field validation. ```yaml theme={null} GATHER: checkin_date: prompt: "Check-in date?" type: date required: true validate: "value >= today()" retry_prompt: "The check-in date must be today or later." checkout_date: prompt: "Check-out date?" type: date required: true validate: "value > checkin_date" retry_prompt: "Check-out must be after your check-in date of {{checkin_date}}." ``` ### Validate with CONSTRAINTS For business rules that go beyond field format, use `CONSTRAINTS` alongside `GATHER`. ```yaml theme={null} CONSTRAINTS: - REQUIRE order_id.matches("^ORD-[A-Z0-9]{5}$") OR email IS SET ON_FAIL: "Please provide a valid order number (format: ORD-XXXXX) or an email address." - REQUIRE lookup_attempts <= 3 ON_FAIL: "I have been unable to find your order after several attempts. Let me connect you with a specialist." ``` ### Validate payment fields in a flow step ```yaml theme={null} collect_payment: REASONING: false GATHER: - card_number: required validation: "^[0-9]{13,19}$" validation_process: REGEX retry_prompt: "Please enter a valid card number (13-19 digits)." max_retries: 3 - expiry: required validation: "^(0[1-9]|1[0-2])\\/([0-9]{2})$" validation_process: REGEX retry_prompt: "Please enter expiry as MM/YY." - cvv: required validation: "^[0-9]{3,4}$" validation_process: REGEX retry_prompt: "CVV must be 3 or 4 digits." THEN: process_payment ``` ## LLM inference LLM inference lets the agent fill field values from conversational context without asking the user explicitly. ### Enable inference on a field ```yaml theme={null} GATHER: cancellation_reason: prompt: "May I ask the reason for the cancellation?" type: string required: false infer: true ``` With `infer: true`, if the user already said "I do not need the order anymore," the agent fills `cancellation_reason` without prompting again. ### Control inference confidence ```yaml theme={null} GATHER: destination: prompt: "Where would you like to travel?" type: string required: true infer: true infer_confidence: 0.9 infer_confirm: true ``` | Property | Default | Purpose | | - | - | - | | `infer` | `false` | Allow the LLM to extract the value from context. | | `infer_confidence` | `0.8` | Minimum confidence score to accept an inferred value. | | `infer_confirm` | `true` | Ask the user to confirm inferred values before proceeding. | When `infer_confirm: true`, the agent confirms with the user before accepting — for example, "It sounds like you want to travel to Paris. Is that correct?" ### Add extraction hints Extraction hints guide the LLM on how to map natural language input into structured values. ```yaml theme={null} GATHER: cancellation_scope: prompt: "Would you like to cancel the entire order or specific items?" type: string required: false infer: true extraction_hints: - "If user says 'all', 'everything', 'whole order', infer 'full'" - "If user mentions specific items or says 'some', infer 'partial'" selected_items: prompt: "Which items would you like to cancel?" type: string required: false extraction_hints: - "Only needed for partial cancellations" - "Match user input to item names from the order" ``` Extraction hints are especially useful for domain-specific terminology and ambiguous phrases. ### Silent extraction without prompting Use `prompt_mode: extract_only` when the prompt is an instruction to the LLM, not a question for the user. The agent silently extracts the value from context. ```yaml theme={null} GATHER: sentiment: prompt: "Assess the customer's emotional state from the conversation" type: string required: false infer: true prompt_mode: extract_only ``` ### Activate fields conditionally Use `depends_on` and `activation` to collect a field only when another field meets a condition. ```yaml theme={null} GATHER: trip_type: prompt: "Is this a round trip or one way?" type: string required: true infer: true extraction_hints: - "If user mentions return date, infer 'round_trip'" - "If user says 'one way' or mentions only departure, infer 'one_way'" return_date: prompt: "When would you like to return?" type: date required: false infer: true depends_on: [trip_type] activation: when: "trip_type == 'round_trip'" ``` `return_date` is only collected when `trip_type` is `round_trip`. If the user says "I need a round trip to London, leaving March 10 and returning March 17," both fields are inferred from a single message. Use `activation: progressive` to activate a field only after its dependencies are met, letting the conversation unfold naturally. ```yaml theme={null} GATHER: budget: prompt: "What is your budget?" type: string required: true luxury_preferences: prompt: "Any specific luxury amenities you want?" type: string activation: progressive depends_on: [budget] ``` ### High-confidence inference without confirmation Set `infer_confirm: false` for fields where the inferred value is obvious from context — for example, detecting language from how the user writes. ```yaml theme={null} GATHER: language_preference: prompt: "What language do you prefer?" type: string required: false infer: true infer_confidence: 0.95 infer_confirm: false ``` Use a high `infer_confidence` threshold to avoid silent mismatches when skipping confirmation. ## Optional fields and defaults Optional fields are collected only if the user provides them. Default values are used when the user skips the field. ```yaml theme={null} GATHER: priority: prompt: "What priority level? (low, medium, high)" type: string required: false default: "medium" notes: prompt: "Any additional notes?" type: string required: false ``` ## Collecting ranges and lists By default, GATHER collects a single scalar value per field. Use range or list to collect structured multi-value input instead. ### Collect a range Set `range: true` to collect a `{low, high}` pair — useful for price ranges, age brackets, date windows, and similar bounded inputs. ```yaml theme={null} GATHER: price_range: prompt: "What is your budget range? (for example. $100 to $300)" type: number required: true range: true ``` The collected value is stored as an object: `{low: number, high: number}`. ### Collect a list Set `list: true` to collect multiple values of the same type as an array — useful for item selections, amenity preferences, or multiple destinations. ```yaml theme={null} GATHER: preferred_amenities: prompt: "Which amenities are important to you? (for example. pool, gym, parking)" type: string required: false list: true ``` The collected value is stored as an array: `["val1", "val2", "val3"]`. ## Handling sensitive fields For PII and sensitive fields, use the `sensitive` property along with `sensitive_display` property to suggest how to show the sensitive fields. For full details on display modes and masking configuration, see [Handling transient and sensitive fields.](/agent-platform/abl-reference/gather#transient-and-sensitive-fields) ## Present collected values Use `PRESENT` to show collected values back to the user as each field is filled. ```yaml theme={null} collect_trip_info: REASONING: false GATHER: - destination: required prompt: "Where would you like to stay?" - checkin_date: required type: date - checkout_date: required type: date PRESENT: | Here is what I have so far: {{#if destination}}Destination: {{destination}}{{/if}} {{#if checkin_date}}Check-in: {{checkin_date}}{{/if}} {{#if checkout_date}}Check-out: {{checkout_date}}{{/if}} COMPLETE_WHEN: destination AND checkin_date AND checkout_date THEN: search_hotels ``` ## Handling corrections to fields within a flow Enable corrections so users can change previously provided values without restarting the flow. This property is set at the flow level and not at the field level. ### Enable corrections on a flow step ```yaml theme={null} collect_trip_info: REASONING: false GATHER: - destination: required prompt: "Where would you like to stay?" - checkin_date: required type: date prompt: "Check-in date?" - checkout_date: required type: date prompt: "Check-out date?" CORRECTIONS: true COMPLETE_WHEN: destination AND checkin_date AND checkout_date THEN: search_hotels ``` With `CORRECTIONS: true`, the user can say "actually, change the destination to London" at any point during collection, and the agent clears and re-collects that field. ### Use SUB\_INTENTS for targeted corrections `SUB_INTENTS` define scoped intents valid only within a step. if a field is cleared by a sub intent, the agent re-collects it. ```yaml theme={null} collect_trip_info: REASONING: false GATHER: - destination: required - checkin_date: required type: date - checkout_date: required type: date SUB_INTENTS: - INTENT: "change destination" CLEAR: [destination] RESPOND: "Sure, what is the new destination?" - INTENT: "change dates" CLEAR: [checkin_date, checkout_date] RESPOND: "What are your new travel dates?" COMPLETE_WHEN: destination AND checkin_date AND checkout_date THEN: search_hotels ``` ### Use ON\_INPUT for deterministic correction branching ```yaml theme={null} get_dates: REASONING: false GATHER: - checkin_date: required type: date - checkout_date: required type: date ON_INPUT: - IF: input == "back" THEN: get_destination - IF: input.contains("change destination") THEN: get_destination - ELSE: THEN: get_guests ``` ### Handle corrections at the review step with DIGRESSIONS `DIGRESSIONS` allow jumping to any step in the flow from a review or confirmation step. Unlike `SUB_INTENTS`, digressions leave the current step entirely. ```yaml theme={null} review_booking: REASONING: false PRESENT: | Booking Summary: Hotel: {{selected_hotel.name}} Dates: {{checkin_date}} to {{checkout_date}} Guests: {{num_guests}} Guest: {{guest_name}} ({{guest_email}}) Total: ${{total_price}} Type "confirm" to complete or "change [field]" to modify. DIGRESSIONS: - INTENT: "change hotel" GOTO: select_hotel - INTENT: "change dates" CLEAR: [checkin_date, checkout_date] GOTO: collect_trip_info - INTENT: "change guest" GOTO: collect_guest_info ON_INPUT: - IF: input == "confirm" THEN: confirm - ELSE: RESPOND: "Please type 'confirm' to proceed or 'change [field]' to modify." THEN: review_booking ``` ### Handle exhausted retries `MAX_ATTEMPTS` on the step limits total retries. `ON_EXHAUSTED` defines the fallback step when the limit is reached. ```yaml theme={null} verify_identity: REASONING: false MAX_ATTEMPTS: 3 ON_EXHAUSTED: connect_to_human GATHER: - verification_code: prompt: "Enter the 6-digit code sent to your phone." type: string required: true validation: "^[0-9]{6}$" validation_process: REGEX retry_prompt: "That code is not valid. Please enter exactly 6 digits." max_retries: 3 CALL: verify_code(verification_code) ON_SUCCESS: THEN: authenticated ON_FAIL: RESPOND: "That code did not match. Please try again." THEN: verify_identity ``` `MAX_ATTEMPTS` counts entries to the step, not individual field retries. `max_retries` on a field counts field-level retries separately. ### Apply global digressions across all flow steps Global digressions are available in every step. `RESUME: true` returns to the current step after handling the digression. ```yaml theme={null} FLOW: steps: - welcome - collect_info - search - confirm global_digressions: REASONING: false - INTENT: "cancel" RESPOND: "Booking cancelled. Would you like to start over?" GOTO: welcome - INTENT: "help" RESPOND: "I can help you find and book hotels. Tell me your destination and dates." RESUME: true - INTENT: "start over" GOTO: welcome ``` Without `RESUME`, the `GOTO` target becomes the new active step. ## Troubleshooting | Symptom | Resolution | | - | - | | **Fields not extracted from natural language** | Verify `STRATEGY: llm` is set (or omitted, since `llm` is the default). Add `extraction_hints` for ambiguous fields. | | **Step advances before all required fields are collected** | Add `COMPLETE_WHEN` with explicit conditions listing all required fields. | | **Validation never passes** | Check regex pattern escaping — in ABL, backslashes need to be doubled (`\\d`, not `\d`). | | **User stuck in retry loop** | Set `max_retries` to 2–3. After exhaustion, the agent proceeds or escalates rather than looping. | | **Cross-field validation runs before dependent field is collected** | Order `GATHER` fields so dependencies are listed first, or use `COMPLETE_WHEN` to control when validation evaluates. | | **Inference fills a field incorrectly** | Raise `infer_confidence` to `0.9` or higher. Set `infer_confirm: true` to require explicit user confirmation. | | **Agent asks for a field the user already mentioned** | Verify `infer: true` is set on the field and `STRATEGY` is `llm` or `hybrid` (not `pattern`). | | **Extraction hints ignored** | Hints must be a list of strings. Each hint should be a clear, specific instruction — not a long paragraph. | | **User correction not detected** | Add explicit `SUB_INTENTS` or `DIGRESSIONS` for the correction patterns your users are likely to use. | | **Cleared field not re-collected** | After `CLEAR`, the step must re-prompt. Verify `GATHER` includes the cleared field and `COMPLETE_WHEN` requires it. | # Quick start guide Source: https://koreai.mintlify.app/agent-platform/getting-started Sign up for Agent Platform, configure the LLM provider and Arch AI, invite team members to your workspace, and start building your AI project. ## Access the platform Agent Platform is available to Kore.ai customers and partners. Sign-up is enabled only for approved domains. To request access for your organization, contact us. The platform runs in multiple regions. For the US-Central Cloud, use [https://agents.kore.ai/](https://agents.kore.ai/). See the region-specific URLs below. ### Region-specific URLs and allowlist of IPs The platform sends requests to a fixed set of egress IP addresses. If your systems use IP restrictions, add the region-specific egress IPs below to your allowlist to permit platform traffic. | Region | Region-specific URL | Egress IPs | | :- | :- | :- | | USA-Central | [agents.kore.ai](https://agents.kore.ai) | • 172.173.109.141
• 172.212.188.206
• 20.9.69.249
• 20.12.238.143
• 104.43.208.181
• 52.173.108.174
• 172.202.57.107
• 135.119.76.33
• 130.131.217.68 | | USA-Swift | [us-swift-agents.kore.ai](https://us-swift-agents.kore.ai) | • 44.216.129.184
• 54.225.127.87
• 34.201.194.187 | | United Kingdom | [uk-agents.kore.ai](https://uk-agents.kore.ai) | • 20.90.75.123 | | United Kingdom-Prime | [uk-prime-agents.kore.ai](https://uk-prime-agents.kore.ai) | • 3.9.151.173 | | Germany | [de-agents.kore.ai](https://de-agents.kore.ai) | • 3.65.5.141 | | Germany-Prime | [de-prime-agents.kore.ai](https://de-prime-agents.kore.ai) | • 20.79.56.64 | | Japan | [jp-agents.kore.ai](https://jp-agents.kore.ai) | • 20.48.103.185 | | Japan-Prime | [jp-prime-agents.kore.ai](https://jp-prime-agents.kore.ai) | • 18.180.133.211 | | Australia | [au-agents.kore.ai](https://au-agents.kore.ai) | • 52.187.236.188 | | Singapore | [sg-agents.kore.ai](https://sg-agents.kore.ai) | • 4.194.44.63 | | United Arab Emirates | [uae-agents.kore.ai](https://uae-agents.kore.ai) | • 20.74.173.252
• 20.74.153.122 | | Kingdom of Saudi Arabia | [ksa-agents.kore.ai](https://ksa-agents.kore.ai) | 8.228.193.152 | | India | [ind-agents.kore.ai](https://ind-agents.kore.ai) | • 135.235.170.165 |
### Set up your account, workspace, and initial configuration 1. Sign in to the platform at [https://agents.kore.ai/](https://agents.kore.ai/) and create a workspace as part of the onboarding flow. 2. Invite team members to your workspace: click the **Admin/Gear** icon at the top, click **Invite members**, enter the member's email address, select a role, and click **Send invite**. 3. Configure the LLM provider and Arch AI: 1. Register provider credentials and configure at least one supported model. See [Configure LLM](/agent-platform/administration/ai-configuration#llm-providers). 2. Configure the model and credentials Arch uses to generate agent specifications and drive chat-based orchestration. See [Configure Arch](/agent-platform/administration/ai-configuration#arch). Once your account and configuration are set up, you are ready to build your first AI project. *** ## Ways to build on the platform The platform offers three easy ways to build a project with Arch: ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart TD P["Agent Platform"] P --> B["Build with Arch in Studio"] P --> C["Build with Arch using MCP"] P --> D["Build with Arch using CLI"] ``` | Approach | What it is | Best for | | :- | :- | :- | | [Studio](#build-with-arch-in-studio) | Describe or upload your business requirements. Arch interviews you, generates a blueprint and project artifacts, then opens the project in Studio for review. | Teams that want to accelerate development through AI-assisted authoring. | | [MCP](/agent-platform/mcp-server) | Build and manage projects from your development environment, using supported AI assistants and tools through the Model Context Protocol (MCP). | Developers who prefer working from their IDE or AI-assisted coding tools. | | [CLI](/agent-platform/cli) | Create, test, and manage agents and projects from the terminal, and automate validation, evaluation, and deployment in CI workflows. | Developers and teams who prefer automating build, test, and deployment pipelines. | ## Build with Arch in Studio Arch designs, builds, evaluates, and optimizes AI projects from your business requirements. Describe your requirements in natural language, or upload supporting documents, and Arch generates the project architecture and artifacts, such as agents, workflows, and prompts. Arch then runs an initial evaluation and provides a working foundation that you can customize and deploy. ### Create a project with Arch Sign in to the platform and select **Start with Arch** on the **Projects** page. Arch creates your project in three phases: Interview, Blueprint, and Build. Describe the project you want to build. * Describe your requirements in natural language. * Upload a Standard Operating Procedure (SOP). * Upload design documents or reference documents. Arch analyzes your input and asks follow-up questions until it has enough information to generate a complete project blueprint. Depending on your requirements, it may ask about your business processes, communication channels, integrations, and other implementation details. You can upload an SOP, design document, or requirements specification instead of describing your project from scratch. Arch uses the uploaded content to generate more accurate project requirements. **Example** Suppose you want to build a customer support agent for an online retailer. You might describe your requirements as: ```text theme={null} Create a customer support agent that helps customers track orders, initiate returns, answer product questions, and escalate complex issues to a human agent. ``` Arch analyzes your request and interviews you to refine the requirements before generating the project blueprint. Arch may ask follow-up questions such as: | Arch asks | You respond | | - | - | | What channels should the agent support? | Web chat and WhatsApp. | | Which business systems should the agent integrate with? | Our order management system and CRM. | | Should the agent escalate certain requests? | Yes. Escalate refund approvals over \$500 to a human agent. | | Does the agent require access to a knowledge base? | Yes. Use our product catalogue and return policy documents. | Interview After gathering your requirements, Arch generates a blueprint that represents the proposed project architecture. Review the blueprint and either accept it or request changes. Arch updates the blueprint based on your feedback until it accurately reflects your requirements. **Example** Based on the customer support requirements, Arch generates a blueprint containing a Customer Support Agent, an Order Management workflow, the required integrations, and relationships between the customer-facing and escalation agents. Blueprint Spend time reviewing the blueprint before proceeding. Changes made at this stage are incorporated into the generated project, reducing the need for manual updates later. After you approve the blueprint, Arch assembles the generated artifacts into a working project scaffold and opens the project in Studio. Depending on your requirements, Arch generates the necessary project artifacts, which can include: * Agents * Workflows * Tools * Prompts * Knowledge configuration **Example** For the customer support project, Arch creates the customer support agent, the required workflows, prompts, tools, and knowledge configuration, and assembles them into a working project in Studio. Build ### Review the project setup After Arch generates your project, it opens the project in Studio and displays the **Project setup** panel. Review the project status and resolve any issues with the help of Arch before customizing your project. Project setup panel The panel includes the following sections: | Section | Description | | - | - | | **Lifecycle** | Shows the current stage of the project setup and continuous improvement lifecycle. | | **Build and validation details** | Displays the status of each stage, including project scaffold creation, compile repair, draft review, runtime evaluation, and promotion readiness. | | **Recommended next step** | Highlights validation issues and recommends the next actions before continuing. | ### Review the initial evaluation Arch generates an evaluation suite and runs an initial evaluation of the project. The evaluation uses generated scenarios and personas to simulate conversations and identify potential issues. * Review the identified issues and recommendations. * Update your agents, workflows, prompts, or tools as needed. * Rerun the evaluation to verify the improvements. * Repeat this process until the project meets your quality requirements. **Example** The initial evaluation may identify that the agent correctly answers product questions but doesn't escalate high-value refund requests as specified. Update the workflow or prompts, then rerun the evaluation to verify the improvement. For more information about evaluation suites and results, see [Evaluations](/agent-platform/evals). For more information about iterative optimization, see [Optimize with Arch AI](/agent-platform/optimize). ### Customize your project After reviewing the validation and evaluation results, customize the generated project to meet your business requirements. Depending on your project, you can: * Use **Auto Tune** to apply recommended improvements to your project. * Review and update the generated agents, workflows, tools, and prompts. * Replace placeholder or mock tools with production integrations. * Configure authentication and connection settings. * Configure knowledge sources and other project settings. * Test the project and verify that it meets your business requirements before deployment. Depending on your project requirements, Arch may generate placeholder tools or sample configurations. Replace these with your production systems before deploying your project. ### Next steps Learn how to customize and deploy your project: * [Agents](/agent-platform/create-agents) – Review and customize the generated agents. * [Workflows](/agent-platform/workflows) – Modify the generated workflows and orchestration logic. * [Tools](/agent-platform/tools-overview) – Configure production tools and integrations. * [Knowledge](/agent-platform/knowledge) – Configure and manage knowledge sources. * [Evaluations](/agent-platform/evals) – Run additional evaluations to validate project quality. * [Optimize with Arch AI](/agent-platform/optimize) – Improve your project using recommendations based on evaluation results. * [Deployment](/agent-platform/deployment) – Deploy your project to the target environment. # Glossary Source: https://koreai.mintlify.app/agent-platform/glossary A comprehensive reference of terms used throughout the Agent Platform documentation. Terms are organized alphabetically for quick lookup. *** ## A ### ABL (Agent Blueprint Language) The enterprise control plane for agentic AI — a schema-driven language purpose-built for multi-agent orchestration where deterministic governance meets autonomous reasoning. ABL spans the full control spectrum: delegate autonomously, supervise selectively, or lock down as a deterministic state machine. Agent definitions compile into immutable artifacts — every version is auditable, every deployment is reproducible, every change is governed. ABL is both human-writable and designed as a code-generation target for AI-authored blueprints. **Related articles:** [ABL language overview](/agent-platform/concepts) ### Agent An autonomous unit of work defined in ABL that handles a specific domain or task. Each agent has a goal, persona, tools, and optional conversation flow. Agents can operate independently or collaborate through delegation and handoff patterns. **Related articles:** [Agent concepts](/agent-platform/concepts), [Flow-based execution](#flow-based-execution), [Reasoning mode](#reasoning-mode) *** ## C ### Channel A deployment endpoint through which end users interact with your agents. Channels include web chat widgets, SDKs (JavaScript, React, iOS, Android), REST APIs, and third-party integrations (Slack, WhatsApp, Microsoft Teams). Each channel has its own configuration, authentication, and content format support. **Related articles:** [Channels overview](/agent-platform/channels). ### COMPLETE A lifecycle construct in ABL that defines when an agent considers its task finished. Each `COMPLETE` block specifies a `WHEN` condition, an optional closing response, and an optional `STORE` directive to persist results. When a completion condition evaluates to true, the agent ends the conversation or returns control to its parent. **Related articles:** [ESCALATE](#escalate), [HANDOFF](#handoff) ### Connector An integration that links an external data source to a knowledge base. Connectors handle ingestion from sources such as file uploads, web crawls, Google Drive, Confluence, SharePoint, and custom APIs. Each connector manages its own sync schedule and change detection. **Related articles:** [Knowledge base](#knowledge-base), [RAG](#rag-retrieval-augmented-generation) ### Constraint A business rule that must be satisfied before an agent proceeds with an action. Authors can group constraints under named labels such as `pre_search` or `pre_booking`, but those labels are organizational only. Runtime scoping comes from the rule itself, using constructs such as `WHEN` and structural `BEFORE`, plus `ON_FAIL` actions to respond, collect missing information, escalate, hand off, or redirect to a different step. **Related articles:** [Guardrail](#guardrail) *** ## D ### DELEGATE A multi-agent construct that allows an agent to dispatch a subtask to another agent and wait for the result. Delegation includes input/output mapping, a purpose description, timeout configuration, and failure handling. The delegating agent pauses execution until the sub-agent returns its result. Delegates can target local or remote agents via REST or A2A protocol. **Related articles:** [HANDOFF](#handoff), [Supervisor](#supervisor) *** ## E ### Embedding A numerical vector representation of text content used for semantic search. The platform generates embeddings during knowledge base ingestion, converting documents into vectors that can be compared for similarity. Embedding models (such as BGE-M3) run as part of the SearchAI pipeline. **Related articles:** [Vector search](#vector-search), [RAG](#rag-retrieval-augmented-generation), [Knowledge base](#knowledge-base) ### ESCALATE A construct that transfers control from an AI agent to a human operator. Escalation triggers fire based on configurable conditions (such as user frustration, policy violations, or agent inability to resolve a request). Each trigger includes a priority level (`low`, `medium`, `high`, `critical`), contextual information for the human agent, and routing configuration. Post-resolution actions define what happens after the human completes the task. **Related articles:** [HANDOFF](#handoff), [COMPLETE](#complete) *** ## F ### Feature gate A mechanism that controls access to platform capabilities based on a tenant's subscription plan tier. Feature gates determine which functionality is available (such as advanced guardrails, custom roles, SSO, or higher usage limits) and enforce entitlement boundaries at the API level. **Related articles:** [Plan tier](#plan-tier) ### FLOW An optional construct in ABL that adds structured, step-based execution to an agent. Each flow contains named steps with actions such as responding, calling tools, collecting user input (GATHER), branching on conditions, and transitioning between steps. Agents without a FLOW section operate in reasoning mode by default, using LLM inference to decide actions dynamically. Adding a FLOW does not change the agent's type -- it is still the same agent, with the option to enable or disable reasoning on individual steps via `REASONING: true/false`. **Related articles:** [GATHER](#gather), [Flow-based execution](#flow-based-execution), [Reasoning mode](#reasoning-mode) ### Flow-based execution An execution style where an agent includes a `FLOW` section in its ABL definition, providing structured, step-based conversation control. The agent moves through named steps with explicit transitions, branching conditions, and user input collection. Adding a flow does not create a different type of agent -- it adds structure to the same agent. Within the flow, each step can set `REASONING: true` to use LLM-driven decision-making or `REASONING: false` for deterministic behavior, allowing fine-grained control over how each part of the conversation executes. **Related articles:** [Agent](#agent), [Reasoning mode](#reasoning-mode), [FLOW](#flow) *** ## G ### GATHER A data collection construct within ABL that defines fields to collect from users during a conversation. Each field specifies a name, type, prompt, validation rules, and extraction strategy. GATHER supports LLM-based inference, pattern matching, hybrid extraction, field dependencies, progressive activation, and sensitive data handling (PII masking and transient storage). **Related articles:** [FLOW](#flow) ### Guardrail A safety mechanism that evaluates agent inputs and outputs against defined policies. Guardrails can `block`, `warn`, `redact`, `escalate`, `fix`, `reask`, or `filter` content based on rule-based checks, model-based safety classifiers (such as OpenAI Moderation or Azure Content Safety), or LLM-based evaluation prompts. Guardrails support streaming evaluation, graduated severity actions, and priority ordering. **Related articles:** [Constraint](#constraint) *** ## H ### HANDOFF A multi-agent construct that transfers an active conversation from one agent to another. Unlike delegation, a handoff passes full conversational context (including variable state, conversation summary, and memory access) to the target agent. Handoffs can be one-way or round-trip (with `RETURN: true`), and support priority ordering, remote agent targeting, and asynchronous dispatch. **Related articles:** [DELEGATE](#delegate), [ESCALATE](#escalate), [Supervisor](#supervisor) *** ## K ### Knowledge base A managed collection of documents and data sources that an agent can search during conversations. Knowledge bases use retrieval-augmented generation (RAG) to provide agents with relevant context from your own content. Each knowledge base is scoped to a project and linked to one or more connectors that handle data ingestion, chunking, and indexing. **Related articles:** [RAG](#rag-retrieval-augmented-generation), [Embedding](#embedding), [Vector search](#vector-search), [Connector](#connector) *** ## M ### MCP (Model Context Protocol) An open protocol to connect AI agents to external tools and data sources through a standardized interface. In ABL, tools can declare an `MCP` binding that connects to a configured MCP server, allowing agents to invoke external capabilities without custom HTTP integration code. The platform manages MCP server connections, authentication, and lifecycle. **Related articles:** [Tool](#tool), [Sandbox](#sandbox) ### Member See [Role](#role). ### Message A single unit of communication within a conversation turn. Messages have a role (`user`, `assistant`, `system`, or `tool`) and content. The platform tracks messages as part of the conversation history, which is used for context management, compaction, and tracing. **Related articles:** [Turn](#turn), [Session](#session) ### MFA (Multi-factor authentication) An authentication method that requires users to verify their identity through two or more factors before accessing the platform. MFA adds a layer of security beyond username and password, typically using time-based one-time passwords (TOTP) or authentication apps. **Related articles:** [SSO](#sso-single-sign-on), [OIDC](#oidc-openid-connect), [SAML](#saml-security-assertion-markup-language) *** ## O ### Observatory The platform's real-time monitoring and debugging interface for agent sessions. Observatory provides visibility into active sessions, conversation history, LLM calls, tool invocations, state transitions, and trace events. Use Observatory to diagnose agent behavior, inspect decision paths, and replay sessions. **Related articles:** [Trace](#trace), [Session](#session) ### OIDC (OpenID Connect) An identity layer built on top of OAuth 2.0 that the platform supports for single sign-on authentication. OIDC enables your organization's identity provider to authenticate users and issue identity tokens that the platform validates. Configure OIDC in your tenant's SSO settings. **Related articles:** [SSO](#sso-single-sign-on), [SAML](#saml-security-assertion-markup-language), [MFA](#mfa-multi-factor-authentication) ### Organization The top-level entity in the platform's resource hierarchy. An organization groups one or more tenants under a single billing and administrative boundary. Organization members have roles such as `ORG_OWNER`, `ORG_ADMIN`, `ORG_MEMBER`, and `ORG_BILLING`. Organizations are optional; tenants can operate independently without an organization parent. **Related articles:** [Tenant](#tenant), [Project](#project), [Workspace](#workspace) *** ## P ### Plan tier A subscription level that determines the features, usage limits, and support options available to a tenant. Plan tiers control quotas for LLM tokens, concurrent sessions, knowledge base storage, and access to advanced capabilities. Each tier includes a set of entitlements that the platform enforces through feature gates. **Related articles:** [Feature gate](#feature-gate) ### Project A container within a tenant that groups related agents, knowledge bases, tools, and configuration. Projects provide resource isolation, access control, and deployment boundaries. Each project has its own members with role-based permissions, LLM configuration, environment variables, and SDK channels. **Related articles:** [Tenant](#tenant), [Workspace](#workspace), [Organization](#organization) *** ## R ### RAG (Retrieval-augmented generation) A technique that enriches LLM prompts with relevant content retrieved from knowledge bases before generating a response. RAG allows agents to answer questions using your organization's data without fine-tuning the underlying model. The platform supports RAG, function-based, and hybrid retrieval strategies. **Related articles:** [Knowledge base](#knowledge-base), [Embedding](#embedding), [Vector search](#vector-search) ### Reasoning mode The default execution mode for every agent. In reasoning mode, an agent uses LLM inference to decide its actions dynamically based on its goal, persona, tools, and constraints, using strategies such as ReAct (reason + act), chain-of-thought, or direct inference. An agent without a `FLOW` section operates entirely in reasoning mode. Within a flow, individual steps can enable or disable reasoning via `REASONING: true/false`, allowing fine-grained control over which parts of the conversation use LLM-driven decision-making. Reasoning mode is not a separate agent type -- it is the built-in behavior of every agent. **Related articles:** [Agent](#agent), [Flow-based execution](#flow-based-execution), [FLOW](#flow) ### Role A named set of permissions assigned to a user within the platform. Built-in roles include: | Role | Scope | Description | | - | - | - | | **Owner** | Tenant | Full administrative control, including billing and tenant deletion | | **Admin** | Tenant | Manage members, settings, projects, and workspace-wide configuration (can't delete the tenant) | | **Member** | Tenant | Create and manage projects assigned to them | | **Viewer** | Tenant | Read-only access to tenant resources | Custom roles can be created within a tenant with specific permission sets and optional inheritance from a parent role. **Related articles:** [Tenant](#tenant), [Project](#project) *** ## S ### SAML (Security Assertion Markup Language) An XML-based standard for exchanging authentication and authorization data between an identity provider and the platform. SAML enables enterprise single sign-on, allowing your employees to access the platform using their corporate credentials. Configure SAML in your tenant's SSO settings. **Related articles:** [SSO](#sso-single-sign-on), [OIDC](#oidc-openid-connect), [MFA](#mfa-multi-factor-authentication) ### Sandbox An isolated execution environment for running user-defined code as a tool. Sandboxes support JavaScript and Python runtimes with configurable memory limits and timeouts. Code runs in a restricted environment with no access to the host filesystem or network, providing safe execution of custom logic within agent flows. **Related articles:** [Tool](#tool), [MCP](#mcp-model-context-protocol) ### SDK (Software Development Kit) A client library for integrating the Agent Platform into your application. SDK handle session management, message transport, authentication, and UI rendering. Available SDK include JavaScript (browser), React components, and REST API clients. SDK connects through a configured channel with its own API key. **Related articles:** [Channel](#channel), [Webhook](#webhook) ### Session A stateful conversation between an end user and one or more agents. Each session maintains conversation history, variable state, active thread information, and gathered data. Sessions are scoped to a tenant and project, stored in Redis for active conversations and persisted to durable storage for cold sessions. Sessions support forking, thread stacks for multi-agent handoffs, and automatic expiration via configurable TTLs. **Related articles:** [Turn](#turn), [Message](#message), [Observatory](#observatory) ### SSO (Single sign-on) An authentication scheme that allows users to log in once with a single set of credentials and access the platform without re-authenticating. The platform supports SSO via SAML 2.0 and OpenID Connect (OIDC) protocols, enabling integration with enterprise identity providers such as Okta, Azure AD, and Google Workspace. **Related articles:** [SAML](#saml-security-assertion-markup-language), [OIDC](#oidc-openid-connect), [MFA](#mfa-multi-factor-authentication) ### Supervisor A coordination layer defined in ABL that manages routing between multiple agents. The supervisor maintains shared state, defines routing rules based on intent matching and conditions, enforces policies, and controls communication settings. Supervisors determine which agent handles each user message based on priority-ordered routing rules. **Related articles:** [Agent](#agent), [HANDOFF](#handoff), [DELEGATE](#delegate) *** ## T ### Tenant An isolated workspace within the platform that contains projects, members, and configuration. Every API request is scoped to a tenant, providing strict data isolation between different teams or customers. Each tenant has its own LLM policies, retention settings, and member roles. Tenants can optionally belong to an organization for centralized billing and administration. **Related articles:** [Organization](#organization), [Project](#project), [Workspace](#workspace) ### Tool An external capability that an agent can invoke during a conversation. Tools are defined in ABL with a name, description, parameters, and return type. The platform supports multiple execution types: | Type | Description | | - | - | | **HTTP** | REST API calls with configurable auth, headers, and retry | | **MCP** | Calls to an MCP server via the Model Context Protocol | | **Lambda** | Serverless function invocation (AWS Lambda or equivalent) | | **Sandbox** | User-uploaded code running in an isolated runtime | Tools can include hints for caching, latency expectations, side effect declarations, and confirmation requirements. **Related articles:** [MCP](#mcp-model-context-protocol), [Sandbox](#sandbox), [Connector](#connector) ### Trace A structured record of events emitted during agent execution. Traces capture LLM calls, tool invocations, state transitions, guardrail evaluations, handoffs, and errors. Every execution path produces trace events that are stored in the platform's trace store and viewable through the Observatory. Traces enable debugging, performance analysis, and compliance auditing. **Related articles:** [Observatory](#observatory), [Session](#session) ### Turn A single request-response cycle within a session. A turn begins when the user sends a message and ends when the agent produces a complete response. A single turn may involve multiple internal operations including LLM calls, tool invocations, guardrail checks, and state updates. Sessions consist of one or more turns. **Related articles:** [Session](#session), [Message](#message) *** ## V ### Vector search A search technique that finds documents by comparing the semantic similarity of their embedding vectors rather than exact keyword matches. The platform uses vector search within knowledge bases to retrieve contextually relevant content for RAG, even when the user's query uses different wording than the source documents. **Related articles:** [Embedding](#embedding), [RAG](#rag-retrieval-augmented-generation), [Knowledge base](#knowledge-base) *** ## W ### Webhook An HTTP callback mechanism for receiving real-time event notifications from the platform. You can configure webhook subscriptions to receive events such as agent responses, session completions, and escalation triggers. Each delivery includes an idempotency key for exactly-once processing, automatic retry on failure, and delivery status tracking. **Related articles:** [Channel](#channel), [SDK](#sdk-software-development-kit) ### Workspace A synonym for [tenant](#tenant) in the platform. The terms "workspace" and "tenant" are used interchangeably in different parts of the platform. Both refer to the same isolated environment containing projects, members, and configuration. **Related articles:** [Tenant](#tenant), [Organization](#organization), [Project](#project) # Receive and use incoming SIP headers in voice agents Source: https://koreai.mintlify.app/agent-platform/handle-incoming-sip-headers When an inbound call or transferred call includes SIP headers, the platform can expose supported headers to the agent as session metadata. Agents can use this context for routing, personalization, or transfer continuity. For example, an agent can: * Route a call based on `x-queue` or `x-skill`. * Continue a CCaaS journey using `user-to-user`. * Inspect transfer context from `referred-by`, `diversion`, or `history-info`. - This capability applies to voice channels that expose inbound SIP headers to the runtime, including the Kore Voice Gateway path. - This capability is currently in Alpha. The agent can access only headers that are sent by the upstream SIP environment and pass the platform's filtering and safety checks. Validate the behavior in your SIP environment before using it in production. ## How incoming SIP headers are processed Incoming SIP headers are sent by the upstream SIP system. Unlike outgoing transfer headers, you do not configure header names or values in the agent or project settings. The processing flow is: ```text theme={null} Upstream SIP System ↓ Inbound INVITE or transfer REFER ↓ Platform-supported inbound headers ↓ Runtime filtering and normalization ↓ _metadata.sip ↓ Agent ``` The process works as follows: 1. An upstream system, such as an SBC, carrier, PBX, or transfer source, sends SIP headers with the inbound INVITE or transfer REFER. 2. The voice provider path forwards the available headers to the runtime. 3. The runtime validates, filters, and normalizes the headers. 4. Supported headers that pass these checks are added to \_metadata.sip before the first agent turn. 5. The agent can read the headers from session metadata and use them in conditions, prompts, or tool calls. There is no project-level configuration for selecting incoming SIP headers. Project-level Header Passthrough and Allowed Transfer Headers settings apply to outgoing transfers, not incoming header processing. ## Platform-supported incoming headers The platform exposes a fixed set of incoming SIP header names. Headers with other names are dropped. Supported header names are: ```text theme={null} call-id reason x-call-id x-sbc-call-id x-queue x-skill x-campaign x-correlation-id x-request-id x-session-id x-tenant-id x-agent-id x-contact-id x-project-id x-custom-data x-transfer-reason user-to-user referred-by diversion history-info ``` ### Header normalization Before exposing headers to the agent, the runtime normalizes them as follows: * Header names are case-insensitive and stored in lowercase. `X-Queue` becomes `x-queue`. * Underscores are converted to hyphens. `x_queue` becomes `x-queue`. * Repeated or multi-valued headers are combined into a comma-separated value. * CR/LF characters are removed from values. * Values containing null characters are rejected. ### Blocked headers Infrastructure and security-sensitive headers are not exposed to the agent. Examples include: ```text theme={null} via route record-route contact authorization proxy-authorization p-asserted-identity ``` ## Access incoming headers in the agent Incoming SIP headers are available in the runtime session metadata at: `_metadata.sip`. Example: ```yaml theme={null} _metadata: sip: headers: x-queue: claims-vip user-to-user: account=12345;journey=renewal inviteHeaders: x-queue: claims-vip referHeaders: user-to-user: account=12345;journey=renewal source: refer ``` The metadata includes: | Field | Description | | - | - | | `headers` | Unified map of all projected SIP headers. | | `inviteHeaders` | Headers associated with the inbound `INVITE`. | | `referHeaders` | Headers associated with transfer or `REFER` context. | | `source` | Indicates the header source, typically `invite` or `refer`. | ## Use incoming headers in agent You can use incoming SIP headers directly in agent conditions or assign them to simpler variables for reuse. ### Use headers directly in conditions Use bracket notation when referencing header names that contain hyphens. ```yaml theme={null} FLOW: start: REASONING: false BRANCHES: - IF: _metadata.sip.headers["x-queue"] == "claims-vip" RESPOND: "Routing to VIP claims handling." - IF: _metadata.sip.headers["user-to-user"] != null RESPOND: "Transfer context detected." - ELSE: RESPOND: "Standard voice entry." ``` ### Assign headers to variables When a header is used in multiple conditions, prompts, or tools, assign its value to a variable in ON\_START. ```yaml theme={null} ON_START: SET: sip_queue = _metadata.sip.headers["x-queue"] SET: sip_skill = _metadata.sip.headers["x-skill"] SET: sip_uui = _metadata.sip.headers["user-to-user"] SET: sip_source = _metadata.sip.source You can then use the variables elsewhere in the agent: GOAL: | Help the caller and honor any SIP-based routing context. Queue: {{sip_queue}} Skill: {{sip_skill}} COMPLETE: - WHEN: sip_source == "refer" RESPOND: "Transfer flow completed." ``` This approach is recommended when the same SIP-derived value is used in multiple places. ### Use incoming headers in tool calls If a tool or HTTP integration requires a value from an incoming SIP header, first assign the header value to a variable in ON\_START. For example: ```yaml theme={null} ON_START: SET: sip_queue = _metadata.sip.headers["x-queue"] SET: sip_uui = _metadata.sip.headers["user-to-user"] ``` You can then pass sip\_queue or sip\_uui to downstream tools or integrations. ## Limits The following limits apply to incoming SIP headers: * Maximum 30 projected headers. * Maximum 2048 bytes per value. * Maximum 16384 bytes total for the projected SIP header block. If these limits are exceeded, excess data is dropped rather than causing session initialization to fail. ## Security considerations Treat incoming SIP headers as upstream-provided context, not as authenticated proof of identity or authorization. Do not rely solely on a SIP header to authorize sensitive actions such as: * Accessing an account. * Confirming a user's identity. * Issuing a refund. * Changing security-sensitive information. If a header such as user-to-user contains a customer identifier, validate it against a trusted system before using it for sensitive operations. ## Troubleshooting | Symptom | Possible cause and resolution | | - | - | | `_metadata.sip` is empty or missing | The upstream provider path did not expose SIP headers, or no headers passed platform filtering. | | A header is missing | The header is not in the platform-supported set or is intentionally blocked. | | `X-Queue` does not match | Header names are normalized to lowercase. Use `x-queue`. | | A value contains comma-separated entries | Repeated or multi-valued headers are combined. | | A value is truncated or missing | The value may have exceeded a size limit or failed a safety check. | | An agent condition does not compile | Use bracket notation for hyphenated names, for example `_metadata.sip.headers["user-to-user"]`. | | You need to add headers to an outgoing transfer | Configure custom SIP headers for the transfer instead. See **Sending Custom SIP Headers on Voice Transfers**. | # Inbox Source: https://koreai.mintlify.app/agent-platform/inbox Inbox provides a centralized interface for managing human tasks generated by workflows and agents. These tasks allow workflows and agents to pause execution and wait for user input before continuing. This allows workflows to combine automated orchestration with manual user actions. Escalation tasks are typically used for user-requested human assistance, high-risk operations, system failures or repeated routing failures. Use Inbox to: * Review workflow and agent tasks * Claim unassigned tasks * Complete approval requests * Submit the required input for Data Entry tasks ## How Inbox tasks are created Inbox tasks are created when workflows or agents require human participation during execution. Tasks can be generated from: * Human task nodes * Agent escalations triggered through `ESCALATE` ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart TD A[Workflow or Agent Execution] A --> B{Human Action?} B -->|Approval / Data Entry| C[Workflow Task] B -->|Agent Escalation| D[Escalation Task] C --> E[Execution Pauses] D --> E E --> F[Human Review] F -->|Approved / Completed| H[Resume] F -->|Rejected / Escalated| I[Transfer, Retry, or End] ``` ## Access Inbox Go to your project and select **Operate** > **Inbox**. The Inbox page displays human-in-the-loop tasks and agent escalations assigned to you or available to claim for you. Inbox ## Claim tasks Inbox supports both assigned and unassigned human tasks. Tasks can be assigned either to everyone or to specific users. When a task is assigned to Everyone: * The task appears as unclaimed in the Inbox. * Users must claim the task before acting on it. Inbox displays a **Claim Task** action for unclaimed tasks. After a task is claimed, the Inbox displays the Claimed user and the Claim timestamp. This allows teams to manage shared operational queues while ensuring a single user completes the task. ## Complete Data Entry tasks Data Entry tasks collect user input during workflow execution. Data Entry forms are rendered directly inside the inbox. When structured user input is required, configure form fields in the Data Entry task definition. See *Agent Collaboration and Handoff*. To complete a Data Entry task: 1. Open the task in the Inbox. 2. Enter the required values. 3. Click **Submit Data.** After submission, workflow execution resumes using the submitted values. Tasks configured with timeouts display a countdown timer in the Inbox while waiting for user action. ## Complete Approval tasks Approval tasks allow users to review workflow requests before workflow execution continues. Approval tasks display: * Approval subject * Approval message * Associated workflow information Users can claim and review approval tasks directly from the Inbox. Approval responses are returned to the workflow execution context and used in downstream workflow execution. ## Workflow execution and task progression Human-in-the-loop tasks are synchronized with workflow execution. When workflow execution reaches a human task node, a task is created in the Inbox. Workflow execution resumes after the task is completed. If workflow execution reaches another human task node, a new task is created in the inbox. Human-in-the-loop workflows support durable execution. The platform preserves workflow state while waiting for user action, allowing workflow execution to resume from the paused step after the task is completed. In the Flow builder, the workflow execution panel displays the current execution state, such as *Waiting for human response.* Execution details are updated as tasks are completed and workflow execution progresses to downstream steps. After the required human-in-the-loop tasks are completed, workflow execution resumes and progresses to downstream workflow steps. # Inbuilt tool - call_transfer Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/call-transfer [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `call_transfer` tool transfers an active voice call to a SIP endpoint or a PSTN number from within an agent. Unlike `ESCALATE`, which routes a conversation through the platform's human escalation workflow, `call_transfer` performs a direct telephony transfer. Use it when your agent needs to hand the active call to another `SIP` or `PSTN` destination without creating a human escalation task. ## When to use Use `call_transfer` when you want to: * Transfer the active call to a SIP endpoint or to a PSTN number. * Route calls programmatically based on business logic. * Integrate with external contact-center or telephony systems. For structured human-agent escalation with routing rules, context propagation, and post-completion actions, use `ESCALATE` instead. ## Declare the tool `call_transfer` is an inbuilt tool, so it needs no HTTP endpoint or Tool Library entry. Declare it in the `TOOLS:` section so that your flow or reasoning agent can use it. ```yaml theme={null} TOOLS: call_transfer( callTransferType: string, message: string ) -> object ``` An agent that routes through `ESCALATE` with `voice.call_transfer_type: sip` or `pstn` invokes `call_transfer` itself, so you do not declare the tool for that path. To call `call_transfer` from a flow or let the model call it, declare it as shown above. A `CALL: call_transfer` step in a flow without a `TOOLS:` declaration fails validation with an undefined-tool error. ## Parameters | Parameter | Required | Description | | - | - | - | | `callTransferType` | Yes | Transfer type. Supported values: `sip`, `pstn`. | | `message` | No | Message played to the caller before the transfer begins. | | `language` | No | TTS language code for `message` (for example, `en-US`, `es-ES`). | | `headers` | No | Custom SIP headers to forward on a SIP transfer. Only headers permitted by your transfer header policy are sent. | ## Where the destination comes from For security, the destination is never taken from the model or from values passed in the call. The platform resolves it, so a prompt-injected conversation cannot redirect a live call. **PSTN transfers** dial the number configured in **Project Settings > Agent Transfer > Voice Gateway**. The number must be in E.164 format. If it is missing or invalid, the transfer fails and nothing is dialed. **SIP transfers** use the first destination available from this list: 1. The destination returned by a `RESOLVE_SIP_DESTINATION` step earlier in the flow. 2. The `sip_transfer_id` of the matching `ESCALATE` routing entry (when the transfer is triggered by escalation). 3. The `sipTransferId` default declared on the tool with `default_source: config`. 4. The SIP target configured in **Project Settings > Agent Transfer > Voice Gateway**. If you declare `approved_authorities` on the tool (dynamic SIP routing), the transfer requires a routing decision for the call. When none exists, or the resolved destination is not on the approved list, the transfer fails closed. It does not fall back to the project default. The transfer method (`refer`, `invite`, or `bye`) is also set in Project Settings, not in the call. ## How it works * The platform resolves the destination for the current call, as described above. * For dynamic routing, the SIP URI template is filled with the routed values, and the result is validated against the approved authorities. * If `message` is set, it is played to the caller before the transfer starts. * The runtime dispatches the transfer and waits for the real SIP outcome. The tool reports success only when the call actually connected. * If the transfer fails, the caller stays with your agent and the flow follows the `ON_FAILURE` path. On voice channels that cannot report a transfer outcome, the tool returns `status: "initiated"`. This means the transfer was started, not that it connected. Do not tell the caller the transfer succeeded in that case. ### Passing custom SIP headers When using SIP transfers, you can attach custom SIP headers with the `headers` parameter on `call_transfer`. For details, see [Pass Custom SIP Headers on Voice Transfer](/agent-platform/sending-custom-sip-headers). ## Errors | Code | Meaning | | - | - | | `INVALID_INPUT` | The call arguments did not match the tool's schema. | | `MISSING_PHONE_NUMBER` | PSTN transfer, but no valid PSTN number is configured for the project. | | `MISSING_SIP_TARGET` | SIP transfer, but no destination could be resolved. | | `INVALID_SIP_URI` | The transfer method is `invite` and the destination is not a valid `sip:` or `sips:` URI. | | `SIP_ROUTING_FAILED` | Dynamic routing could not be validated for this call. The transfer was not attempted. | | `SIP_TRANSFER_ID_INVALID` | The authored SIP destination could not be resolved or failed safety checks. The transfer was not attempted. | | `MISSING_SIP_URI_HEADER` / `INVALID_SIP_URI_HEADER` | A required header value for the SIP URI is missing or invalid for this call. | | `VOICE_TRANSFER_UNSUPPORTED` | The current voice channel does not support transfers. | | `VOICE_TRANSFER_DISPATCH_UNAVAILABLE` | The voice session that owns the call is not available. | | `TRANSFER_DISPATCH_FAILED` | The voice gateway failed to dispatch or observe the transfer. | ## Examples **Example 1: Transfer to a configured SIP destination** Use a project-configured SIP destination when all calls transfer to the same endpoint. ```yaml theme={null} AGENT: transfer_my_call TOOLS: call_transfer( callTransferType: string, message: string ) -> object FLOW: start -> handoff handoff: CALL: call_transfer( callTransferType: "sip", message: "Connecting you now." ) THEN: COMPLETE ON_FAILURE: transfer_failed transfer_failed: RESPOND: "I'm sorry, I couldn't connect you right now. Please try again later." THEN: COMPLETE ``` The destination is the SIP target from Project Settings. **Example 2: Route calls dynamically** Use a workflow to determine the transfer destination at runtime. This approach is useful when calls must be routed to different queues or contact-center destinations based on customer attributes such as region, language, or business unit. ```yaml theme={null} AGENT: transfer_my_call TOOLS: call_transfer(callTransferType: string, sipTransferId: string, message: string) -> object params: sipTransferId: default_source: config template: "sip:{{route.vdn}}@{{route.ip}}:{{route.port}};user=phone?User-to-User={{sip.user-to-user}}" approved_authorities: - "sip:421003@172.0.41.7:5060" - "sip:421004@172.0.41.7:5060" - "sip:421005@172.0.41.7:5060" - "sip:5210[0-9][0-9]@172.0.41.7:5060" confirm: never FLOW: resolve_destination -> transfer_step resolve_destination: REASONING: false RESOLVE_SIP_DESTINATION: FROM_WORKFLOW: "lookup_vdn_by_region_language" THEN: transfer_step ON_FAILURE: transfer_failed transfer_step: CALL: call_transfer( callTransferType: "sip", message: "Connecting you to the right desk." ) THEN: COMPLETE ON_FAILURE: transfer_failed transfer_failed: RESPOND: "I'm sorry, I couldn't connect you right now. Please try again later." THEN: COMPLETE ``` **Example 3: Transfer to a PSTN number** ```yaml theme={null} TOOLS: call_transfer( callTransferType: string, message: string ) -> object FLOW: transfer_step: CALL: call_transfer( callTransferType: "pstn", message: "Transferring you to our support line." ) THEN: COMPLETE ON_FAILURE: transfer_failed ``` The number dialed is the PSTN transfer number set in Project Settings. You cannot pass a phone number in the call. ## `call_transfer` vs `ESCALATE` `call_transfer` is an immediate, stateless telephony handoff with no record kept once the call leaves. `ESCALATE`, on the other hand, creates a trackable HumanTask (queueing, priority, SLA, optional ITSM integration) and works the same way for both voice and digital channels. | Feature | `call_transfer` | `ESCALATE` | | - | - | - | | **What it does** | Transfers the active voice call directly. | Creates and tracks a human escalation task. | | **Mechanism** | Direct SIP REFER / PSTN dial. No task record is created. | Creates a HumanTask with assign/claim/resolve lifecycle, SLA tracking, and optional ITSM ticket creation. | | **Channels** | Voice only. | Voice, chat, email, messaging, and campaign channels. | | **Task lifecycle** | Does not manage human task lifecycle. | Creates, tracks, and completes human tasks. | # Inbuilt tool - check_availability Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/check-availability [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `check_availability` tool checks whether a human agent is available to take the conversation. ## When to use Use `check_availability` before `transfer_to_agent` to confirm that an agent with the right skills or queue is online. If none is available, offer a callback or keep the caller with the AI agent. The tool requires Kore SmartAssist agent transfer to be configured for the project. If it is not configured, the call fails. ## Declare the tool ```yaml theme={null} TOOLS: check_availability( agentId: string, contactId: string, tenantId: string, projectId: string, skills?: string[], queue?: string, language?: string ) -> boolean ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `agentId` | Yes | The agent identifier. | | `contactId` | Yes | The contact (caller or user) identifier. | | `tenantId` | Yes | The tenant identifier. | | `projectId` | Yes | The project identifier. | | `skills` | No | Skills the human agent must have. | | `queue` | No | Queue to check. | | `language` | No | Language the human agent must support. | ## Returns A boolean: `true` when a matching agent is available, `false` otherwise. Invalid input returns the error code `INVALID_INPUT`. # Inbuilt tool - check_hours Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/check-hours [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `check_hours` tool checks whether the contact center is currently within business hours. ## When to use Use `check_hours` before you transfer to a human agent, so you can offer a callback or a message outside business hours instead of sending the caller to an unstaffed queue. The tool requires Kore SmartAssist agent transfer to be configured for the project. If it is not configured, the call fails. ## Declare the tool ```yaml theme={null} TOOLS: check_hours(agentId: string, hoursId: string) -> boolean ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `agentId` | Yes | The agent identifier. | | `hoursId` | Yes | The ID of the business-hours configuration to check. | ## Returns A boolean: `true` when the contact center is open, `false` when it is closed. Invalid input returns the error code `INVALID_INPUT`. ## Example ```yaml theme={null} FLOW: check_open: CALL: check_hours(agentId: "support_bot", hoursId: "weekday_hours") ON_RESULT: open: hand_to_human closed: after_hours_message ``` The `ON_RESULT` shape above is illustrative. Use the result-handling form that your flow already uses for boolean results. # Inbuilt tool - deflect_to_chat Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/deflect-to-chat [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `deflect_to_chat` tool moves a voice conversation to a chat channel. The caller continues with an automated dialog or with a human agent over chat. ## When to use Use `deflect_to_chat` when a task is better handled in chat than by voice, for example when the caller needs to receive a link, fill in a form, or type a long value. The tool speaks an optional message to the caller and starts the switch to chat. ## Declare the tool ```yaml theme={null} TOOLS: deflect_to_chat( deflectionType: string, message?: string, triggerType?: string, language?: string ) -> object ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `deflectionType` | Yes | `automation` to continue with the automated dialog in chat, or `agentTransfer` to continue with a human agent in chat. | | `message` | No | Message spoken to the caller before the switch. | | `triggerType` | No | What triggered the deflection: `userSelection` or `automationContext`. If omitted, `automation` uses `automationContext`, and `agentTransfer` uses `userSelection`. | | `language` | No | Language code for text-to-speech. | ## Returns The deflection branch that was taken: `DEFLECT_AUTOMATION` or `DEFLECT_AGENT_TRANSFER`. ## Example ```yaml theme={null} TOOLS: deflect_to_chat(deflectionType: string, message?: string) -> object FLOW: move_to_chat: CALL: deflect_to_chat( deflectionType: "automation", message: "I'll continue with you over chat. You'll receive a message shortly." ) THEN: COMPLETE ``` # Inbuilt tool - get_attachment_url Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/get-attachment-url [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `get_attachment_url` tool returns a time-limited public URL for a file that the user uploaded during the conversation. ## When to use Use `get_attachment_url` when you need to give the URL of an uploaded file to another system, such as an external API or a ticketing tool, or to share the file in a message. To forward the file to an external system for you, use [route\_attachment](/agent-platform/inbuilt-tools/route-attachment). ## Declare the tool ```yaml theme={null} TOOLS: get_attachment_url(attachment_id: string, expires_in_seconds?: number) -> object ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `attachment_id` | Yes | The ID of the uploaded attachment. | | `expires_in_seconds` | No | How long the URL stays valid. Default: 3600 (1 hour). Maximum: 86400 (24 hours). | ## Returns A URL that can be fetched from outside the platform, with the lifetime it is valid for. Depending on the storage backend, this is a presigned storage URL or a platform-served download link. The platform may apply a shorter lifetime to the platform-served link, so use the lifetime in the result. If the attachment does not exist or does not belong to the current conversation, the tool returns `ATTACHMENT_NOT_FOUND`. If `attachment_id` is missing, it returns `MISSING_PARAMETER`. # Inbuilt tools Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/index Inbuilt tools are tools that the Artemis runtime provides. They cover voice transfers, IVR and DTMF, human-agent handoff, and attachment handling. You do not build an HTTP endpoint or add a Tool Library entry for them. ## Use an inbuilt tool Declare the tool in the `TOOLS:` section of your agent, with its input fields and return type. Then call it from a flow (`CALL:`) or let a reasoning agent choose it. ```yaml theme={null} TOOLS: send_dtmf(digits: string, reason?: string) -> object FLOW: press_one: CALL: send_dtmf(digits: "1") THEN: COMPLETE ``` Keep these rules in mind: * **Declare the tool.** A `CALL:` to an inbuilt tool that is not declared in `TOOLS:` fails validation, and a reasoning agent only sees the tools you declare. * **Names are reserved.** If a project tool has the same name as an inbuilt tool, the project tool is ignored and the platform implementation runs. * **The platform owns sensitive values.** For example, `call_transfer` never accepts a destination from the model. See each tool's page. ## Available tools | Tool | Purpose | Available in | Page | | - | - | - | - | | `call_transfer` | Transfers a voice call to a SIP endpoint or PSTN number. | Voice agents | [call\_transfer](/agent-platform/inbuilt-tools/call-transfer) | | `transfer_to_agent` | Transfers the conversation to a human agent through an agent desktop provider. | All agents. On voice, only on supported gateways. | [transfer\_to\_agent](/agent-platform/inbuilt-tools/transfer-to-agent) | | `check_hours` | Checks whether the contact center is within business hours. | Agents using SmartAssist agent transfer | [check\_hours](/agent-platform/inbuilt-tools/check-hours) | | `check_availability` | Checks whether a human agent is available. | Agents using SmartAssist agent transfer | [check\_availability](/agent-platform/inbuilt-tools/check-availability) | | `set_queue` | Validates a routing queue. | Agents using SmartAssist agent transfer | [set\_queue](/agent-platform/inbuilt-tools/set-queue) | | `ivr_menu` | Plays a menu and collects one keypad press. | Voice agents | [ivr\_menu](/agent-platform/inbuilt-tools/ivr-menu) | | `ivr_digit_input` | Collects multi-digit keypad input. | Voice agents | [ivr\_digit\_input](/agent-platform/inbuilt-tools/ivr-digit-input) | | `send_dtmf` | Sends keypad tones into the active call. | Voice agents | [send\_dtmf](/agent-platform/inbuilt-tools/send-dtmf) | | `deflect_to_chat` | Moves a voice conversation to a chat channel. | Voice agents | [deflect\_to\_chat](/agent-platform/inbuilt-tools/deflect-to-chat) | | `get_attachment_url` | Returns a public URL for a file uploaded in the conversation. | All agents | [get\_attachment\_url](/agent-platform/inbuilt-tools/get-attachment-url) | | `route_attachment` | Sends an uploaded file to a configured external system. | All agents | [route\_attachment](/agent-platform/inbuilt-tools/route-attachment) | For DTMF concepts and secure collection, see [DTMF support in Artemis voice agents](/agent-platform/dtmf-support). # Inbuilt tool - ivr_digit_input Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/ivr-digit-input [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `ivr_digit_input` tool plays a prompt and collects a sequence of keypad digits, such as an account number, ZIP code, or PIN. ## When to use Use `ivr_digit_input` when the caller must enter more than one digit. For a single-key menu choice, use [ivr\_menu](/agent-platform/inbuilt-tools/ivr-menu). Collection stops when the maximum number of digits is reached, the ending key is pressed, or the inter-digit timeout expires. For secure collection of sensitive values, see [DTMF support in Artemis voice agents](/agent-platform/dtmf-support). ## Declare the tool ```yaml theme={null} TOOLS: ivr_digit_input( prompt: string, noInputConfig: object, noMatchConfig: object, maxDigits?: number, minDigits?: number, validation?: object, endingKeyPress?: string, interDigitTimeout?: number, language?: string ) -> object ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `prompt` | Yes | Message played to the caller. | | `noInputConfig` | Yes | What to do when the caller presses nothing: `timeout` (1–120 seconds), `maxRetries` (0–10), and an optional `message`. If `message` is omitted, the main prompt is replayed. | | `noMatchConfig` | Yes | What to do when the entry fails validation: `maxRetries` (0–10) and an optional `message`. If `message` is omitted, the main prompt is replayed. | | `maxDigits` | No | Upper bound on digits collected, 1 to 20. Default: 10. This is not a required length. | | `minDigits` | No | Minimum digits before the entry is accepted, 1 to 20. Must not exceed `maxDigits`. | | `validation` | No | `{allowedLengths: number[]}` to require an exact or enumerated length, for example `[12]` or `[15, 16]`. Each length must be within `minDigits` and `maxDigits`, and values must be unique. | | `endingKeyPress` | No | Key that ends the entry, for example `#`. | | `interDigitTimeout` | No | Milliseconds to wait between digits, 500 to 30000. Default: 2000. | | `language` | No | Language code for text-to-speech. | If `noMatchConfig.maxRetries` is greater than 0, you must also set `minDigits` or `validation.allowedLengths`. Otherwise every entry matches and the retries can never be used, so the call is rejected. ## Returns The tool returns the collected digits and the outcome: `success`, `noInput`, or `noMatch`. The collected digits are returned as a protected token (`{{PII:explicit_dtmf:}}`), not as raw digits, so that a keypad entry such as a PIN does not reach the model or traces. The platform restores the real value only where a step needs it. ## Example Collect a 12-digit account number, ending with `#`: ```yaml theme={null} TOOLS: ivr_digit_input( prompt: string, noInputConfig: object, noMatchConfig: object, maxDigits?: number, validation?: object, endingKeyPress?: string ) -> object FLOW: get_account: CALL: ivr_digit_input( prompt: "Enter your 12-digit account number, then press pound.", maxDigits: 12, validation: {allowedLengths: [12]}, endingKeyPress: "#", noInputConfig: {timeout: 8, maxRetries: 2}, noMatchConfig: {maxRetries: 2} ) ``` # Inbuilt tool - ivr_menu Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/ivr-menu [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `ivr_menu` tool plays a prompt, waits for a single keypad press, and maps the pressed key to a step or intent. ## When to use Use `ivr_menu` when callers choose from a numbered menu, such as "Press 1 for billing, 2 for support." For multi-digit entries such as account numbers, use [ivr\_digit\_input](/agent-platform/inbuilt-tools/ivr-digit-input). `ivr_menu` is available on pipeline voice agents through the Kore Voice Gateway. For how DTMF works, see [DTMF support in Artemis voice agents](/agent-platform/dtmf-support). ## Declare the tool ```yaml theme={null} TOOLS: ivr_menu( prompt: string, dtmfMappings: object[], noInputConfig: object, noMatchConfig: object, bargeIn?: boolean, language?: string ) -> {digit: string, matched: boolean, mappedIntent: string, branch: string} ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `prompt` | Yes | Message played to the caller. | | `dtmfMappings` | Yes | 1 to 12 mappings. Each mapping is `{key, nextStep, intent?}`, where `key` is a single character. | | `noInputConfig` | Yes | What to do when the caller presses nothing: `timeout` (1–120 seconds), `maxRetries` (0–10), and `message`. | | `noMatchConfig` | Yes | What to do when the key is not in the menu: `maxRetries` (0–10) and `message`. | | `bargeIn` | No | Lets the caller press a key while the prompt is playing. | | `language` | No | Language code for text-to-speech. | ## Returns | Field | Description | | - | - | | `digit` | The key the caller pressed. | | `matched` | Whether the key matched a mapping. | | `mappedIntent` | The `intent` of the matched mapping, if set. | | `branch` | `match`, `noInput`, or `noMatch`. | ## Example ```yaml theme={null} FLOW: main_menu: CALL: ivr_menu( prompt: "Press 1 for billing, 2 for support, or 0 for an agent.", dtmfMappings: [ {key: "1", nextStep: "billing", intent: "billing"}, {key: "2", nextStep: "support", intent: "support"}, {key: "0", nextStep: "escalate", intent: "agent"} ], noInputConfig: {timeout: 8, maxRetries: 2, message: "I didn't get that. Please choose an option."}, noMatchConfig: {maxRetries: 2, message: "That isn't a valid option."} ) ``` # Inbuilt tool - route_attachment Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/route-attachment [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `route_attachment` tool sends an uploaded file to an external system that you have configured as a named destination. ## When to use Use `route_attachment` to hand a user's uploaded file to another system, such as a document processor or an archive, without exposing the destination URL to the model. ## Configure a destination Declare each target in the agent's `DESTINATIONS:` section. The tool can only send to a destination declared here, so the model cannot supply its own URL. ```yaml theme={null} DESTINATIONS: doc_processor: url: "https://api.docprocessor.example/ingest" method: POST auth: bearer_token headers: X-Custom: "value" ``` | Field | Required | Description | | - | - | - | | `url` | Yes | HTTPS destination URL. Private and internal URLs are rejected. | | `method` | No | HTTP method, usually `POST` or `PUT`. | | `auth` | No | Auth mode or profile reference resolved by the runtime. | | `headers` | No | Static headers sent with the request. | ## Declare the tool ```yaml theme={null} TOOLS: route_attachment(attachment_id: string, destination: string, metadata?: object) -> object ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `attachment_id` | Yes | The ID of the uploaded attachment. | | `destination` | Yes | The name of a destination declared in `DESTINATIONS:`. | | `metadata` | No | Additional fields to include in the request. | ## What is sent The tool sends the destination a JSON body with `attachment_id`, `filename`, `mime_type`, `category`, `size_bytes`, and `download_url`, plus `metadata` if provided. The `download_url` is a time-limited public link, so the destination does not need platform credentials. ## Returns On success, the destination name, the HTTP status the destination returned, and the `attachment_id`. | Error code | Meaning | | - | - | | `MISSING_PARAMETER` | `attachment_id` or `destination` was not provided. | | `ATTACHMENT_NOT_FOUND` | The attachment does not exist or is not part of this conversation. | | `DESTINATION_ERROR` | The destination returned a non-success HTTP status. | An unknown destination name is rejected with the list of available destinations. If the destination does not respond within 30 seconds, the request times out and the tool returns an error. # Inbuilt tool - send_dtmf Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/send-dtmf [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `send_dtmf` tool sends keypad tones into the active voice call. Use it when your agent has to navigate an external IVR. ## When to use Use `send_dtmf` when your agent has called out to, or been connected to, another phone system and must press keys on the caller's behalf, for example "press 2 for billing" or entering an extension. To collect keypad input from the caller, use [ivr\_menu](/agent-platform/inbuilt-tools/ivr-menu) or [ivr\_digit\_input](/agent-platform/inbuilt-tools/ivr-digit-input). For DTMF concepts, see [DTMF support in Artemis voice agents](/agent-platform/dtmf-support). ## Declare the tool ```yaml theme={null} TOOLS: send_dtmf( digits: string, durationMs?: number, interDigitDelayMs?: number, reason?: string ) -> object ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `digits` | Yes | Digits to send. Allowed characters: `0-9`, `*`, `#`. Up to 64 characters. | | `durationMs` | No | How long each tone plays, 50 to 5000 ms. Default: 250. | | `interDigitDelayMs` | No | Pause between tones, 50 to 5000 ms. Default: 300. | | `reason` | No | Why the tones are sent, up to 256 characters. | ## Returns The tool returns the number of digits sent and the tone timing it used. The digits themselves are redacted in the result and in traces, so a value such as a PIN is not exposed. An invalid entry returns an error such as `VOICE_DTMF_INVALID` and sends nothing. ## Example ```yaml theme={null} TOOLS: send_dtmf(digits: string, reason?: string) -> object FLOW: choose_billing: CALL: send_dtmf(digits: "2", reason: "Select billing in the external IVR") THEN: COMPLETE ``` # Inbuilt tool - set_queue Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/set-queue [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `set_queue` tool validates a routing queue with the contact center, so you can confirm that a queue exists before you route a conversation to it. ## When to use Use `set_queue` when the target queue is decided at runtime, for example from the caller's intent or language, and you want to confirm that it is valid before calling `transfer_to_agent` with that `queueId`. The tool requires Kore SmartAssist agent transfer to be configured for the project. If it is not configured, the call fails. ## Declare the tool ```yaml theme={null} TOOLS: set_queue(agentId: string, queueId: string) -> boolean ``` ## Parameters | Parameter | Required | Description | | - | - | - | | `agentId` | Yes | The agent identifier. | | `queueId` | Yes | The queue to validate. | ## Returns A boolean: `true` when the queue is valid, `false` otherwise. Invalid input returns the error code `INVALID_INPUT`. # Inbuilt tool - transfer_to_agent Source: https://koreai.mintlify.app/agent-platform/inbuilt-tools/transfer-to-agent [Back to Inbuilt tools](/agent-platform/inbuilt-tools/index#available-tools) The `transfer_to_agent` tool transfers the conversation to a human agent through an agent desktop provider, such as Kore SmartAssist. ## When to use Use `transfer_to_agent` when the user asks to speak with a human, or when the issue cannot be resolved by the AI agent and you want the conversation handed to a contact-center agent with the conversation context. To move a voice call to a SIP or PSTN number without involving an agent desktop, use [call\_transfer](/agent-platform/inbuilt-tools/call-transfer). To create a tracked human task with SLA and queueing, use `ESCALATE`. ## Declare the tool ```yaml theme={null} TOOLS: transfer_to_agent( provider: string, skills?: string[], queueId?: string, priority?: number, postAgentAction?: string ) -> object ``` Declare only the fields you use. ## Parameters | Parameter | Required | Description | | - | - | - | | `provider` | Yes | The agent desktop provider to transfer to. | | `skills` | No | Agent skills required for routing. Up to 50. | | `queueId` | No | Target queue ID. | | `priority` | No | Priority from 0 to 10. | | `subType` | No | SmartAssist conversation subtype for contact-center routing. | | `namedAgents` | No | Specific agent IDs to route to. Up to 50. | | `namedAgentOptions` | No | Named-agent routing options: `waitForAgent` (boolean) and `waitDurationSeconds` (0–86400). | | `agentMatchingConditions` | No | Matching conditions: `skills`, `skillGroups`, and `agentGroups` (each up to 50). | | `metadata` | No | Additional metadata to pass to the provider. Must not exceed 16 KB. | | `postAgentAction` | No | What happens when the human agent disconnects: `return` (back to the AI agent) or `end`. | | `providerConfig` | No | Provider-specific configuration overrides. | ## Voice channels On voice, the transfer works only on gateways that can move the caller's call leg: Kore Voice Gateway, Genesys SAVG, and AudioCodes. On other voice surfaces, such as Twilio, LiveKit, VXML, and Genesys Audio Connector, the tool fails instead of reporting a handoff that did not happen. ## Example ```yaml theme={null} TOOLS: transfer_to_agent(provider: string, queueId?: string, priority?: number) -> object FLOW: hand_to_human: RESPOND: "Let me connect you with a specialist." CALL: transfer_to_agent(provider: "kore", queueId: "billing", priority: 5) THEN: COMPLETE ``` # Agent Platform overview Source: https://koreai.mintlify.app/agent-platform/index \{ **Artemis** } is the AI-native **Kore.ai Agent Platform** for building, running, and governing programmable agent systems at enterprise scale. It unifies conversational AI and agentic AI under a single programming model, letting one agent definition combine deterministic workflows with autonomous reasoning. Agent definitions are authored in Agent Blueprint Language (ABL™), compiled to a portable intermediate representation, and executed by a single runtime that handles orchestration, observability, governance, and scale. ## Key capabilities The platform introduces five capabilities that together change how enterprise agents are built and operated. Key Capabilities Key Capabilities | Capability | Description | | - | - | | **Agent Blueprint Language (ABL™)** | A typed, schema-driven domain-specific language for defining agent behavior. ABL provides formal constructs for goals, tools, memory, guardrails, handoffs, and delegation. Definitions are compiled and validated rather than stitched together as framework glue code. | | **Arch™ AI** | A built-in multi-agent system that drives the full agent lifecycle — design, build, test, deploy, observe, and optimize — with a direct connection to the ABL compiler and runtime. Arch AI authors and validates ABL on the user's behalf, so agent creation does not require writing ABL by hand. | | **Reasoning-aware Observability** | Native tracing across agent delegation chains. Every reasoning step, tool call, routing decision, and guardrail evaluation is captured as structured trace data, making agent behavior auditable for compliance and debugging. | | **Agent Lifecycle as Code** | Agent definitions are immutable YAML artifacts that are Git-native, diffable, and reviewable in pull requests. Agents move through development, staging, pilot, and production environments with the same software practices used for application code. | | **AI-native Foundation** | A re-architected runtime that delivers the same enterprise capabilities as platform with at least 50% lower infrastructure footprint. Horizontally scalable from the ground up, with multi-tier guardrails, circuit breakers, and rate limits built in. | ## Key components The platform is organized into four planes that sit on a shared enterprise foundation. Builders interact with the Studio plane; the Compiler and Runtime planes execute agents; the Operations plane provides observability and governance. The platform connects to enterprise ecosystems — AI models, channels, data sources, and cloud or on-premise infrastructure — through pluggable adapters. Architecture Architecture Each component delivers a focused capability. Components communicate through ABL-defined contracts and shared runtime services. | Component | Description | | - | - | | **Studio IDE** | The authoring environment for agents. Includes the Agent Builder for visual editing, the Arch AI Assistant for conversational authoring, and a Monaco-based code builder for direct ABL editing. | | **ABL Compiler** | Compiles ABL definitions through DSL → IR → Runtime stages. Output is Git-native and diffable. Compile-time validation catches missing tools, broken references, and contract violations before deployment. | | **Agent Runtime** | Executes compiled agents. Handles reasoning, flow control, supervisor agents, and multi-agent orchestration. Runs both the agentic and deterministic execution paths defined in ABL. | | **Arch AI** | Built-in AI that builds, manages, and optimizes agents. Operates across the full lifecycle, so users do not need to author ABL directly. Connects to the ABL compiler and runtime. | | **Tools and Integrations** | The connectivity layer for external systems. Supports HTTP, MCP, AWS Lambda, SDK invocations, the A2A protocol, sandboxed tools, OpenAPI definitions, and webhooks. Includes circuit breakers and retry logic. | | **Search AI** | The knowledge retrieval layer. Provides RAG, vector search, knowledge graph traversal, and reranking. Surfaces relevant context to agents during reasoning. | | **Workflow Engine** | Runs long-running workflows that span minutes to days. Supports human-in-the-loop steps, approval gates, and durable execution that survives pod restarts. | | **Agent Evals** | Automated evaluation framework. Provides test suites, regression tracking, and quality gates that run as part of the deployment pipeline. | | **Deep Observability** | Captures over 200 trace events per session. Includes runtime inspection, alerting, and session-level debugging across multi-agent interactions. | | **Agent Insights** | Analytics dashboards covering ROI tracking, experiments, user feedback, and usage patterns across deployed agents. | | **Operations and Analytics** | Operational tooling: Observatory, traces, diagnostics, alerts, deployments, CI/CD, evals, ROI tracking, backup and disaster recovery, and voice analytics with quality monitoring. | | **Security and Governance** | Enterprise controls: SSO, RBAC, guardrails, audit logging, encryption, KMS, multi-tenancy, PII detection, identity verification, and compliance reporting. | | **Enterprise Foundation** | The platform substrate. Built ground-up for AI-native architecture with proven scalability. Provides versioning, environment management, and certifications for PCI, SOC 2, GDPR, and FedRAMP. | ### Enterprise ecosystem The platform is agnostic to every ecosystem layer. Each layer is pluggable: customers can swap providers without rewriting agents. | Layer | Supported Providers and Types | | - | - | | **AI Models** | OpenAI, Anthropic, NVIDIA, Cohere, LLaMA 2, and additional providers. Open-source and custom fine-tuned models are supported through the same interface. | | **Channels** | Microsoft Teams, ServiceNow, Slack, Webex, Zoom, and additional channels. Agents are deployed across 27 channels in total — web, voice, messaging, email, and enterprise APIs — without per-channel rebuilding. | | **Data Sources** | Salesforce, SAP, Microsoft, VMware, Zendesk, and additional sources. Data is accessed through tool definitions, not embedded in agent logic. | | **Cloud / Infrastructure** | Microsoft Azure, Google Cloud, AWS, on-premise installations, and private VPC deployments. | ## Agent Blueprint Language ABL is the language used to define agents on the platform. It is a typed, schema-driven domain-specific language that produces an intermediate representation (IR) that the runtime can execute. The same blueprint compiles to the same AST and IR, whether it is authored as ABL syntax or as YAML. See [Agent Blueprint Language](/agent-platform/abl). ## Multi-agent orchestration The runtime supports four orchestration patterns out of the box. Each pattern is invoked through ABL constructs and is enforced by the runtime, including the difficult cases — parallel fan-out with barrier synchronization, asynchronous delegation that survives pod restarts, and multi-intent messages where a single user turn maps to multiple agents.
See [Multi-Agent Orchestration](/agent-platform/orchestrate). **Cross-runtime and supervisory features** * **Google A2A Protocol (v0.3.0+)** – Used for cross-runtime agent communication, including agents hosted outside the platform. * **Supervisor Routing** – Handoff and delegation decisions are guardrail-aware; the runtime can override an agent's chosen route when policy requires it. * **Pipeline Classifier** – Short-circuits the orchestrator for high-confidence intents, so simple requests do not pay the cost of a full reasoning loop. ## Agent lifecycle with Arch AI Arch AI is not a copilot or a suggestion engine; it is a multi-agent system with specialized sub-agents that drive every phase of the agent lifecycle. There is no separate manual step to author ABL, run tests, or promote between environments — Arch AI runs each phase end-to-end and surfaces work products for human review. See [Arch AI](/agent-platform/arch-ai). ## Agent lifecycle as code Agent definitions are first-class software artifacts. They are stored as YAML files, reviewed in pull requests, and promoted across environments using the same CI/CD practices used for application code. ### Environment pipeline Agents move through four environments. Each environment runs the same compiled IR; configuration and credentials are externalized. * **Dev** – Where agents are authored and unit-tested. * **Staging** – Where automated test suites and regression checks run. * **Pilot** – Where a limited production audience validates behavior on real traffic. * **Production** – Where the agent serves the full audience. ### YAML definitions Agent configurations are Git-native and diffable. A change to an agent's behavior produces a reviewable diff in version control, just like a change to application code. Compiler validation runs on every commit, so broken definitions never reach a downstream environment. ## How it works Every enterprise interaction is processed by two cognitive engines that operate in parallel. ABL defines both engines in a single language, the runtime executes both, and a shared memory layer coordinates state between them. This split-brain model allows a single agent definition to combine autonomous reasoning with deterministic, auditable execution.
See [Dual-Brain Architecture](/agent-platform/dual-brain-architecture). ## Infrastructure efficiency Agent Platform is rebuilt from the ground up on an AI-native enterprise foundation. The runtime is horizontally scalable and has a substantially smaller infrastructure footprint than Platform 1.0. For equivalent workloads, customers can expect at least a 50% reduction in infrastructure footprint relative to a Platform 1.0 deployment, with no reduction in capability. The reduction comes from architectural changes in the runtime — not from disabling features. ## Enterprise capabilities The platform is built for the scale, governance, and accountability that enterprises require. The four capabilities below are foundational and apply to every deployment. | Capability | What It Covers | | - | - | | **Security and Data Governance** | Every agent interaction is scoped, encrypted, and auditable by design. Tenant isolation, role-based access control, PII detection, and key management are built into the platform rather than added on. | | **Multi-tenancy at Scale** | A single platform deployment supports hundreds of agent deployments across business units, geographies, and brands. Each tenant is fully isolated, and policy is administered centrally. | | **Omnichannel Reach** | Agents are deployed across 27 channels — including web, voice, messaging, email, and enterprise APIs — with consistent behavior across channels. There is no per-channel rebuilding. | | **Compliance and Auditability** | Full audit trails, data minimization controls, right-to-erasure handling, and governance policies that satisfy regulated industries. Agent behavior is explainable, traceable, and accountable. | ## Business outcomes The Platform is designed around three measurable outcomes for the enterprise: | Outcome | How It is Achieved | | - | - | | **Build Faster** | Arch AI authors and validates ABL on the user's behalf, automated test matrices replace manual test authoring, and compile-time checks catch errors before they reach review. Time-to-market for new agentic solutions is shortened across every phase of the lifecycle. | | **Run Leaner** | The re-architected runtime delivers at least a 50% reduction in private VPC infrastructure footprint for equivalent workloads. The reduction is a direct cost saving with no trade-off in capability. | | **Scale Smarter** | ABL is AI-programmable, so the platform itself becomes a code-generation target. New capabilities can be added, refined, and operated through the same compiler-validated abstraction, with full observability and governance built in. | ## Deployment options Choose SaaS or self-hosted to match your infrastructure and compliance requirements. | Option | Description | | - | - | | **Cloud (SaaS)** | Default experience. Sign up, open Studio in your browser, and start building — nothing to install or manage. | | **Self-hosted (Enterprise)** | For organizations requiring on-premises deployment. Same platform capabilities, available for enterprise customers with data-residency or compliance requirements. | *** # Integrations overview Source: https://koreai.mintlify.app/agent-platform/integrations Artemis connects your agents to external systems so they can retrieve data, take action, hand off conversations to live agents, and reach users across your enterprise ecosystem. Each integration is a configured, authenticated connection to a third-party system that your agents and workflows use. The platform organizes integrations into four categories: * [**Actions and triggers integrations**](#actions-and-triggers-integrations) execute connector actions inside workflows. * [**Knowledge base connector integrations**](#knowledge-base-connector-integrations) bring content from external systems into a knowledge base. * [**Agent desktop integrations**](#agent-desktop-integrations) hand off conversations to a live agent desktop. * [**Channel integrations**](#channel-integrations) deploy agents to messaging, voice, and other user-facing surfaces. ## Actions and triggers integrations Use the [Integration node](/agent-platform/workflows#integration-node) in a workflow to execute connector actions, such as sending a Slack message, creating a Jira issue, or requesting approval by email, during workflow execution. Each integration exposes a set of authenticated actions that you configure as workflow steps. | Category | Integrations | | - | - | | Communication | Amazon SES, Amazon SNS, Discord, Gmail, Microsoft Outlook, Microsoft Teams, SendGrid, Slack, Twilio | | Productivity | Asana, ClickUp, Confluence, Google Calendar, Google Docs, [Jira](/agent-platform/integrations/jira-actions), Linear, Microsoft Excel, Microsoft Outlook Calendar, Microsoft Power BI, Microsoft SharePoint, Notion | | Storage | Airtable, Amazon DynamoDB, Amazon S3, Azure Blob Storage, Azure Cosmos DB, Databricks, Google Drive, Google Sheets, Microsoft OneDrive, MongoDB, Oracle Database | | CRM and sales | HubSpot, Microsoft Dynamics 365, Pipedrive, Salesforce, Shopify, Stripe | | Service management | ServiceNow, Zendesk | | AI and development | Anthropic Claude, GitHub, OpenAI, Microsoft Foundry | | Document processing | Amazon Textract, Azure Document Intelligence, Docling | | Others | Amazon SQS | ## Knowledge base connector integrations [Connectors](/agent-platform/knowledge#connectors) bring content from third-party systems into a knowledge base so agents can retrieve it during conversations. Setting up a connector requires authentication credentials, content and filter selection, field mapping to the knowledge base schema, and access permission configuration. | Category | Connectors | | - | - | | Cloud storage | Amazon S3, [Google Drive](/agent-platform/connectors/googledrive), [Box](/agent-platform/connectors/box), OneDrive, Dropbox, Azure Blob Storage, Google Cloud Storage buckets, Egnyte | | Productivity and docs | [Confluence Cloud](/agent-platform/connectors/confluence), Confluence Server, Axero, Notion, Google Docs, MS Word Online, Coda, Quip, Slite, GitBook, Airtable, Guru, Miro, Slab | | Project management | [Jira](/agent-platform/connectors/jira), Jira Data Center, Asana, Monday.com, ClickUp, Linear, Trello, Aha!, Hive, Shortcut, Teamwork, Wrike, YouTrack | | CRM and sales | [Salesforce](/agent-platform/connectors/salesforce), HubSpot, Pipedrive, Zoho CRM, Dynamics 365, Shopify | | IT and support | [ServiceNow](/agent-platform/connectors/servicenow), [Zendesk](/agent-platform/connectors/zendesk), FreshDesk, Intercom, PagerDuty, Freshservice, Front, HelpScout, Opsgenie, Re:amaze, Wolken Service Desk, xMatters | | Communication | MS Teams, Slack, Discord, Gmail, Google Chat, Zoom, LumApps, Zulip | | Developer tools | Bitbucket, [Custom Connector](/agent-platform/connectors/custom-connector), GitHub, GitLab, Stack Overflow Teams, ReadMe, Swagger/OpenAPI, JSON Connector, Datadog, Jenkins, JFrog Artifactory, Figma, TestRail, Zeplin | | Enterprise and HR | Oracle Knowledge, Workday, SAP, Bigtincan | | Media | YouTube, Vimeo | | Web and content | Web Crawler, RSS Feed, Sitemap, WordPress, DotCMS, Invision Community | | File sources | File Upload, [SFTP](/agent-platform/connectors/sftp), Rich Media | | Enterprise storage | [SharePoint](/agent-platform/connectors/sharepoint) | ## Agent desktop integrations Agent desktop integrations hand over an Artemis conversation to a live agent working in a contact center or service desktop. | Category | Integrations | | - | - | | Agent desktop | [Kore SmartAssist](/agent-platform/integrations/ccai), [Genesys Cloud](/agent-platform/integrations/genesys-cloud), Genesys Open Messaging, [Salesforce](/agent-platform/integrations/salesforce), ServiceNow, [Five9](/agent-platform/integrations/five9) | ## Channel integrations Channels connect deployed agents to the surfaces where users interact, including your own apps, voice calls, messaging apps, webhooks, and other agent frameworks. A [channel](/agent-platform/channels#channels) always binds to a deployment in a specific environment. Configure deployments first, then add channels. | Category | Channels | | - | - | | Messaging or digital | [Slack](/agent-platform/channels/slack), [LINE](/agent-platform/channels/line), [Microsoft Teams](/agent-platform/channels/ms-teams-copilot), [WhatsApp](/agent-platform/channels/whatsapp), [Messenger](/agent-platform/channels/messenger), [Twilio SMS](/agent-platform/channels/twilio), [Telegram](/agent-platform/channels/telegram), [Zendesk](/agent-platform/channels/zendesk), [Instagram](/agent-platform/channels/instagram), [Genesys](/agent-platform/channels/genesys), [Genesys Open Messaging](/agent-platform/channels/genesys-open-messaging), Email, [AI for Work](/agent-platform/channels/ai-for-work), [Kore Agent Assist](/agent-platform/channels/kore-agent-assist) | | Voice | [Realtime LLM Voice](/agent-platform/channels/voice#set-up-realtime-llm-voice-channel), [Pipeline Voice](/agent-platform/channels/voice#set-up-pipeline-voice-channel), VXML IVR, [Genesys Audio Connector](/agent-platform/channels/genesys-audio-connector), AudioCodes | | SDK | [Web SDK](/agent-platform/channels/web-sdk-channel), API, Mobile SDK | | Webhook | [Webhooks (HTTP Async)](/agent-platform/channels/webhook) | | Protocols | [AG-UI (CopilotKit)](/agent-platform/channels/ag-ui-copilotkit), [Agent-to-Agent (A2A)](/agent-platform/channels/agent-to-agent) | *** **Related articles:** * [Workflow > Integration node](/agent-platform/workflows#integration-node) * [Knowledge base connectors](/agent-platform/knowledge#connectors) * [Channels](/agent-platform/channels) # Agent AI integration Source: https://koreai.mintlify.app/agent-platform/integrations/agent-ai This guide explains how to configure Artemis in Agent AI to generate better next best action (NBA) suggestions in the Agent AI widget. ## Prerequisites Before configuring Artemis in Agent AI, verify you meet the following prerequisites in Artemis: * An active project. For more details, see [Create and manage projects](/agent-platform/projects). * A configured model. For more details, see [Create models](/agent-platform/administration/general-settings#models). * An active agent. For more details, see [Create agents](/agent-platform/create-agents). * An active deployment. For more details, see [Create a deployment](/agent-platform/deployment#create-a-deployment). * An active connection. For more details, see [Channels](/agent-platform/channels). ## Step 1: Set up a connection in Artemis 1. Sign in to **Artemis**. 2. Create a **Project**, if you don't have one. 3. Go to **Agents** and create an agent. 4. Go to **Deployments** > **Environments** and create a deployment in one of the following environments: **Development**, **Staging**, or **Production**. 5. Go to the **Channels** tab and locate the **Kore Agent Assist** channel. 6. Select **Kore Agent Assist**, then click **+ New**. 7. In the **New Kore Agent Assist Connection** window, enter the following details: | Field | Description | | - | - | | Display Name | Enter a name for the connection. | | Environment | Select an environment from the list. | | Initial Status | Select Active or Inactive. | 8. Click **Create**. 9. Select the connection you created. 10. In the **Overview** tab, note the **Domain URL**, **Environment**, **App ID**, and **API Key**. The App ID is the project ID, and the system generates the API Key when you create the connection. Copy and store the **API Key** for future use, as it is visible only once. If you lose it, select the channel you created, go to the **Overview** tab, and click **Rotate** to regenerate the API key. ## Step 2: Configure an Artemis app in Agent AI Configuring an Artemis app in Agent AI is a four-part process: entering app details, choosing the first request trigger, selecting conversation metadata, and adding custom metadata. ### Provide app details | Field | Description | | - | - | | App Name | A name for the app (up to 50 characters). For example, Travel Agent. | | Domain URL | The Artemis app's host URL. For example, `https://agents.kore.ai/`. | | Environment | The environment name as configured in the Artemis app. | | App ID | The Artemis app ID generated in the Artemis app. | | API Key | The API key from the Artemis app's API Key section. | ### Choose first request trigger Select one of the following options to define how Artemis receives its first request. These options are mutually exclusive. | Option | Description | | - | - | | Use 'Trigger Welcome message' as the first request to Agentic | Triggers the agent's welcome as soon as the widget loads, before any customer message. Artemis runs the agent's `ON_START` handler, including its `SET` and `CALL` statements, and returns the welcome rendered with live session values. For details, see [How to use ON\_START to greet users](/agent-platform/abl/how-to/use-on-start-welcome). | | Pass landing summary as first request to Agentic App | Sends the app-customer conversation's landing summary as the first request to Artemis on agent transfer. | If you don't select either option, Artemis generates NBAs only when a customer sends a message. ### Select conversation metadata Select the metadata to pass to the Artemis app. This preserves context during transfers from automation to human agents. | Metadata | Description | | - | - | | Landing Summary | Summary of the customer-app conversation before transfer to a human agent. | | Agent-Customer Chat History | Conversation history between the customer and human agent up to the latest message. | | Custom Data | Collected custom data. | | Intermittent Summary | Agent-generated mid-conversation summary. | ### Add custom metadata Add a custom metadata script in the code editor to pass to the Artemis app. The system validates and highlights errors as you enter the script. To configure an Artemis app: 1. Sign in to **AI for Service**. 2. Go to **Agent AI** > **Configurations** > **Linked Services** > **Agentic Configuration**. 3. Click **+ Add Agentic Copilot App**. 4. Enter the app name. 5. Enter the **Domain URL**, **Environment**, **App ID**, and **API Key** from your Artemis app. 6. Select **Use 'Trigger Welcome message' as the first request to Agentic** or **Pass landing summary as first request to Agentic App**. 7. Expand the **Pass Metadata** section and select the metadata to pass. 8. Expand the **Add Custom Metadata** section and enter your script in the code editor. Use the **Enlarge** or **Copy** buttons as needed. 9. Click **Test Connection & Save**. # CCAI outbound campaign integration Source: https://koreai.mintlify.app/agent-platform/integrations/campaign This guide explains how to configure the AI for Service (CCAI) outbound voice campaign to place calls through Artemis and trigger a specific deployed agent for each call. It reflects the current Campaigns for Artemis implementation. ## How agent triggering works AI for Service doesn't select an Artemis agent by name. Three bindings link the campaign to the agent: 1. The AI for Service campaign links to an Artemis project through a project ID and an API key. 2. The campaign selects one Artemis voice connection, a voice channel connection in that project. 3. The voice connection binds to a specific deployment (deploymentId). That deployment is the agent that runs when the call connects. The deployment bound to the selected voice connection determines which agent answers the campaign call. To change the triggered agent, either change the deployment bound to that connection or select a different connection. ## Prerequisites Before configuring the campaign, confirm the following: * Access to the CCAI Campaigns module in AI for Service. * An Artemis domain, environment, project ID, and a project-scoped API key (`abl_...`). * A deployed agent in that project. * At least one voice channel connection in that project and environment, bound to the target deployment, with outbound calling enabled. Complete this configuration first. AI for Service reads these values when the campaign links to the project. 1. Create or select the project. Note the Project ID, for example `019dd2a0-d7cf-7854-a3c1-eaa78c6d8d6d`. 2. Build and deploy the agent that handles campaign calls. Deploying the agent to a target environment (dev, staging, or prod) produces a deployment. Campaign calls trigger the deployment, not the draft agent. 3. Create a voice channel connection and bind it to the deployed agent: * Supported voice channel types include korevg (Voice Gateway, primary), genesys\_audio\_connector, voice\_twilio, voice\_pipeline, and voice\_realtime (any isVoice type in the manifest). * Configure the caller ID, phone numbers, and carrier/trunk (BYOC SIP or the shared carrier). * Enable outbound calling (config.outboundCall must not be false). Inbound calling is independent (config.inboundCall). * Bind the connection to the deployment so the connection carries a deploymentId. This binding determines which agent answers. 4. Generate a project API key (`abl_...`) with access to the project. AI for Service uses this key to authenticate to the internal campaign endpoints. These endpoints require API-key authentication (`requireApiKeyAuth`) and the `channel_connection:read` and `workflow:execute` permissions to list connections. `GET /api/internal/campaigns/voice-connections?projectId=...&environment=...` returns the connection with a non-null `voiceAppSid`, a resolved `trunkName`, `dialoutUrl`, and the bound `deploymentId`. 1. Go to **CCAI** > **Campaigns**. 2. Click **Create New Voice Campaign**, or edit an existing voice campaign. [Learn more](/ai-for-service/contact-center/campaigns#create-a-voice-campaign). 3. Select the dialing mode **Agentless**, and turn on **Enable Campaigns for Artemis**. 4. Click **Edit Settings** and enter the Artemis linkage: | Field | Description | Example | | :- | :- | :- | | Domain Name | Artemis domain URL | `https://agents.kore.ai` | | Environment | Environment of the deployment/connection | `dev`, `staging`, `prod` | | Project Name | Display name for reference | Customer Support Project | | Project ID | Artemis project ID | `019dd2a0-d7cf-7854-a3c1-eaa78c6d8d6d` | | API Key | Project-scoped API key | `abl_...` | 5. Click **Save**. 6. In the campaign's caller ID or voice connection section, select the voice connection whose bound deployment is the target agent. This selection (the connectionId) determines the agent. 7. Configure the contact list, DNC settings, scheduling, and dispositions as usual, then save or start the campaign. The environment entered must match the environment where the deployment and voice connection reside. The endpoints filter connections by `projectId` and `environment`. ## Supported dialing modes Campaigns for Artemis supports one dialing mode, **Agentless**. The agent handles the call end-to-end; Start Flow and experienceFlow aren't used in V2. In Agentless mode, the outbound tag carries the complete userInfo object, so standard and custom contact fields reach the Runtime session. For details, see [Read Campaign Contact Data in an Artemis Runtime Session](/agent-platform/integrations/read-campaign-contact-data). ## What reaches the agent AI for Service places campaign context in the Jambonz call tag. Artemis copies this context into the Runtime session at `session.data.values.customerData`, where the agent reads it with dotted-path interpolation: ```yaml theme={null} {{customerData.userInfo.firstName}} # contact / custom fields {{customerData.campaignId}} # campaign control fields {{customerData.campaignInstanceId}} {{customerData.campaignDialListId}} ``` Example `customerData`: ```yaml theme={null} { "provider": "abl_v2", "dialerType": "Agentless", "campaignId": "campaign-123", "campaignInstanceId": "instance-456", "campaignDialListId": "contact-789", "phoneNumber": "+15551234567", "userInfo": { "firstName": "John", "lastName": "Doe", "policyNumber": "P-8801" }, "agent": { "agentId": "..." } } ``` ## Post call status When a voice call reaches a **final** state, the KoreVG gateway sends a terminal `call:status` event carrying the telecom outcome. Artemis presents those reason fields on the runtime session at `session.data.values.callStatus`, so an authored agent can read them from the DSL as `{{callStatus.*}}`. This enables workflows to distinguish scenarios such as no answer, caller cancelled, busy, or network failure, and implement different retry, escalation, or follow-up logic accordingly. | Field | Meaning | Example | | - | - | - | | `callStatus.call_status` | Raw provider status | `completed`, `no-answer`, `failed`, `busy`, `canceled` | | `callStatus.sip_status` | Raw SIP response code | `200`, `487`, `480`, `404` | | `callStatus.sip_reason` | Raw SIP reason phrase | `OK`, `Request Terminated`, `Temporarily not available` | | `callStatus.call_termination_by` | Who ended an **answered** call | `caller`, this value is null when the call does not connect. | | `callStatus.duration` | Answered-call duration (seconds) | `31`, this value is null when the call does not connect. | | `callStatus.disposition` | Normalized disposition | `completed`, `no-answer`, `busy`, `failed`, `machine_detected` | **Disposition mapping** | Scenario | Raw `call_status` / SIP | `callStatus.disposition` | | - | - | - | | Caller answered, then hung up | `completed` / 200 OK, `call_termination_by: caller` | `completed` | | Bot ended the call | `completed` / 200 OK, `call_termination_by: jambonz` | `completed` | | Rang, not answered | `no-answer` / 487 Request Terminated | `no-answer` | | Busy / rejected | `busy` | `busy` | | Voicemail / answering machine | `AMD` | `machine_detected` | | Phone off / unreachable / airplane | `failed` / `480 Temporarily not available` | `failed` | | Invalid / unroutable number | `failed` / `404 Not Found` | `failed` | **Reading it in the agent DSL** ```yaml theme={null} COMPLETE: - WHEN: callStatus.call_termination_by == "caller" RESPOND: 'Thanks for your time — goodbye.' ``` **Trigger workflows** When an agentless campaign call is unsuccessful on its final retry - the callee hits voicemail / an answering machine, or never picks up (no-answer / busy / failed), Artemis fires a platform event, `campaign.call_unsuccessful`. Take appropriate actions on call failure by authoring a workflow that subscribes to that event. Workflows receive the same information in the `campaign.call_unsuccessful` trigger payload as shown below. ```yaml theme={null} triggerPayload: callStatus: no-answer # raw provider status (use this) disposition: no-answer # normalized (use this) amdType: no-answer # LEGACY/overloaded — kept for back-compat only sipStatus: 487 # present for never-connected statuses sipReason: Request Terminated callTerminationBy: caller # present only if the call connected duration: 12 # present only if the call connected campaignId / campaignInstanceId / attemptNumber / maxAttempts / isFinalAttempt / userInfo / ... ``` * `call_termination_by` and `duration` are **only** present when the call actually connected (an answered call). For `no-answer`/`failed`/`busy` they are null. * Because a `callStatus` is terminal event, hence most most useful to **post‑call** workflows. * `callStatus.call_status` is the raw provider string; `callStatus.disposition` is the normalized one. Prefer `disposition` for conditional post-call workflows. ## Call status relay back to AI for Service After a successful dial, AI for Service registers the call through `POST /api/internal/campaigns/call-context` (stored in Redis with a 24-hour TTL). As the call progresses, Runtime relays `call:status` events back to AI for Service, normalizing raw states to campaign values such as `trying`, `early-media`, `in-progress`, `busy`, `no-answer`, `completed`, `failed`, and `machine_detected`. Runtime posts these values to the CCAI campaign status endpoint (`/campaign/api/v1/trigger/callStatus`, and the progressive-agent variant). Runtime requires the `CAMPAIGN_API_BASE_URL` setting to know where to send these updates. ## Verify with a test call 1. Create a test contact with distinctive, non-production values. 2. Confirm the campaign points to a non-production project or environment and the intended voice connection. 3. Start the campaign against one test record. 4. Confirm the call reaches the expected deployment or agent, the one bound to the selected connection. 5. Inspect the Runtime session or trace and locate `data.values.customerData`. Confirm the expected contact fields appear under `customerData.userInfo`. 6. Confirm the agent's responses resolve the intended `{{customerData...}}` paths. 7. Confirm call-status updates appear in CCAI. Don't add raw customer data to application logs to verify interpolation. Use the authorized session/trace view according to the project's PII policy. ## Troubleshooting | Symptom | Likely cause | Recommended action | | :- | :- | :- | | Voice connections don't load in CCAI | Wrong domain or environment, invalid or expired API key, or wrong project scope. | Verify the Domain Name, Environment, and Project ID, and confirm the API key belongs to the project. | | A connection is missing from the list | The connection is not a voice type, is in another environment, or is deleted. | Confirm the connection is a voice channel in the selected project and environment. | | Calls don't trigger | The connection lacks outbound calling, has no phone number, or the campaign is not active. | Enable outbound calling on the connection, verify the caller ID or contacts, and set the campaign to Active. | | The wrong agent answers | The selected connection is bound to a different deployment. | Rebind the connection to the intended deployment, or select the correct connection. | | A BYOC call fails or uses the wrong trunk | The BYOC connection lacks a distinct carrier, so the fallback is refused. | Set the BYOC connection's own carrier; it must not equal the shared default. | | `customerData.userInfo` is missing at runtime | The dialing mode didn't send the full contact object. | Check the dialing mode; see the contact-data guide's dialing-mode limitation. | ## CCAI validation messages | Message | Meaning | Action | | :- | :- | :- | | Invalid domain name provided. | The domain isn't a valid URL. | Verify the domain host. | | API Key is invalid or expired. | The key is wrong, expired, or revoked. | Generate or copy a valid API key. | | API key doesn't have access to the specified Project ID. | The key is valid but not for this project. | Use a key with access to the project. | | Project ID not found. | The project doesn't exist or is inaccessible. | Verify the project ID. | | Unable to connect to Agent Platform campaign voice connections endpoint. | The domain or environment doesn't resolve the endpoint. | Verify the Domain Name and Environment. | | Unable to connect to the specified Agent Platform environment. | The platform is unavailable, or a server error occurred. | Retry later, or contact support. | ## Security and privacy Store the Artemis API key securely. Never share it in tickets, screenshots, or logs. If the key is shared, update the campaign settings. Campaign contact data can contain PII. Include only the fields the conversation needs, avoid exposing internal identifiers, and apply the project's guardrail and PII policies before using customer data in model-visible prompts or spoken responses. ## Customer identity on outbound calls On outbound voice calls, the platform identifies the customer who was called, not the channel's own number. Each customer gets their own contact, memory, and history, and workflows and reporting after the session ends see the called customer. **Session fields** | Field | Meaning | Notes | | - | - | - | | `session.anonymousId`, `session.sessionPrincipalId`, `session.rawCallerId`, `caller_ani` | The customer, on every call | On outbound calls, this is the called customer | | `session.rawFrom`, `session.rawTo` | The literal upstream From and To values | | | `session.calledNumber`, `session.rawCalledNumber`, `dnis` | The To leg of the call | The customer on an outbound call; the channel's own number on an inbound call | | `session.callDirection` | `inbound` or `outbound` | Set from the gateway's reported call direction | | `session.channelNumber` | The channel's own number | Present on every call, regardless of direction | Use `session.anonymousId` for the customer on both inbound and outbound calls, `session.channelNumber` for the channel's own number, and `session.callDirection` to distinguish the two. **Example** ```yaml theme={null} TOOLS: record_disposition(phone: string, channel_number: string, direction: string) -> object description: "Save the call disposition for the customer on this call." FLOW: save_disposition save_disposition: REASONING: false CALL: record_disposition WITH: phone: session.anonymousId channel_number: session.channelNumber direction: session.callDirection AS: disposition RESPOND: "Thank you, your preference has been saved." ``` *** **Related articles:** * [Read Campaign Contact Data in an Artemis Runtime Session](/agent-platform/integrations/read-campaign-contact-data) * [Contact Center AI integration with Artemis](/agent-platform/integrations/ccai) # Contact Center AI integration Source: https://koreai.mintlify.app/agent-platform/integrations/ccai [Back to list of integrations](/agent-platform/integrations#agent-desktop-integrations) For secure, bidirectional communication between an Artemis AI application and contact center agents, you can integrate the AI for Service Contact Center app with an Artemis project. The integration requires: * **Contact Center AI app configuration** in AI for Service, using tenant, project, and token details from Artemis. * **Artemis project configuration**, using tokens and IDs from the Contact Center app, set as the default Agent Transfer route in Artemis. This connection is shared infrastructure. After it's configured, Artemis routes escalated conversations to the Contact Center app and returns its responses to the user. For voice, the same connection also carries the live call transfer. This article covers the shared connection setup and digital channel (chat and email) routing. For voice channel setup, complete the steps in this article first, then go to [CCAI voice transfer](/agent-platform/integrations/ccai-voice-transfer). ## Prerequisites * Access to AI for Service as admin or app owner to create and publish Contact Center apps. * Artemis app access with permission to open Agent Chat, Profile, Integrations, Deployments, and API Keys. * Access to an Artemis project. * A secure token generator or password manager to create secrets that meet the required complexity rules. * A test agent or flow that can reach an agent-transfer path, to validate the connection after setup. * The integration is enabled on request. To enable it, [contact Support](https://support.kore.ai/). ## Step 1: Contact Center app configuration (Required for both digital and voice channel interactions) 1. Log in to the **AI for Service** platform. 2. Create a new app and select Contact Center as the app type. 3. Complete the onboarding journey for the new app. 4. Go to **Flows & Channels**. 5. Select **Artemis Integration**. (Note: The integration is enabled on request. If you are unable to see the page, [contact Support](https://support.kore.ai/).) 6. Enter the following fields. These fields require values to be fetched from the Artemis project. | **Field** | **Where to fetch the value** | **Implementation notes** | | - | - | - | | Artemis tenant ID | Artemis: Agent Chat > initiate chat > Debug > Data or Artemis Profile > Workspace ID | Use the tenant/workspace ID for the Artemis workspace being connected. | | Artemis project ID | Artemis: Agent Chat > initiate chat > Debug > Data | Use the project ID for the target Artemis project. | | Artemis webhook URL | Artemis: Project > Integrations > Kore SmartAssist > Connect > Webhook Channel Callback URL | Copy the callback URL exactly as shown. | | Artemis access token | Bring your own key | Create a secret that meets the [token and secret rules](/agent-platform/integrations/ccai#token-and-secret-rules). | | Artemis auth token | Artemis: Project > Deployments > API Keys > Create a platform key | Create a platform key and copy the generated token. The token is shown only once, so copy it immediately; it typically starts with `abl_`. Start with the least-privilege scope your design needs, such as Read Sessions for session history and transcripts. | 7. Review all Artemis Integration fields, then click **Enable Artemis Integration**. If an integration already exists for the app, click **Update Artemis Integration** instead. 8. Confirm the integration status shows **Enabled**. 9. Review and publish the Contact Center app. Don't swap the **Artemis access token** and the **Artemis auth token**. The access token is a secret you generate yourself and must match the Webhook Secret you enter in Step 2. The auth token is the platform key generated in Artemis. Swapping these two values causes authentication failures. ## Step 2: Artemis project configuration (Required for both digital and voice channel interactions) 1. Go to **Project** > **Integrations** > **Kore SmartAssist** > **Connect**. 2. Configure the following fields: | **Artemis connection field** | **Source** | **Notes** | | - | - | - | | Connection Name | Enter a unique connection name. | Use a name that identifies the environment and AI for Service app, such as AI for Service-uat-contact-center. | | Base URL | CCAI → Flows and Channels → Artemis Integration → CCAI host URL | Use the environment root URL, not a deep `/builder`, `/app`, or `/hooks` path. Example: `https://platform.kore.ai/`. | | CCAI App Token (optional) | CCAI → Flows and Channels → Artemis Integration → CCAI auth token | Populate this when the implementation needs Artemis to retrieve post-transfer voice transcripts. | | XOCC Webhook Channel Token | CCAI → Flows and Channels → Artemis Integration → Web token | Copy exactly from AI for Service. | | Webhook Secret | Bring your own key | Use the same value as the Artemis access token entered in AI for Service Artemis Integration. | | App ID | CCAI → Flows and Channels → Artemis Integration → CCAI stream ID | Copy exactly. | | Account ID | CCAI → Flows and Channels → Artemis Integration → Account ID | Copy exactly. | ## Step 3: Configure digital channel routing ### How routing works * The conversation remains with the AI agent until a transfer is triggered. * Artemis resolves the transfer configuration and selects the active **connection as per the settings**. * Artemis sends the transfer request and conversation context to **CCAI**. * CCAI routes the conversation to the appropriate queue or agent based on its routing configuration. * If enabled, Artemis retrieves the conversation transcript after the transfer is complete. When you enable the Artemis integration, CCAI creates two default flows: Artemis Voice Flow and Artemis Chat Flow. These default flows don't contain an automation node. Instead, they include nodes such as check agent availability, check business hours, and set queue. You can customize the Start Flow to add routing behavior beyond the defaults, and link it to conditional flows for in-queue, no-agent, and out-of-office-hours scenarios. After a conversation lands in a queue, CCAI also evaluates any additional routing configuration set for that queue, such as preferred-agent or conditional routing. If your implementation sets routing values in the escalate block and also customizes the Start Flow, confirm with Support which configuration takes precedence in your environment before relying on it in production. ### Routing options customers can choose | **Option** | **Behavior** | **When to choose it** | | - | - | - | | Default routing | All eligible transfers use the connection and queue configured under Agent Transfer > Default Routing. | Best starting point for one CCAI environment or one general support destination. | | Queue-based routing | The DSL can pass a queue ID; the transfer carries or resolves a queue so CCAI can deliver to the right team. | Use for billing, technical support, claims, or other stable team destinations. | | Skill or named-agent routing | The DSL can pass a skill ID or agent ID; the transfer specifies required skills or selected agents when the use case supports it. | Use for language, product, certification, or VIP specialization. Keep a queue fallback. | | Priority-based routing | A numeric priority influences the relative urgency of the transfer. | Use sparingly for genuinely urgent or high-value interactions; scale being 0 to 10. 0 highest and 10 lowest. | | Post-agent action | Return to bot keeps the session in the bot lifecycle after the live-agent leg; End session closes it. | Choose based on whether the bot should resume, collect feedback, or terminate after handoff. | ### Routing precedence * Values assigned in the escalate block apply to that specific transfer. * If a value is not assigned, Artemis uses the configured Agent Transfer default. * If data is missing or an ID is invalid, routes to the configured general-support fallback in CCAI. ### Set up digital channel routing * Go to **Project Settings** → **Agent Transfer** → **Default Routing** → **Connection** and select the **integration**. * Review Queue, Priority, and Post-Agent Action. * Set up a digital channel. When an agent escalates a conversation, Artemis routes it to the Contact Center app via the configured connection. The Contact Center app's response, returned through the connection, is delivered to the user. ## Step 4: Test and verify 1. For digital channels, enter a message requesting a transfer to a live agent. 2. Verify the expected Artemis behavior: an escalation request is initiated and the conversation is transferred to CCAI. 3. Verify the expected CCAI behavior: a new chat conversation is routed to the configured queue or agent. Accept the interaction. 4. Verify the transfer details: * **Artemis Sessions/Trace:** Confirm that an escalation event is recorded for the transfer. * **Artemis Transfer Sessions:** Confirm the provider is **SmartAssist**, the channel is chat, and the transfer completes successfully. * **CCAI Agent Console:** Confirm the interaction is offered to the expected queue or agent, accepted successfully, and transitions to the **Connected** state. ## Example use case: dynamic customer-profile routing You can send a customer to a different CCAI queue based on information Artemis already has about them. In this example, Premium customers who need help with Payments go to a specialist priority queue, while other customers go to the most appropriate standard queue. Use the same pattern for language, product, region, account status, or any other reliable profile value. ### 1. Choose the profile information Use information that's available before the agent transfer and that has a clear source of truth. Keep the allowed values consistent across the customer system, Artemis, and CCAI. For example: customer tier (Premium or Standard), product or intent (Payments, Cards, General), language, or customer status (authenticated, unauthenticated, high-risk). ### 2. Define the decision rules Evaluate rules from top to bottom. Put the most specific rule first, followed by broader rules, and always finish with a safe default. * Premium + Payments → Payments Priority queue, Payments skill, high priority. * Standard + Payments → Payments Support queue, Payments skill, normal priority. * Premium + any other product → Priority Support queue, Premium skill (if supported). * Missing or unknown profile data → General Support queue. Don't block the customer because a profile field is unavailable. The queue names and priority labels are examples. Replace them with the actual CCAI queue names, IDs, skills, and approved priority scale for your environment. ### 3. Add queues and skills in the Artemis escalate block Create the required queues and skills in CCAI first, then copy their IDs into the Artemis routing logic. Use the queue ID as the destination and the skill ID as the agent qualification, and keep a general-support queue as the fallback. Set these values inside the [`escalate` block](/agent-platform/abl/reference/multi-agent-and-supervisor#escalate) in the Artemis agent that invokes the live-agent transfer. Example DSL pattern: ```yaml theme={null} escalate { reason = "customer_requested_agent" if customer.tier == "Premium" and customer.product == "Payments" { queueId = "QID_PAYMENTS_PRIORITY" skillId = "SKILL_PAYMENTS" priority = 1 } else if customer.product == "Payments" { queueId = "QID_PAYMENTS_SUPPORT" skillId = "SKILL_PAYMENTS" priority = 5 } else if customer.tier == "Premium" { queueId = "QID_PRIORITY_SUPPORT" skillId = "SKILL_PREMIUM" priority = 5 } else { queueId = "QID_GENERAL_SUPPORT" skillId = null priority = 5 } postAgentAction = "RETURN_TO_BOT" } ``` Replace the placeholder values with the actual CCAI IDs. The exact property names depend on the Artemis DSL version. Don't use display names when the integration requires IDs. Field reference: * `queueId`: the CCAI queue that should receive the interaction. * `skillId`: the CCAI skill required for the interaction, such as Payments, Spanish, or Premium Support. * `agentId`: an optional named-agent destination. Use it only when the business case requires direct routing. * `priority`: the agreed numeric priority. * `postAgentAction`: what Artemis does after the live-agent leg, such as `RETURN_TO_BOT` or End session. ### 4. Test the escalation * Premium customer with a Payments request → Payments Priority queue plus Payments skill. * Standard customer with a Payments request → Payments Support queue plus Payments skill. * Premium customer with another product → Priority Support queue plus Premium skill. * Missing or invalid profile data → General Support fallback queue. * Selected queue has no agents or is outside business hours → CCAI in-queue or no-agent flow. The connection configuration establishes communication between Artemis and CCAI. The escalate block determines the destination for an individual conversation. Keep changing workforce decisions, such as agent availability and queue capacity, in CCAI rather than encoding them in the bot. ## Points to note * **Attachments**: When a user shares a file during a conversation in Artemis, the file and its associated metadata are transferred to the CCAI agent. The agent can use the attachment and its metadata as part of its reasoning and response generation. See [supported attachment types](/agent-platform/administration/security-observability-settings#attachments). * **Transfer lifecycle events**: When a session is escalated to a human agent in the contact center, the platform captures key events in the transfer lifecycle, such as transfer initiated, transfer completed, human agent connected, and session ended, as **platform events**. These events include transfer metadata that can be consumed by downstream integrations, workflows, and analytics. ### Token and secret rules | Requirement | Rule | | - | - | | Length | Use 32 to 512 characters. | | Character mix | Use three of the four character types: uppercase letters, lowercase letters, digits, symbols. | | Webhook Secret matching | Must match the Artemis access token configured in AI for Service. | | Storage | Don't paste secrets into tickets, chat, emails, or SOP. Store them in the approved vault. | | Rotation | Rotate the paired AI for Service Artemis access token and Artemis Webhook Secret together. | ### Validation checklist | Validation item | Expected result | | :- | :- | | Create AI for Service app of Contact Center type | New app is visible and onboarding is complete. | | Configure Artemis integration | Populate tenant ID, project ID, webhook URL, access token, and auth token. | | Enable of the integration | Integration status shows enabled or active. | | Publish AI for Service app | Publish the latest version successfully. | | Create Artemis Kore SmartAssist connection | Connection saved with unique name, base URL, tokens, app ID, and account ID. | | Match webhook secret | Matches the AI for Service Artemis access token exactly. | | Initiate test interaction | You can initiate a flow without authentication errors. | | Check debug data | Tenant ID and project ID align with the intended workspace and project. | ## Troubleshoot integration issues | Symptom | Likely cause | Recommended action | | - | - | - | | 401 or 403 response | Token mismatch or expired API key | Re-copy the Artemis auth token and verify the webhook secret. | | Webhook events aren't received | Incorrect callback URL or channel token | Verify the callback URL and CCAI Webhook Channel Token. | | Wrong tenant or project is connected | Values copied from another workspace or project | Fetch tenant ID and project ID again from Agent Chat → Debug → Data or confirm Workspace ID in Profile. | | Connection name rejected | Connection name isn't unique | Use a unique name. | | Integration works before publish but not after changes | Changes saved but not published | Publish the AI for Service app again. | *** **Related articles:** * [CCAI voice transfer](/agent-platform/integrations/ccai-voice-transfer) * [Know more about Contact Center](/ai-for-service/contact-center) * [Transfer sessions](/agent-platform/transfer-sessions) # CCAI voice transfer Source: https://koreai.mintlify.app/agent-platform/integrations/ccai-voice-transfer [Back to CCAI integration](/agent-platform/integrations/ccai) This article covers the SIP-based voice call transfer from an Artemis voice agent to a human agent in CCAI: creating the SIP trunk, binding it to a human destination, configuring Artemis for SIP REFER, wiring the ABL, and testing the live call handoff. Complete [Step 1: Contact Center app configuration](/agent-platform/integrations/ccai#step-1-contact-center-app-configuration) and [Step 2: Artemis project configuration](/agent-platform/integrations/ccai#step-2-artemis-project-configuration) in [CCAI integration](/agent-platform/integrations/ccai) before you start. Those steps are required for both digital and voice channels. ## Prerequisites * The shared connection setup in [CCAI integration](/agent-platform/integrations/ccai) is complete and the integration status shows **Enabled**. * Artemis production and your CCAI production environment are in the **same cloud region/cluster**. This keeps the transfer on the internal Kore voice gateway. If the environments are in different clusters, the call leaves the Kore network and requires a purchased PSTN number and provider routing. * **App Owner** role in CCAI. A standard builder role can't complete every integration and telephony step. * An available CCAI queue or named agent, and access to the CCAI Agent Console. * Two separate audio endpoints for the final test: the caller on a phone or a telephony-enabled video call, and the human agent on a separate device. * Approved secret storage for the Artemis Platform Key and the shared webhook secret. ## End-to-end path ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A[Caller] --> B[Artemis voice agent] B -->|call_transfer sip| C[SIP REFER] C --> D[CCAI SIP trunk] D --> E[Start Flow] E --> F[Human agent] ``` ## Step 1: Create the CCAI SIP trunk and routing number Go to **Contact Center AI** → **Flows & Channels** → **Voice Gateway** → **SIP Numbers** → **Configure SIP Trunk**. This creates the internal destination that Artemis will REFER the call to. | **CCAI SIP field** | **Known-good baseline** | | - | - | | SIP URI | Generated by CCAI. Example: `sip:savg-cusprd-sbc1.kore.ai:5060` | | Network | List of IP addresses. | | Incoming IPs | Example: `54.172.60.0, 54.172.60.3, 54.244.51.0, 54.244.51.3`; confirm the current tenant allowlist with telephony administration. | | E.164 Syntax | Checked | | DID / routing ID | A unique routing identifier, such as `4848898755`. On this same-cluster internal path it does not need to be a purchased public DID. | | DTMF Types | `RFC 2833` | | SIP Transport | `UDP` | | Credentials / termination | Optional. Leave blank unless a Kore telephony administrator gives you tenant-specific values. | Keep exactly one leading `sip:` in the resulting SIP number (`sip:@:5060`). A double `sip:` prefix breaks the destination. ## Step 2: Route the SIP number to a human destination 1. Click **Next**. Reserve or select the SIP routing number and enable **Inbound Calls**. Select the intended CCAI app. 2. Create a **Call Start Flow**. Supply a non-empty description and select the SIP number when prompted. 3. Connect the Start node to an **Agent Transfer** node, then choose the intended queue, skill, or named employee. 4. For every receiving employee, enable Contact Center access, Agent Console as the default experience, Voice, and the required queue/skill membership. 5. Publish the Start Flow. Attaching a Start Flow can return an already-published CCAI app to **In Development**. Republish the app after this step (see Step 3). Before moving on, confirm: * Agent Console is open and the human agent is **Available**. * The target queue/skill or named-agent route is correct. * The app and every relevant voice flow show **Published**. ## Step 3: Publish the CCAI app Publish the CCAI app and all affected voice/Start flows. ## Step 4: Configure Artemis agent transfer for SIP REFER In Artemis, go to **Project Settings → Agent Transfer** and enter the following configurations under **Voice Gateway**, in addition to the **Default Routing connection** configured in [CCAI integration](/agent-platform/integrations/ccai#set-up-digital-channel-routing): | **Setting** | **Value** | | - | - | | Gateway Type | `Kore Voice Gateway` | | Transfer Method | `SIP REFER`. The underlying `transfer_method` parameter also accepts `invite` or `bye`; use `refer` (SIP REFER) unless your design calls for a different signaling method. | | Header Passthrough | On | | SIP Transfer Target URI | `sip:@:5060` with exactly one `sip:` prefix. | | PSTN target | Blank for an internal, same-cluster SIP path | | SIP URI is SmartAssist trunk | `On`. This is required for transcript/recording synchronization and two-way audio. | | Allowed Transfer Headers | Optional; add only headers required by your queue/skill routing design. [Learn More](/agent-platform/sending-custom-sip-headers). | ## Step 5: Configure the voice-transfer mechanism in the ABL Use the [`Escalate` block](/agent-platform/abl/reference/multi-agent-and-supervisor#escalate) with `routing.voice.call_transfer_type` set to `sip` (and `transfer_method` set to `refer`, `invite`, or `bye`) to drive the live SIP call handoff. This is the recommended pattern — it works unchanged for chat and voice, with no separate tool definition needed: ```yaml theme={null} ESCALATE: routing: - voice: { call_transfer_type: sip, transfer_method: refer } # transfer_method: refer | invite | bye ``` Alternatively, you can call the `call_transfer` tool directly. If you do, define it in the TOOLS block as follows: ```yaml theme={null} TOOLS: call_transfer(callTransferType: string, phoneNumber?: string, sipTransferId?: string, message?: string, headers?: object) -> object description: "Transfer the live voice call to a human agent over SIP. Call this whenever the caller asks for a human, an agent, a representative, or to be transferred. Always pass callTransferType as sip." ``` Add a clear PERSONA instruction so the agent invokes the transfer promptly and only on a genuine request: * When the caller explicitly asks to speak with or be transferred to a human employee, representative, live agent, customer-service agent, or real person, invoke the transfer immediately with `callTransferType` set to `sip`. Don't continue troubleshooting, offer alternatives, or ask a follow-up question before calling the tool. * Never merely say that a transfer is starting, underway, or complete. Invoke the transfer and rely on its result. * Don't treat negated, hypothetical, quoted, historical, or third-party mentions of a human representative as a transfer request. The TOOLS block may appear before or after PERSONA; ABL construct order isn't significant. Compile, lint, and validate before deploying. Non-blocking metadata warnings don't replace runtime validation. ## Step 6: Deploy and bind the voice channel in Artemis Saving an ABL change updates only the draft. It doesn't change the behavior of a live call until you create and bind a new deployment. 1. Open **Artemis → Deployments** and choose the environment used by the Realtime Voice channel. 2. Create a deployment, choose **Customize per agent**, and select **Auto-create from draft** for the voice agent. Confirm the correct Entry Agent, then deploy. The previous deployment can keep draining; it doesn't have to be retired first. 3. Go to **Channels**, open the Realtime Voice channel, and confirm it points to the same environment and the newly active deployment/version. 4. Configure other channel properties and save. 5. Confirm the Artemis Realtime Voice channel has a working inbound DID if dialing from a phone. **This public inbound DID is different from the CCAI SIP routing ID.** After deployment, start a brand-new voice call to pick up the change; existing sessions keep the runtime state they began with. Verify the Realtime Voice channel binding every time you create a new version — the agent configuration can be correct while live calls still use an older deployment. ## Step 7: Test and verify Don't use the CCAI browser softphone for the final test — voice transfer doesn't work correctly from that path. Call the Artemis agent from an external mobile phone or a telephony-enabled video call (for example, Google Meet), and keep the human CCAI Agent Console on a separate machine/audio endpoint when possible. 1. Publish the CCAI app and all affected voice/Start flows. Confirm the human agent is **Available** before dialing. 2. Call the Artemis agent and say a test phrase such as **"Transfer me to a human agent."** 3. Verify the expected Artemis behavior: the `call_transfer` tool is invoked with `callTransferType=sip`. The configured transition message (if any) plays once, and a SIP REFER/transfer session begins. 4. Verify the expected CCAI behavior: a new voice interaction is offered to the configured queue or agent. Accept the interaction. 5. Confirm success: the AI stops responding to the caller, the human and caller hear one another, and two-way audio continues until the human ends the call. Evidence to inspect: * **Artemis Sessions/Trace:** a `call_transfer` tool invocation, not only an escalation event. * **Artemis Transfer Sessions:** provider **SmartAssist**, channel **voice**, and a successful/connected lifecycle. * **CCAI Agent Console:** offered interaction, assigned queue/agent, acceptance, and connected state. * **Audio:** caller and human can interrupt/respond naturally; the AI no longer answers after acceptance. ## Troubleshoot voice transfer issues | What you observe | Most likely cause / next action | | - | - | | AI never answers | Fix the Artemis Realtime Voice inbound DID/provider/channel binding. The transfer ABL can't run before an Artemis session exists. | | AI promises a transfer, but no CCAI offer | Confirm the live ABL calls the transfer with `sip`, the draft was deployed, and the voice channel is bound to that deployment. | | Transfer session starts, but no CCAI offer | Check the active SmartAssist connection, CCAI Artemis Integration status, SIP target, Start Flow, app publish status, and agent/queue availability. | | CCAI offer is accepted, but there's silence and the AI still responds | Turn on **SIP URI is SmartAssist trunk**; confirm SIP REFER, the single-prefix target URI, and that the test didn't originate from the SmartAssist browser softphone. | | SIP target doesn't connect | Remove any double `sip:` prefix. Use `sip:@:5060`. | | Studio/soft test differs from a phone test | Use the supported external phone/video-call path. If a public carrier intercept occurs before an Artemis session, repair inbound DID/provider provisioning. | | Changes look right but old behavior remains | Republish CCAI after flow changes; create a new Artemis deployment after ABL changes; start a new call. | ## Final go/no-go checklist * Artemis and CCAI are on matching production environments, and the CCAI user is App Owner. * The CCAI SIP trunk and routing number exist; the Call Start Flow routes to a real human destination and is published. * The Artemis Integration fields are saved in CCAI, and the corresponding connection fields in Artemis are populated. * A new agent version/deployment is active, and Realtime Voice is bound to it. * Agent Transfer uses Kore Voice Gateway, SIP REFER, the correct SIP target, and SmartAssist trunk **On**. * A new external voice test produces a CCAI offer, human acceptance, AI disengagement, and two-way audio. ## Points to note * **Conversation transcripts and recordings**: Recordings and transcripts from voice calls in CCAI are available in Artemis after the session ends, under Sessions logs. It may take up to 10 minutes to present them in Artemis due to post-session processing. *** **Related articles:** * [CCAI integration](/agent-platform/integrations/ccai) * [Sending custom SIP headers](/agent-platform/sending-custom-sip-headers) * [Escalate block reference](/agent-platform/abl/reference/multi-agent-and-supervisor#escalate) * [Transfer sessions](/agent-platform/transfer-sessions) # Five9 integration Source: https://koreai.mintlify.app/agent-platform/integrations/five9 [Back to list of integrations](/agent-platform/integrations#agent-desktop-integrations) For secure, bidirectional communication between the AI application and live representatives, you can integrate Five9 Virtual Contact Center with an Artemis project. The integration requires: 1. [Five9 setup](#five9-setup) — configure a Campaign (Five9's queue equivalent) that agents can log into. 2. [Artemis project configuration](#configure-the-connection-in-artemis) using the tenant name, campaign name, and auth mode from Five9. Set it as the default Agent Transfer route. After the configuration is complete, Artemis routes escalated conversations to the configured Five9 Campaign, and a live Five9 agent can pick up the interaction from their agent desktop with full conversation context carried over. **Campaign = Five9's queue.** Five9 uses "Campaign" — specifically an Inbound Campaign — as its routing/queue construct, the same role a "queue" plays in Genesys or Salesforce. It is not a marketing or outbound-dialing campaign. Agents log into a Campaign to receive routed interactions, and Artemis checks that Campaign for logged-in agents before creating a transfer. **Important considerations** * **Authentication modes**: The integration supports two auth modes — `anonymous` (authenticates using only the tenant name, no stored Five9 user credentials, recommended default) and `supervisor` (authenticates using a dedicated Five9 username and password). Each transfer performs a fresh authentication against Five9; Artemis does not cache or reuse tokens across conversations. * **Agent availability check**: Before creating a conversation, Artemis checks whether any agent is logged into the configured Campaign. If none are available, the transfer is blocked and the end user sees: *"We are currently unable to service your request. Please contact us during normal business hours."* The conversation then continues with the bot. ## Prerequisites * Access to Five9 VCC Admin for your tenant. * Supervisor auth only: a dedicated Five9 user (username and password) created in Five9 VCC Admin > User Management. * Artemis project access with permission to open **Project** > **Integration**, and **Project Settings** > **Agent Transfer**. *** ## Five9 setup For more details on the setup steps, refer to the Five9 documentation for Admin Console > Create Inbound Campaigns ↗. 1. Log in to Five9 VCC Admin. 2. Create a new **Inbound Campaign**: * Go to **Campaigns** > **Add Campaign**. * Configure the campaign settings, ensuring it is enabled for chat/messaging. * Note the exact **Campaign Name**. 3. Log in at least one agent to the campaign: * Go to **Users** > **User Management**. * Select an agent and log them into the campaign. *** ## Configure the connection in Artemis 1. Go to **Project** > **Integration** > **Agent Desktop** > **Five9**. 2. Select **Connect**. The **Connect Five9** configuration dialog opens. 3. Configure the following fields: | Artemis connection field | Notes | | - | - | | Connection Name | A friendly name to identify this connection, such as `My Five9`. | | Tenant Name | Your Five9 tenant name, such as `your-tenant`. | | Campaign Name | Five9 Campaign for inbound routing. Must match an active Campaign with agents logged in. | | Host (optional) | Five9 API host. Defaults to `app.five9.com` if left empty. | | Auth Mode | Enter `anonymous` or `supervisor`. | | Username (optional) | Required for supervisor auth mode. | | Password (optional) | Required for supervisor auth mode. | | Callback URL (optional) | Override webhook callback URL — auto-generated if left empty. Leave empty unless you have a specific reason to override it. | 4. Click **Create Connection**. 5. Use the Five9 integration for Agent Transfer routing:
Go to **Project Settings** > **Agent Transfer** > **Default Routing** > **Connection** and select the integration. When an agent escalates a conversation, Artemis routes it to the configured Five9 Agent Desktop system. ## Verify the connection 1. In Five9, make sure at least one agent is logged into the configured Campaign — otherwise the transfer is blocked with the "unable to service your request" message. 2. Trigger an agent transfer in a test conversation in your Artemis project. 3. Open the Five9 agent desktop and accept the incoming interaction. 4. Confirm that messages sent from the test conversation appear on the agent side, and that agent replies appear back in the Artemis conversation. *** ## Troubleshoot integration issues | Symptom | Likely cause | Recommended action | | :- | :- | :- | | Transfer fails with an authentication error | Invalid Tenant Name, or invalid Username/Password for supervisor mode | Re-check the Tenant Name against Five9 VCC Admin, and re-verify credentials if using supervisor auth mode. | | Transfer fails with a conversation creation error | Campaign Name doesn't match an active, chat/messaging-enabled Campaign | Verify the Campaign Name exactly matches an active Campaign in Five9 VCC Admin. | | User sees "We are currently unable to service your request" | No agent is currently logged into the configured Campaign | Log an agent into the Campaign in Five9, then retry the transfer. | | Agent joins but doesn't receive messages, or agent replies don't reach the user | Callback URL is unreachable, or an incorrect Callback URL override was entered | Leave Callback URL empty to use the auto-generated URL, or verify a manual override is publicly reachable. | | Transfer intermittently fails only for some conversations | Five9 returned a "service migrated" response while switching data centers | Artemis retries automatically on the new data center. If failures persist, retry the transfer or contact support. | *** **Related articles:** * [Transfer sessions](/agent-platform/transfer-sessions) * [Escalate to a human agent](/agent-platform/escalate) # Genesys Cloud integration Source: https://koreai.mintlify.app/agent-platform/integrations/genesys-cloud [Back to list of integrations](/agent-platform/integrations#agent-desktop-integrations) For secure, bidirectional communication between the AI application and live representatives, you can integrate Genesys Cloud CX Agent Desktop with an Artemis project. The integration requires: 1. [Genesys Cloud setup](#genesys-cloud-setup) — create a queue, an inbound message flow, and a deployment in Genesys Cloud Admin. 2. [Artemis project configuration](#artemis-project-configuration) using the organization ID, deployment ID, and queue name from Genesys Cloud. Set it as the default Agent Transfer route. After the configuration is complete, Artemis routes escalated conversations to the configured Genesys Cloud queue, and a live Genesys Cloud agent can pick up the interaction from their Agent Desktop with full conversation context carried over. **Important considerations** * **Integration modes**: The integration supports two modes — **Web Messaging** (Web Messaging API + Messenger Deployment, recommended) and **WebChat** (Guest Chat v2 API + Third-Party Widget Deployment). Choose the mode that matches the deployment you create in Genesys Cloud. * **No OAuth**: This is a configuration-only connection. Artemis authenticates to Genesys Cloud using the deployment and organization details entered during setup — there's no auth profile to configure. ## Prerequisites * Access to Genesys Cloud Admin with permission to create queues, inbound message flows, and deployments (Messenger or Widget). * Artemis project access with permission to open **Project** > **Integration**, and **Project Settings** > **Agent Transfer**. *** ## Genesys Cloud setup 1. Go to **Admin** > **Contact Center** > **Queues**. 2. Create or select the target queue. 3. Note the exact **queue name** — it's case-sensitive and must match what you enter in Artemis. 4. Assign agents to the queue and configure its service level and routing rules. 1. Go to **Admin** > **Architect**. 2. Create an inbound message flow that transfers to the queue created above. 3. Add a **Transfer to ACD** action and select the target queue. 4. Save and publish the flow. Which deployment you create depends on the integration mode you plan to use in Artemis. | Mode | Where to create it | What to copy | | - | - | - | | Web Messaging | **Admin** > **Message** > **Messenger Deployments** — create a deployment and attach it to the inbound message flow from step 2. | Messenger Deployment ID | | WebChat | **Admin** > **Contact Center** > **Widgets** — create a **Third Party** widget and route it to the flow from step 2. | Widget Deployment Key | Genesys Cloud is region-specific. Confirm the URLs for your organization's region. | Region | Login URL | WebSocket URL (Web Messaging) | | - | - | - | | US East / West | `https://login.mypurecloud.com` | `wss://webmessaging.mypurecloud.com/v1` | | Germany | `https://login.mypurecloud.de` | `wss://webmessaging.mypurecloud.de/v1` | | Ireland | `https://login.euw2.pure.cloud` | `wss://webmessaging.euw2.pure.cloud/v1` | | Australia | `https://login.mypurecloud.com.au` | `wss://webmessaging.mypurecloud.com.au/v1` | You can also find your login URL in your browser's address bar while signed in to Genesys Cloud Admin. The API URL is derived automatically by replacing `login.` with `api.`. Go to **Admin** > **Account Settings** > **Organization Settings** and copy the **Organization ID**. *** ## Artemis project configuration 1. Go to **Project** > **Integration** > **Agent Desktop** > **Genesys Cloud**. 2. Select **Connect**. The **Connect Genesys Cloud** configuration dialog opens. 3. Configure the following fields: | Artemis connection field | Source | Notes | | - | - | - | | Connection Name | Enter a unique connection name. | A friendly name to identify this connection, such as `My Genesys Cloud`. | | Integration Type | Choose based on the deployment created in [Genesys Cloud setup](#genesys-cloud-setup). | `messaging` — Web Messaging (recommended). `webchat` — Guest Chat v2 / Third-Party Widget. | | Genesys Cloud Login URL | Genesys Cloud region URL. See [Genesys Cloud setup](#genesys-cloud-setup). | Used to derive the Guest Chat API base URL for WebChat mode. | | Organization ID | Genesys Admin → Account Settings → Organization Settings. | Copy exactly. | | Deployment ID | Web Messaging: Admin → Message → Messenger Deployments. WebChat: Admin → Contact Center → Widgets → Third Party. | Must match the Integration Type selected above. | | Queue Name | Genesys Admin → Contact Center → Queues. | Must exactly match the queue referenced by your Architect inbound message flow. Case-sensitive. | | Genesys WebSocket URL (optional) | Genesys Cloud region URL. See [Genesys Cloud setup](#genesys-cloud-setup). | Required for Web Messaging mode, such as `wss://webmessaging.mypurecloud.com/v1`. | | Webhook Callback URL (optional) | Bring your own key. | ABL Runtime webhook for inbound agent events: `https://your-runtime/api/v1/agent-transfer/webhooks/genesys?tid=`. | 4. Click **Create Connection**. 5. Use the Genesys Cloud integration for Agent Transfer routing:
Go to **Project Settings** > **Agent Transfer** > **Default Routing** > **Connection** and select the integration. When an agent escalates a conversation, Artemis routes it to the configured Genesys Cloud Agent Desktop queue. ### Validation checklist | Validation item | Expected result | | :- | :- | | Create a Genesys Cloud queue | Queue is visible in Admin > Contact Center > Queues and has assigned agents. | | Publish inbound message flow | Flow is published in Architect and transfers to the target queue. | | Create a deployment | Messenger Deployment ID or Widget Deployment Key is available. | | Create Artemis Genesys Cloud connection | Connection saved with a unique name, login URL, organization ID, deployment ID, and queue name. | | Set default Agent Transfer route | Genesys Cloud connection is selected in Project Settings > Agent Transfer > Default Routing. | | Initiate test transfer | Test conversation escalates without connection errors. See [verify the connection](#verify-the-connection). | ## Verify the connection 1. Trigger an agent transfer in the test conversation in your Artemis project. 2. Open Genesys Cloud **Agent Desktop** and accept the incoming interaction from the configured queue. 3. Confirm that messages sent from the test conversation appear on the agent side, and that agent replies appear back in the Artemis conversation. *** ## Troubleshoot integration issues | Symptom | Likely cause | Recommended action | | - | - | - | | Connection fails with a "not configured" error | One or more required fields are missing from the connection | Re-open the connection and verify Organization ID, Deployment ID, and Queue Name are all filled in. | | Transfer fails immediately in Web Messaging mode | Genesys WebSocket URL is missing | Add the region-specific WebSocket URL to the connection — required for the `messaging` integration type. | | Genesys returns a 400 error when creating the conversation | Integration Type doesn't match the deployment created | Confirm the Deployment ID belongs to the same mode selected in Integration Type, and switch modes if needed. | | WebSocket connection is refused (403) | The Genesys deployment restricts allowed domains | In the deployment's settings in Genesys Cloud, allow all domains or add the Artemis domain to the allow list. | | WebSocket connection not found (404) | Wrong WebSocket URL for the region | Verify the region-specific endpoint against the region URLs table in [Genesys Cloud setup](#genesys-cloud-setup). | | Agent never receives the interaction | Queue Name doesn't exactly match the queue referenced by the Architect inbound message flow | Confirm the queue name is spelled and cased exactly as in Genesys Admin > Contact Center > Queues, and that the flow is published. | **Related articles:** * [Transfer sessions](/agent-platform/transfer-sessions) * [Escalate to a human agent](/agent-platform/escalate) # Jira Actions integration Source: https://koreai.mintlify.app/agent-platform/integrations/jira-actions [Back to list of integrations](/agent-platform/integrations#actions-and-triggers-integrations) Use the Jira integration to enable Artemis agents to perform actions on Jira Cloud through Action Tools. Before creating a Jira Action Tool, configure an OAuth authentication profile that allows Artemis to securely access your Jira Cloud instance. ## Prerequisites * A Jira Cloud site with administrator access. * An [Atlassian Developer account](http://developer.atlassian.com). * Permission to create OAuth 2.0 (3LO) applications in Atlassian. * An Artemis project with permission to create Integration Profiles and Action Tools. ## Step 1: Create a Jira OAuth 2.0 application 1. Sign in to the **Atlassian Developer Console**. 2. Create a new **OAuth 2.0 (3LO)** application. 3. Configure the required OAuth scopes for the Jira APIs your agent will use. The required scopes are also listed in the auth profile creation page in Artemis. 4. Copy the **Callback URL** displayed in the Artemis authentication profile (created in Step 2) and add it as an **Authorized Redirect URI** in the Atlassian application. 5. Save the application. 6. Copy the generated **Client ID** and **Client Secret**. These will be required when creating the authentication profile. For detailed instructions, see the [Atlassian documentation](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/). ## Step 2: Create a Jira integration profile for authentication 1. In Artemis, navigate to **Integrations**. 2. Select **Jira**. The page lists all the supported actions and triggers. Click **Connect**. 3. This takes you to the auth profile creation page. 4. Select **OAuth App** as the authentication type. Refer to [this](/agent-platform/administration/integrations-settings#auth-profiles) for more details on creating an auth profile. 5. Enter the client ID and secret. 6. Ensure that: 1. The scopes listed in the auth profile page are enabled for the app in the Atlassian account. 2. The redirect URI configured in the Atlassian OAuth app matches the URI shown in this profile page. Click **Create Profile**. After creating the profile: 1. Click **Authorize** to test the Jira configuration. 2. Sign in to your Atlassian account when prompted. 3. Review and approve the requested permissions. 4. After authorization completes successfully, verify that the auth profile status changes to **Active**. ## Step 3: Create a Jira action tool 1. Navigate to **Tools**. 2. Click **New Tool** → **Action Tool**. 3. Choose **Jira** as the integration. 4. Select the required Jira action. 5. Select the configured Jira authentication profile. 6. Configure the required input and output parameters. 7. Save the tool. [Learn More about Action Tools](https://docs.kore.ai/agent-platform/tools/actions). ## Step 4: Use the action tool After the Action Tool is created, you can add it to your agent and invoke it during conversations. For information about configuring tool inputs, outputs, and runtime behavior, see the [Tools](/agent-platform/tools/actions#assign-an-action-to-an-agent) documentation. ## Troubleshoot common issues | **Issue** | **Resolution** | | - | - | | Authorization fails or does not complete | Verify that the **Callback URL** configured in the Atlassian OAuth application exactly matches the Callback URL displayed in the Artemis authentication profile. | | Authentication window remains pending | Complete the OAuth consent flow in the same browser session and ensure the callback URL is reachable. | | Invalid client credentials | Verify that the **Client ID** and **Client Secret** are copied correctly from the Atlassian Developer Console. | | API calls fail after authentication | Confirm that the OAuth application includes all required Jira API scopes for the actions your agent performs. | # Quality AI integration Source: https://koreai.mintlify.app/agent-platform/integrations/quality-ai This guide explains how to configure the By AI Agent metric in Quality AI to run AI-based evaluations through Artemis. The metric uses a parent metric with multiple sub-metrics, each with its own question, weight, and logic. A single evaluation call processes all sub-metrics and returns results with justifications. Supervisors can pass metadata to the Execute API request through `requestMeta`. The system maps configured custom fields to key-value pairs and includes `conversationId` by default. By AI Agent connects to your Artemis project through project-based authentication set up when you create the metric. ## When to use this metric Use this metric type for evaluation scenarios that require: | Scenario | Description | | - | - | | **Multi-dimensional assessments** | Evaluate several facets (sub-metrics) under one parent metric. | | **Autonomous AI analysis** | Use AI agents to interpret, reason, and assess interactions with contextual understanding. | | **Weighted evaluations** | Assign different weights to sub-metrics to prioritize specific aspects. | | **Efficient execution** | Reduce redundant API calls by evaluating multiple sub-metrics in a single agentic request. | | **Seamless configuration** | Select agentic applications from the same workspace without entering endpoint URLs. | | **Context-aware evaluations** | Pass custom metadata (for example, customer ID, ticket ID) to enable external data lookups during evaluation. | ## Prerequisites Before creating a By AI Agent metric, confirm: * Access to both Quality AI and Agent Platform, using the same workspace on both. * Permissions to view and deploy project/app. * The By AI Agent Metric feature is enabled for your workspace account. * At least one project/app is configured on the Agent Platform with the required response structure. * (Optional) Custom fields in the Quality AI custom field registry to enable request metadata mapping. * A deployed Artemis project with its Project Key and Channel ID. *** ## Configure a By AI Agent metric ### Step 1: Navigate to metric configuration 1. Navigate to **Quality AI** > **Configure** > **Evaluation Forms** > **Evaluation Metrics**. 2. Click **+ New Evaluation Metric**. 3. From the **Evaluation Metrics Measurement Type** dropdown, select **By AI Agent**. ### Step 2: Create the parent metric 1. Enter a descriptive **Name** (for example, Compliance Disclosure). 2. Select the **Language** for the AI Agent's evaluation. 3. The **Question** field is defined later under the sub-metrics section. ### Step 3: Connect to Artemis Enter the details of the Artemis project to evaluate against: | Field | Description | | - | - | | **Project Key** | Identifies the Artemis project. The system uses it to create and periodically refresh the authentication token. | | **Channel ID** | Identifies the channel that attributes the request's source. | By AI Agent V2 uses project-key-based authentication. The system creates the authentication token from the Project Key, refreshes it periodically, and automatically sets the required origin header on the run API; no manual token management is needed. ### Step 4: Test the connection and fetch sub-metrics 1. Click **Test Connection**. 2. The system sends a test call to the selected app and retrieves available sub-metrics for configuration. 3. On success, the panel confirms the connection and shows how many sub-metrics the agent application includes. If the agentic app response doesn't match the required contract, Test Connection fails, blocking configuration. *** ### Step 5: Configure sub-metrics Upon successful connection, the system displays all sub-metrics returned by the agentic app with their reference names. Click **Edit** next to the **Weightage** field to open the sub-metric configuration panel, where you can define the following: | Field | Description | | - | - | | **Display Name** | Label for the sub-metric. | | **Question** | Evaluation question for this sub-metric. | | **Evaluation Criteria** | Assign a weightage to each outcome (**Yes** / **No**). | | **Fatal Error** | If enabled, failing this sub-metric marks the entire interaction as a critical failure. | Sub-metric weightages must sum to 100%; they allocate the score within the parent metric's total. ### Step 6: Configure custom field propagation Optionally, configure custom fields to send to the Agent Platform as metadata for evaluations that depend on conversation data. These fields populate the `requestMeta` object of the [Execute API](/agent-platform/v1/apis/agentic-apps/execute#sample-request) request. 1. Select a conversation-level **Custom Field**. 2. Define **Header Name** as the key in `requestMeta`. 3. Add multiple mappings using **+ Add Custom Field**. For Agent AI and Express sources, `customConversationId` is automatically included in `requestMeta`. When all details are configured, click **Create** to save the metric for AI Agent evaluation. *** ## Set up the response format Ensure that the agent is instructed to respond in the [specified response format](#response-format-for-sub-metrics). *** ## Example use case: UDAP compliance For financial services compliance, a single parent metric can evaluate multiple aspects in one API call: | Sub-metric | Weight | What it evaluates | | - | - | - | | Fee Disclosure | 25% | All applicable fees are clearly explained | | Interest Rate Accuracy | 30% | Interest rate information is accurate | | Benefit Explanation | 20% | Benefits are clearly described | | Exclusion Details | 15% | All exclusions are clearly listed | | Terms Clarity | 10% | Overall clarity of terms | Each sub-metric is evaluated independently with a single API call, providing detailed justifications for each aspect. *** ## Evaluation flow The system sends a single evaluation request that includes: * Conversation data (transcripts and sub-metrics). * `requestMeta` (`conversationId` and configured custom fields). The agent evaluates all sub-metrics and returns structured results. The system maps results and displays adherence with reasoning. *** ## Request metadata in the Execute API The system sends metadata in the `requestMeta` object of the Execute API. The `requestMeta` object includes: * **Contents**: the `conversationId` (always included for Agent AI and Express sources) and custom fields configured for the metric, represented as key-value pairs. * **Custom field mapping rules**: the system derives keys from Header Names and sources values from conversation-level custom fields. It supports configuration of multiple custom fields per metric. **Example**: ```json theme={null} { "requestMeta": { "customConversationId": "AWS_Mono_22April0xxxx", "phone_number": "862684xxxx" } } ``` This metadata is used only during evaluation execution and isn't stored in the results. *** ## Response format for sub-metrics The Agent Platform must return responses in this JSON format for Quality AI to process sub-metric results: ```json expandable=true theme={null} { "botId": "string", "accountId": "string", "conversationId": "string", "agentEvaluation": [ { "PARENTMETRIC_ID_VALUE": { "subMetrics": [ { "subMetricId": "string", "subMetricName": "string", "justification": "string", "messageIds": ["array"], "timestamps": ["array"], "source": "agent | customer", "isQualified": "YES | NO | NA", "failureReason": "string" } ] } } ] } ``` ### Sample response ```json expandable=true theme={null} { "botId": "bot_001", "accountId": "account_001", "conversationId": "conv_001", "agentEvaluation": [ { "eval_001": { "subMetrics": [ { "subMetricId": "sm_001", "subMetricName": "Loan Inquiry Identification", "justification": "Agent correctly identified the customer's loan-related query.", "messageIds": ["msg_001"], "timestamps": ["2025-10-17T10:00:00Z"], "source": "agent", "isQualified": "YES", "failureReason": "" }, { "subMetricId": "sm_002", "subMetricName": "Loan Eligibility Explanation", "justification": "Agent provided loan eligibility information.", "messageIds": ["msg_002", "msg_004"], "timestamps": ["2025-10-17T10:00:10Z", "2025-10-17T10:00:35Z"], "source": "agent", "isQualified": "YES", "failureReason": "" } ] } } ] } ``` The Agent Platform contract strictly defines the response format; Quality AI only consumes and maps the response, not modifying the contract. ## Audit logs (By AI Agent V2) For By AI Agent V2 (Artemis) metrics, the **Audit Screen** records an audit log for each evaluation, allowing you to review and debug agent calls. The log lists one entry per sub-metric evaluation: | Column | Description | | - | - | | **Timestamp** | Date and time of the evaluation call. | | **Request Payload** | The payload is sent to the agent API. | | **Response** | The response returned by the agent. | | **Sub-metric** | The sub-metric evaluated in the call. | | **Status** | Outcome of the call, **Success** or **Failed**. | | **View Payload** | Opens the full request and response, including the trace, for debugging. | ## Manage evaluation metrics ### Edit an evaluation metric 1. Select a **By AI Agent** metric. 2. Click **Edit** to update the required metric details and fields. When you update a By AI Agent V2 connection field (Project Key, Project ID, Deployment ID, or Channel ID), the system records a change log entry, for example: "Project ID was updated from \{old value} to \{new value} for metric \{metric name} of type By AI Agent." ### Delete an evaluation metric Before deleting a metric: * Remove it from all associated evaluation forms (for example, Chat Form – COMMON, New Points Based). * Reassign any linked attributes (for example, Agent AI Metric Attribute-1) to a different metric. The system allows deletion only after you resolve all dependencies and save the changes. # Read Campaign contact data in an Artemis runtime session Source: https://koreai.mintlify.app/agent-platform/integrations/read-campaign-contact-data [Back to Campaign integration guide](/agent-platform/integrations/campaign) This guide explains how an Artemis agent can use the customer and campaign fields supplied by an AI for Service (CCAI) campaign during an outbound voice session. ## Supported data contract Campaign contact fields are available under `customerData.userInfo`. This includes standard contact fields and custom columns imported with the campaign contact list. Campaign control fields are stored under `customerData`; they aren't a part of `userInfo`. | Data | Runtime path | Example | | - | - | - | | Standard contact field | `customerData.userInfo.` | `customerData.userInfo.firstName` | | Custom contact-list column | `customerData.userInfo.` | `customerData.userInfo.policyNumber` | | Campaign identifier | `customerData.campaignId` | `customerData.campaignId` | | Campaign instance identifier | `customerData.campaignInstanceId` | `customerData.campaignInstanceId` | | Dial-list record identifier | `customerData.campaignDialListId` | `customerData.campaignDialListId` | | Dialer/provider field | `customerData.` | `customerData.dialerType` | | Assigned-agent information | `customerData.agent.` | `customerData.agent.agentId` | There are no standard nested `customerData.custom` or `customerData.metadata` objects in the current campaign contract. Don't use those paths for contact-list columns. ## Prerequisites * Campaigns for Artemis is enabled and configured. * The campaign uses the intended Artemis project and deployment. * The campaign contact list contains the fields the agent needs. * Contact-list column names are known exactly, including capitalization. Create or import a campaign contact list with the required standard and custom columns. For example: | number | firstName | lastName | policyNumber | segment | preferredLanguage | | - | - | - | - | - | - | | `+15551234567` | `John` | `Doe` | `P-8801` | `gold` | `en-US` | The campaign service stores these contact values in its `userInfo` object. Use stable, ABL-friendly column names. For example, prefer `policyNumber` over `Policy Number`. Renaming a contact-list column also requires updating every ABL reference to that field. When the campaign starts a supported outbound call, it sends campaign data in the voice-provider tag. Artemis copies the received data into the Runtime session under: ```text theme={null} session.data.values.customerData ``` A representative Runtime value is: ```json expandable=true theme={null} { "campaignId": "campaign-123", "campaignInstanceId": "instance-456", "campaignDialListId": "contact-789", "phoneNumber": "+15551234567", "userInfo": { "number": "+15551234567", "firstName": "John", "lastName": "Doe", "policyNumber": "P-8801", "segment": "gold", "preferredLanguage": "en-US" } } ``` `session.data.values.customerData` describes the internal Runtime storage location. In ABL templates, use the shorter `customerData...` path. Use dotted-path interpolation to access a field: ```text theme={null} {{customerData.userInfo.}} ``` Examples: ```text theme={null} {{customerData.userInfo.firstName}} {{customerData.userInfo.policyNumber}} {{customerData.userInfo.segment}} {{customerData.userInfo.preferredLanguage}} ``` The same interpolation syntax can be used in supported ABL text fields such as goals, personas, system prompts, and transfer messages. **Renewal Campaign Agent Example** ```yaml expandable=true theme={null} AGENT: Renewal_Campaign_Agent GOAL: | Assist the customer with their renewal campaign. Campaign context: - Customer: {{customerData.userInfo.firstName}} - Customer ID: {{customerData.userInfo.customerId}} - Policy number: {{customerData.userInfo.policyNumber}} - Campaign ID: {{customerData.campaignId}} Never invent a value when campaign context is missing. PERSONA: | You are a concise and professional renewal specialist. ``` **Deterministic Flow Example** ```yaml expandable=true theme={null} AGENT: Renewal_Campaign_Agent GOAL: 'Help customers with policy renewals.' FLOW: entry_point: campaign_greeting steps: - campaign_greeting - assist_customer campaign_greeting: REASONING: false RESPOND: 'Hello {{customerData.userInfo.firstName}}. I am calling about policy {{customerData.userInfo.policyNumber}}.' THEN: assist_customer assist_customer: REASONING: true GOAL: | Help the customer with their renewal. Use campaign {{customerData.campaignId}} and segment {{customerData.userInfo.segment}} as context. MAX_TURNS: 5 ``` For campaign control data, omit `userInfo`: ```text theme={null} {{customerData.campaignId}} {{customerData.campaignInstanceId}} {{customerData.campaignDialListId}} {{customerData.dialerType}} ``` The agent must not rely on a contact field unless the selected campaign dialing path sends it. Write prompts so the conversation works even when an optional value is absent. Recommended behavior: * Use a field only after confirming that it is populated for the campaign list. * Don't speak placeholders or internal identifiers to the customer. * Ask the customer for a missing value only when it is necessary for the task. * Don't guess or fabricate a missing campaign value. Example instruction: ```yaml theme={null} PERSONA: | Address the caller by first name when it is available. If the first name is unavailable, use a neutral greeting. Never read an unresolved template or internal campaign identifier aloud. ``` 1. Create a test contact with distinctive, non-production values. 2. Start the campaign against a non-production Artemis deployment. 3. Confirm the call reaches the expected project and agent. 4. Inspect the Runtime session or trace and locate `data.values.customerData`. 5. Confirm the required contact fields appear under `customerData.userInfo`. 6. Confirm the ABL response resolves the intended dotted paths. 7. Repeat with a record that omits optional fields to verify the fallback behavior. Don't add raw customer data to application logs merely to verify interpolation. Use the authorized session or trace view and follow the project's PII-handling policy. ## Dialing-mode limitation The current campaign implementation doesn't propagate contact data identically in every dialing mode: * **Agentless:** explicitly adds the complete `userInfo` object to the outbound provider tag. Standard and custom contact fields can therefore reach `customerData.userInfo`. * **Progressive and power-related paths:** currently add selected values such as phone number, full name, user ID, assigned-agent data, and campaign identifiers, but do not consistently add the complete `userInfo` object. Before depending on arbitrary custom contact fields in a progressive or power campaign, verify that the actual Runtime session contains them. If they are absent, the campaign producer must propagate `userInfo`; an ABL-only change can't recover data that wasn't sent. ## Troubleshoot issues | Symptom | Likely cause | Resolution | | :- | :- | :- | | `customerData.userInfo` is absent | The dialing path didn't send the full contact object | Check the dialing mode and inspect the outbound campaign tag | | A custom field is absent | The source contact lacks the column, or the producer omitted it | Verify the campaign list record and producer payload | | A value doesn't resolve | The ABL path or field capitalization differs from the contact column | Use the exact `customerData.userInfo.` path | | `customerData.custom` or `customerData.metadata` is empty | Those aren't standard campaign containers | Move/read the contact field under `customerData.userInfo` | | Campaign IDs are absent under `userInfo` | Control fields live directly under `customerData` | Use `customerData.campaignId` and related root paths | | The field exists in Campaigns but not in Runtime | Data wasn't included in the provider tag | Fix or enable propagation in the campaign producer | ## Security and privacy Campaign contact data can contain personally identifiable information. Only include fields required for the conversation, avoid exposing internal identifiers, and apply the project's guardrail and PII policies before using customer data in model-visible prompts or spoken responses. ## References ```text theme={null} Contact/custom field: {{customerData.userInfo.}} Campaign field: {{customerData.}} Internal storage: session.data.values.customerData Unsupported contract: customerData.custom / customerData.metadata ``` # Salesforce integration Source: https://koreai.mintlify.app/agent-platform/integrations/salesforce [Back to list of integrations](/agent-platform/integrations#agent-desktop-integrations) For secure, bidirectional communication between the AI application and live representatives, you can integrate Salesforce Service Cloud Agent Desktop with an Artemis project. The integration requires: 1. [Salesforce setup](#salesforce-setup) — create an Embedded Service Deployment (MIAW) or a Connected App (BYOC) in Salesforce. 2. [Artemis project configuration](#artemis-project-configuration) using the organization ID and the deployment or Connected App details from Salesforce. Set it as the default Agent Transfer route. After the configuration is complete, Artemis routes escalated conversations to the configured Salesforce queue, and a live Salesforce agent can pick up the interaction from the Service Console with full conversation context carried over. **Important considerations** * **Auth types**: The integration supports two auth types — **MIAW** (Messaging for In-App and Web, SCRT 2.0 unauthenticated guest messaging, recommended) and **BYOC** (Bring Your Own Connected App, using OAuth with a client credentials grant). Choose the type that matches the setup you complete in Salesforce. * **No OAuth for MIAW**: MIAW is a configuration-only connection — Artemis authenticates using only the SCRT URL, Organization ID, and Deployment Developer Name. If you need an authenticated connection, use BYOC and create a Connected App in Salesforce. ## Prerequisites * Access to Salesforce Setup with permission to create Embedded Service Deployments (MIAW) or Connected Apps (BYOC), and to configure Omni-Channel routing. * Artemis project access with permission to open **Project** > **Integration**, and **Project Settings** > **Agent Transfer**. *** ## Salesforce setup For more details on the setup steps, refer to the Salesforce documentation for Messaging for In-App and Web ↗. 1. Go to **Setup** > **Embedded Service Deployments**. 2. Create a new **Messaging for In-App and Web** deployment (or use an existing one). 3. Note the deployment's **Developer Name** (for example, `My_Messaging_Deployment`). 4. Open the deployment's **Code Snippet** and copy the **SCRT URL** (for example, `https://your-org.develop.my.salesforce-scrt.com`). 1. Go to **Setup** > **Company Information**. 2. Copy the **Salesforce.com Organization ID** — it starts with `00D`. 1. Go to **Setup** > **Routing Configuration** (Omni-Channel) and configure the queues and skills used to assign incoming interactions to agents. 2. Test the routing flow in the Salesforce Service Console before connecting Artemis. For more details on the setup steps, refer to the Salesforce documentation for Connected App Client Credentials Setup ↗. 1. Go to **Setup** > **App Manager** > **New Connected App**. 2. Enable OAuth settings and enable the **Client Credentials Flow**. 3. Assign a run-as user for the client credentials flow. 4. Save, then open **Manage Consumer Details** to copy the **Consumer Key** (Client ID) and **Consumer Secret** (Client Secret). Copy your org's SCRT instance URL, for example, `https://your-org.my.salesforce-scrt.com`. *** ## Artemis project configuration 1. Go to **Project** > **Integration** > **Agent Desktop** > **Salesforce**. 2. Select **Connect**. The **Connect Salesforce** configuration dialog opens. 3. Configure the following fields: | Artemis connection field | Applies to | Source / Notes | | - | - | - | | Connection Name | Both | Enter a unique connection name, such as `My Salesforce`. | | Auth Type | Both | `miaw` — Messaging for In-App and Web (no OAuth, recommended). `byoc_oauth` — BYOC Interaction Service with Connected App OAuth. | | SCRT URL (optional) | MIAW only | Your SCRT 2.0 endpoint URL from the Embedded Service Code Snippet. See [Salesforce setup](#salesforce-setup). | | Organization ID | Both | Salesforce Setup > Company Information (starts with `00D`). | | Deployment Developer Name (optional) | MIAW only | The Embedded Service Deployment developer name from Setup > Embedded Service Deployments. | | Instance URL (optional) | BYOC only | Your Salesforce SCRT instance URL. See [Salesforce setup](#salesforce-setup). | | Client ID (optional) | BYOC only | Consumer Key from your Connected App settings. | | Client Secret (optional) | BYOC only | Consumer Secret from your Connected App settings. | | Webhook Callback URL (optional) | Both | ABL Runtime webhook for inbound agent events: `https://your-runtime/api/v1/agent-transfer/webhooks/salesforce?tid=`. | 4. Click **Create Connection**. 5. Use the Salesforce integration for Agent Transfer routing:
Go to **Project Settings** > **Agent Transfer** > **Default Routing** > **Connection** and select the integration. When an agent escalates a conversation, Artemis routes it to the configured Salesforce Agent Desktop system. ### Validation checklist | Validation item | Expected result | | :- | :- | | Complete Salesforce setup | Embedded Service Deployment ready (MIAW) or Connected App created with Client Credentials Flow enabled (BYOC). | | Note Organization ID | Organization ID copied from Setup > Company Information. | | Create Artemis Salesforce connection | Connection saved with a unique name, Auth Type, and the fields required for that auth type. | | Set default Agent Transfer route | Salesforce connection is selected in Project Settings > Agent Transfer > Default Routing. | | Initiate test transfer | Test conversation escalates without connection errors. See [verify the connection](#verify-the-connection). | ## Verify the connection 1. Trigger an agent transfer in the test conversation in your Artemis project. 2. Open the Salesforce **Service Console** (Omni-Channel) and accept the incoming interaction from the configured queue. 3. Confirm that messages sent from the test conversation appear on the agent side, and that agent replies appear back in the Artemis conversation. *** ## Troubleshoot integration issues | Symptom | Likely cause | Recommended action | | - | - | - | | Connection fails with a "no credentials" error | Required MIAW or BYOC fields are missing from the connection | Re-open the connection and verify Organization ID plus the fields required for your Auth Type — SCRT URL and Deployment Developer Name for MIAW; Instance URL, Client ID, and Client Secret for BYOC. | | MIAW authentication fails with a 401 error | Invalid Organization ID or Deployment Developer Name | Re-check both values against Setup > Company Information and Setup > Embedded Service Deployments. | | MIAW authentication fails with a 403 error | The Embedded Service deployment restricts allowed domains | In the deployment's settings in Salesforce, allow all domains or add the Artemis/runtime domain to the allow list. | | Real-time connection fails with a 404 error | Invalid or mistyped SCRT URL | Re-copy the SCRT URL from the deployment's Code Snippet in Setup > Embedded Service Deployments. | | BYOC connection is unauthorized | Incorrect Client ID / Client Secret, or the Client Credentials Flow isn't enabled on the Connected App | Verify the Consumer Key and Consumer Secret in Manage Consumer Details, and confirm the Client Credentials Flow is enabled with a run-as user assigned. | | Agent never receives the interaction | Routing configuration doesn't route the deployment's channel to an active queue | Verify the Omni-Channel routing configuration and queue assignment in Salesforce Setup. | **Related articles:** * [Transfer sessions](/agent-platform/transfer-sessions) * [Escalate to a human agent](/agent-platform/escalate) # Knowledge bases and knowledge tools Source: https://koreai.mintlify.app/agent-platform/knowledge A Knowledge base is a managed collection of documents and data sources that an agent can search during conversations. Knowledge bases in the Agent Platform serve as the primary retrieval layer for enterprise content, enabling agents to query and access information across heterogeneous data sources. Content can be ingested via file uploads, web crawling, and external connectors, and is indexed to support vector, keyword, and structured retrieval. At query time, the platform applies enterprise search and RAG pipelines to automatically contextualize agent requests—resolving intent, enriching queries, and retrieving relevant chunks. This retrieved context is then made available to agents and tools, allowing them to perform grounded reasoning, execute workflows, and generate accurate, context-aware responses. ## How knowledge bases work A knowledge base processes content through an ingestion pipeline (index-time) and serves it via a retrieval pipeline (query-time). ### Ingestion process The ingestion pipeline transforms raw content into retrieval-ready chunks with embeddings and metadata. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A[Ingest] --> B[Extract] B --> C[Chunk] C --> D[Enrich] D --> E[Embed] E --> F[Store] ``` 1. **Ingestion**: Registers and tracks content for processing. For uploaded files, the platform checks for duplicates using content hashing, files that have already been indexed with unchanged content are skipped. For connected data sources, the platform discovers and tracks all documents within the source. 2. **Extraction**: The extraction stage pulls content from your source files. Depending on the content format, different extraction strategies are used. | Format | Extraction approach | | - | - | | Plain text, Markdown | Used as-is | | HTML | Tags stripped, structure preserved where meaningful | | PDF | Text extracted from pages (layout-aware extraction for complex PDFs) | | DOCX / Office formats | Document content extracted from the file structure | | JSON | Values extracted and concatenated with structural context | The extraction stage also captures document metadata (titles, headings, page numbers) that's used later for enrichment and search result context. 3. **Chunking** - The chunking stage splits text into smaller segments that can be individually embedded and retrieved. The chunking strategy you choose directly affects search quality — it determines how coherent and complete each retrievable unit of content is. The platform supports three chunking strategies: | Strategy | How it splits | Best for | | - | - | - | | Fixed-size | Token windows of a configurable size with overlap between chunks | Predictable, uniform content where consistency matters | | Semantic | Natural boundaries such as paragraphs, sections, and topic shifts | Narrative or long-form content where coherence is important | | Sliding window | Overlapping windows that slide across the full text | Content where context continuity across boundaries is critical | Chunk overlap ensures that context at chunk boundaries is not lost. A concept that spans two paragraphs will appear in both chunks when overlap is configured. Always configure overlap when your content spans section boundaries. 4. **Enrich**: The enrichment stage adds metadata to each chunk, improving search quality and enabling filtering. The following enrichments are applied: | Enrichment | What it adds | | - | - | | Entity detection | Identifies emails, URLs, dates, and monetary values within the chunk | | Summary generation | Creates a brief summary of the chunk's content | | Language detection | Identifies the language of the content | | Knowledge graph extraction | Extracts entities and relationships to build a knowledge graph. This feature is available on request. | Enrichment metadata is stored alongside each chunk and can be used to filter and rank search results at query time. 5. **Embedding Generation**: The embedding stage converts each text chunk into a vector representation that captures its semantic meaning. These vectors enable semantic search — finding content that's conceptually related to a query even when the exact words differ. 6. **Store**: The final stage writes the processed chunks to the vector database for fast retrieval. Each stored chunk includes: * The original text content * The vector embedding * Enrichment metadata * Source information — document ID, source ID, and page number * Permission metadata for access control After a chunk is stored, it's immediately available for search. ### Retrieval process When an agent needs information from a knowledge base, the platform executes a search query and returns the most relevant chunks as context for the agent to compose a response. The search pipeline runs in the following sequence: 1. The agent sends the query to the runtime. 2. Runtime embeds the query text into a vector representation. 3. Hybrid search runs - semantic and keyword search execute simultaneously. 4. Results from both strategies are merged. 5. Merged results are re-ranked by relevance. 6. Permission filters are applied to the re-ranked results. 7. Filtered, ranked chunks are returned to the agent as context. 8. The agent uses the context to compose a response. Retrieval Process #### Key concepts 1. **Hybrid Search**: The platform uses hybrid search by default, combining two complementary strategies to maximize retrieval quality. * **Semantic Search** : Semantic search finds chunks whose vector embeddings are most similar to the query embedding. This helps retrieve conceptually relevant content even when the user's phrasing does not directly match the source text. Example: A query for "How do I get my money back?" can match a chunk about "Refund policy and procedures" even though none of the query words appear in the chunk heading. Use semantic search when users are likely to phrase queries in natural language that differs from the terminology used in the source content. * **Keyword Search**: Keyword search finds chunks containing the exact terms in the query. This helps retrieve content with specific names, product codes, technical terms, or identifiers that semantic search might miss due to low vector similarity. Use keyword search when the content contains precise terms — such as model numbers, error codes, or proper nouns — that must match exactly. Hybrid search merges and re-ranks results from semantic and keyword search to produce a single ranked list of the most relevant chunks. 2. **Re-Ranking** After the initial search, results are re-ranked to improve relevance ordering. Re-ranking evaluates the merged result set and adjusts the order based on relevance signals beyond initial similarity scores. 3. **Permission Aware Search** Search results are filtered based on the requesting user's permissions before they're returned to the agent. If your knowledge base is connected to a data source with access controls, such as SharePoint or other enterprise connectors, only documents the user has permission to see are included in the results. Permission filtering happens after re-ranking. The agent only receives chunks the requesting user is authorized to access, regardless of their relevance score. This means result counts may vary between users querying the same knowledge base with the same query. ## Adding knowledge base Knowledge bases are created and managed at the project level and are available to all agents within that project. Every knowledge base in the project is automatically exposed as a tool that any agent can attach to and use. To create a knowledge base: * Navigate to **Knowledge Bases** in the left sidebar. * Click **+ New Knowledge Base**. * Enter a name and description. * Name: used to identify the knowledge base across the project. * Description: helps agents identify when to use this knowledge base as a tool. Write this clearly and specifically. * Click **Create**. The knowledge base is created with default configurations applied. You can review and update these configurations at any time from the knowledge base dashboard. ### Adding data sources A knowledge base can ingest content from multiple source types simultaneously. Each source is managed independently, and you can add, sync, and remove sources without affecting others. When you add a data source to the knowledge base, the platform immediately begins processing it in the background. You don't have to perform any other steps to trigger ingestion or indexing. To add a data source, follow these steps: * Go to the **Sources** and select **+ Add Data Source**. * Select the source connector and select **Connect**. * Provide configuration details for the source. #### File uploads Upload files directly from your local system. The intelligence pipeline immediately processes the uploaded files. If a source file changes, re-upload it. * Supported file types: PDF, DOCX, TXT, CSV, MD, XLSX, JSON, HTML. * Maximum file size: 100 MB per file * Maximum batch size: 40 files per upload. If the source file changes, you need to re-upload it. **Metadata Fields** While uploading a file, you can tag the upload with file metadata: * **Essential Fields** - the most commonly used fields like Author, Category, Tags, Department. * **More Fields** - a list of additional fields covering document metadata such as Title, Content Summary, Language. * **Advanced (custom JSON)** — add custom fields not covered by the standard list, as JSON, for example, `{"custom_string_1": "value", "custom_number_1": 42}`. Fields filled in during upload become searchable. Only filled fields create vocabulary entries for natural language queries — empty fields are not indexed. Click **Upload** to start ingestion. The uploaded content is automatically indexed after the ingestion completes. #### Web crawl Crawl and index web pages from any public or authenticated website. Navigate to **Sources** > **Add Source** > **Web Crawler** to manage web sources. * Provide the domain URL. * Enter auth details, if required. * Select Discovery method. * Crawl Full Sitemap: Discovers the sitemap and retrieves the list of URLs to be crawled. * Guided Discovery: Guide the system to identify pages based on a given URL pattern. * Direct URLs: Directly provide URLs to crawl. * From the list of the discovered URLs, select the URls to crawl. * Configure the web crawl settings. * Start Crawl. **Web Crawl Settings** **How much should we crawl?** : Choose how many pages to crawl in a single run. **Crawl Strategy** * **Rendering** controls how pages are processed before content is extracted. **Standard** option fetches the raw HTTP response only. It's the fastest option, but doesn't capture content that's rendered client-side (for example, React/Vue apps, JS-loaded sections). Best for static HTML sites. **Full rendering** loads the page in a headless browser and executes JavaScript before extracting content, capturing dynamic content like expandable sections, tabs, and client-rendered text. Slower and more resource-intensive, but necessary for JavaScript-heavy sites. **Auto** allows the system to automatically choose per page whether a lightweight HTTP fetch or full browser rendering is needed, based on page characteristics. * **Learned patterns** determines whether the crawler reuses extraction patterns from previous crawl runs. Set to **Keep** by default, which reuses known page layouts and skips the AI layout detection step — making repeat crawls faster. Select **Reset** after a site redesign or template change so the crawler re-learns the new structure from scratch. * **Crawl Speed** controls the delay between page requests. Default value is 200 ms. Faster speeds can trigger rate limits on external sites. **Limits** * **Max pages** caps the number of pages fetched in a batch. The default is 1,000 per batch. * **Max depth** controls how many link-hops from the seed URL the crawler will follow. Default: 5, Range: 1-20. * **robots.txt** honors crawler directives in robots.txt. **Content Processing** * **Extraction quality** controls how aggressively the crawler cleans and filters page content during extraction. Default value is **Balanced**. | Option | Description | | - | - | | Thorough | Keeps more content from each page. | | Balanced | Default quality and completeness for most sites. | | Minimal | Aggressively strips noise. | | Custom | Fine-tune quality gate and content preservation thresholds manually. | **Custom thresholds** | Field | Description | | - | - | | Quality gate | Minimum quality score required to accept extracted content, on a 0–1 scale (default 0.5). Lower values are more permissive — more content is accepted even if extraction confidence is lower. | | Content preservation | How much of the original content to keep when cleaning, on a 0–1 scale (default 0.3). Lower values clean more aggressively, stripping more content. | * **Duplicate content** - Controls how pages with identical content at different URLs are handled. Options: **Deduplicate** (default) or **Keep all**. - **Cookie consent** - Rejects or closes cookie popups before extraction; never accepts tracking. Options: **Auto-dismiss** (default) or **Ignore**. **Scheduled Crawl** Use this option to automatically recrawl the configured website at a specified date and time. Scheduled crawls help keep indexed content up to date without requiring manual execution. To configure a scheduled crawl: * Enable the Scheduled Crawl toggle. * Specify the following settings: | Field | Description | | | - | - | - | | **Date** | Select the date on which the scheduled crawl should begin. | | | **Time** | Specify the time when the crawl should start. | | | **Timezone** | Select the timezone in which the scheduled crawl should run. | | | **Repeat** | Specify how often the crawl should run. Select **Run Once** to execute the crawl only once, or choose a recurring schedule to automatically recrawl the website at the configured interval (if supported). | run. Select Run Once to execute the crawl only once, or choose a recurring schedule to automatically recrawl the website at the configured interval (if supported). | * Click **Save** to save the schedule. This configuration is available only after the initial sync is successful. Go to the source, click **Reconfigure Crawl Strategy** and then click on **Configure** to view the scheduler. **Extraction Preview** shows samples of what will be extracted from each section using the current configuration before the full crawl runs. #### Connectors Pre-built integrations for third-party applications. Navigate to **Sources** > **Add Source** to configure integrations. **Connector Setup Workflow** For each connector, 1. Authentication — Provide OAuth credentials, API keys, or tokens to enable connection with the third party application. 2. Ingestion — Select content types and apply filters. 3. Field Mapping — Align source fields with the inbuilt schema. Map custom fields, if required. 4. Permissions — Configure security settings to enable authorized access to information from the third-party applications. **Enabling/Disabling permissions** When permission-aware ACL is disabled, all content ingested through a connector is indexed as accessible to all users as no document-level permissions are stored. Enabling permission-aware ACL does not automatically update permissions for previously indexed content. The connector continues to use the existing permission information until a permission synchronization is performed. A permission synchronization is triggered only in the following scenarios: * Manually perform a full content synchronization. Incremental or delta synchronizations do not trigger a permission sync. * A scheduled permission synchronization run. During a permission synchronization, the connector does not re-ingest content. It retrieves the latest permissions from the source system and applies them to the documents that are already indexed. Until a permission synchronization completes after enabling permission aware ACL, content indexed while ACL was disabled remains accessible to all users. **Available Connectors** | Connector | Description | | - | - | | Aha! | Index ideas, features, and roadmaps from Aha! | | Airtable | Index spreadsheets and databases from Airtable bases | | Amazon S3 Connector | Index files from S3 buckets | | Asana | Index tasks and projects from Asana | | [Axero](/agent-platform/connectors/axero) | Index Axero spaces, articles, blogs, wikis, files, announcements, and comments | | Azure Blob Storage | Index blobs from Azure Blob Storage containers | | Bitbucket | Index repository metadata, pull requests, comments, and commit timelines | | Bigtincan | Index training courses and content from Bigtincan | | [Box Connector](/agent-platform/connectors/box) | Index files and folders from Box cloud storage | | ClickUp | Index tasks and spaces from ClickUp | | Coda | Index docs and tables from Coda | | [Confluence Cloud Connector](/agent-platform/connectors/confluence) | Index Cloud spaces, pages, blogs, attachments, comments, and tables | | Confluence Server | Index Server spaces, pages, blogs, attachments, and comments | | Datadog | Index metrics, dashboards, and monitors from Datadog | | Discord | Index messages from Discord servers and channels | | DotCMS | Index content and pages from DotCMS | | Dropbox | Index files and folders from Dropbox | | Dynamics 365 | Index entities and records from Microsoft Dynamics 365 | | Egnyte | Index files and folders from Egnyte cloud storage | | Figma | Index design files and components from Figma | | File Upload | | | Freshdesk | Index tickets and knowledge base from FreshDesk | | Freshservice | Index solution articles and tickets from Freshservice | | Front | Index knowledge base articles from Front | | GitBook | Index documentation from GitBook spaces | | GitHub | Index repositories, issues, and wikis from GitHub | | GitLab | Index repositories, issues, and wikis from GitLab | | Gmail | Index emails and attachments from Gmail | | Google Chat | Index messages from Google Chat spaces | | Google Cloud Storage | Index objects from Google Cloud Storage buckets | | Google Docs | Index documents from Google Docs | | [Google Drive Connector](/agent-platform/connectors/googledrive) | Index files and folders from Google Drive | | Guru | Index knowledge cards and collections from Guru | | Help Scout | Index docs, articles, and conversations from HelpScout | | Hive | Index projects, tasks, and actions from Hive | | HubSpot | Index contacts, deals, and knowledge base from HubSpot | | Intercom | Index conversations and articles from Intercom | | Invision Community | Index forums, articles, and discussions from Invision Community | | JFrog Artifactory | Index artifacts and repositories from JFrog Artifactory | | Jenkins | Index build jobs, pipelines, and logs from Jenkins | | [Jira Connector](/agent-platform/connectors/jira) | Index Jira Cloud issues, comments, worklogs, and attachments | | Jira Data Center | Index Jira Data Center or Server issues, comments, worklogs, and attachments | | [Custom Connector](/agent-platform/connectors/custom-connector) | Index structured data from JSON endpoints and files | | Linear | Index issues and projects from Linear | | LumApps | Index content and communities from LumApps intranet | | Microsoft Teams | Index messages and files from Microsoft Teams channels | | Microsoft Word Online | Index documents from Microsoft Word Online | | Miro | Index boards and content from Miro whiteboards | | Monday.com | Index boards and items from Monday.com | | Notion | Index pages and databases from Notion workspaces | | OneDrive | Index files and folders from Microsoft OneDrive | | Opsgenie | Index alerts, incidents, and on-call schedules from Opsgenie | | Oracle Knowledge | Index records and knowledge articles from Oracle applications | | PagerDuty | Index incidents and services from PagerDuty | | Pipedrive | Index deals, contacts, and activities from Pipedrive | | Quip | Index documents and spreadsheets from Quip | | ReadMe | Index API documentation from ReadMe | | Re:amaze | Index conversations and knowledge base from Re:amaze | | Rich Media | Upload and index images, audio, and video files | | RSS Feed | Index articles and content from RSS feeds | | SAP | Index records and documents from SAP systems | | [Salesforce Connector](/agent-platform/connectors/salesforce) | Index Salesforce Knowledge, cases, CRM records, attachments, comments, and custom objects with native ACL sync | | [ServiceNow Connector](/agent-platform/connectors/servicenow) | Index knowledge articles, tasks, attachments, comments, and ACL-aware records | | [SharePoint Connector](/agent-platform/connectors/sharepoint) | Index sites, libraries, and documents from Microsoft SharePoint | | Shortcut | Index stories, epics, and iterations from Shortcut | | Shopify | Index products, orders, and content from Shopify stores | | Slab | Index posts and topics from Slab knowledge base | | Slack | Index messages and files from Slack channels | | Slite | Index notes and documents from Slite | | Sitemap | Discover and index pages from XML sitemaps | | Stack Overflow for Teams | Index questions and answers from Stack Overflow for Teams | | Swagger / OpenAPI | Index API specifications from Swagger and OpenAPI definitions | | Teamwork | Index projects, tasks, and milestones from Teamwork | | TestRail | Index test cases, runs, and plans from TestRail | | Trello | Index boards and cards from Trello | | Vimeo | Index video transcripts and metadata from Vimeo | | Wolken Service Desk | Index tickets and knowledge articles from Wolken Service Desk | | Workday | Index employee records and documents from Workday | | WordPress | Index posts, pages, and content from WordPress sites | | Wrike | Index projects, tasks, and folders from Wrike | | xMatters | Index incidents, on-call schedules, and workflows from xMatters | | YouTrack | Index issues and projects from JetBrains YouTrack | | YouTube | Index video transcripts and metadata from YouTube | | [Zendesk Connector](/agent-platform/connectors/zendesk) | Index tickets, Help Center articles, and comments from Zendesk | | Zeplin | Index design specs and styleguides from Zeplin | | Zoho CRM | Index leads, contacts, and records from Zoho CRM | | Zoom | Index meeting transcripts and recordings from Zoom | | Zulip | Index messages and topics from Zulip chat streams | ### View ingested content To view the ingested content, follow these steps: 1. Go to the **Documents** page. 2. The ingested documents are listed. Click on any document to view the extracted content and the related metadata. Following details of the document are shown. * Metadata - Provides general information about the document like type, language, size, page count. * Extraction Quality - Provides metrics that help evaluate the quality of content extraction. | Metric | Description | | - | - | | Overall | Overall extraction quality score. | | Noise Reduction | Effectiveness of removing irrelevant content. | | Content Preservation | Degree to which original content is retained. | | Structure Preservation | Accuracy of preserving headings, sections, and document structure. | | Metadata Extraction | Quality of metadata extraction from the source document. | * Extraction Report - Shows the extraction configuration used for the document. Use Re-extract to rerun document extraction if extraction settings have changed, metadata needs to be regenerated or extraction quality needs improvement. * Content Analysis - Displays attributes identified during content processing like detected language. * Processing Pipeline - Series of steps through which the document is processed. Also indicates the current status of each step. A document is fully available for retrieval only after all processing stages are successfully completed. * Source Info - Details of the source of the document. * Raw Metadata - Document metadata in JSON format. 3. You can reprocess a given document to ingest, extract and index it again. By default, it uses the **Extraction quality** configuration for the source. Use the **Extraction Preset** option to change the config for a given document. 4. Use the Delete button to delete a single page from the source. Note that if the source is synchronized again, the deleted document will be ingested again unless added to the exclude list explicitly. ### View extracted chunks 1. Navigate to **Chunks** page. 2. Click on a document to see the chunks extracted from the document. 3. See token distribution across chunks from the document. ### Configure content processing pipeline By default, the platform creates a default pipeline for processing of the ingested content automatically to make it searchable. The default pipeline consists of the following stages. You can reconfigure any stage as required. Alternatively, you can add a new flow to the pipeline for custom configurations. **Extraction**: Extracts raw text and structure from ingested files and connected sources. The extraction strategy varies by file type, for example, tables in spreadsheets are extracted differently from paragraphs in a PDF. Configure extraction settings to control how the platform interprets your content structure. **Chunking**: Splits extracted content into smaller segments for indexing. Chunk size and overlap affect search quality; smaller chunks return more precise results while larger chunks retain more surrounding context. Configure the chunking strategy based on the nature of your content and how users are likely to query it. **Content Intelligence**: Enriches chunks with additional metadata, such as topic tags, summaries, or classifications, to improve search relevance. Configure what enrichment is applied to your content at this stage. **Visual Analysis**: Processes visual content in ingested files, such as images, diagrams, and charts, and extracts text or descriptions for inclusion in the index. Enable this stage when your content contains visual elements that carry meaningful information. **Embedding Fields**: Defines which fields from the extracted and enriched content are included in the embedding. Controlling embedding fields lets you tune what the vector search considers when matching a query to a chunk. **Embedding**: Converts the selected fields into vector representations using the configured embedding model. The choice of embedding model affects the quality of semantic search results. Configure the model here based on your language and domain requirements. **Opensearch**: At this stage, the processed and embedded chunks are written to the index using their embeddings. You can run test queries to see what is currently indexed and verify search quality as content is added. The pipeline runs in sequence. A change to an upstream stage, such as chunking, affects all downstream stages. Re-indexing is required when pipeline configuration changes are saved. #### Adding a Pipeline Stage Click **+** at any connection point in the pipeline canvas to insert a new stage. There are two categories of stages that can be added to a pipeline. Add a stage and select it for configuration. **Pipeline Stages** — content-processing stages that run as part of the core ingestion flow. | Stage | Description | | - | - | | Chunking | Splits content into smaller chunks. | | Content Intelligence | Adds summarization, question synthesis, and document-level intelligence. | | Visual Analysis | Analyzes images, tables, charts, and screenshots using a multimodal LLM. | | Enrichment | Enriches chunks with LLM-generated metadata. | **Utility Stages** — general-purpose stages you can insert anywhere in the pipeline for custom processing logic. | Stage | Description | | - | - | | Field Mapping | Maps and transforms fields using conditional rules. | | API Call | Calls an external HTTP API during processing. | | LLM | Enriches data with a large language model call. | | Custom Script | Runs sandboxed JavaScript to transform content and metadata. | ##### Field Mapping Configure one or more rules to map or transform fields during processing. Each rule is independently set to one of two modes, so a single stage can mix Visual and CEL rules as needed. * **Visual mode** — Build a rule using an IF/THEN structure: IF a selected field meets a condition (equals, and so on) against a value, THEN set a target field to a new value. The source (`Select field...`) and target (`Select target field...`) dropdowns list the indexed fields available from your source documents. * **CEL mode** — Write a CEL expression that evaluates directly to the target value, for example, `document.source_type == 'pdf' ? 'document' : 'other'`. Click **Add Rule** to add additional rules — each can independently use Visual or CEL mode; rules run in order. ##### API Call Calls an external HTTP endpoint during processing. | Field | Description | | - | - | | Provider | The API provider type. Select **Custom API** to call any external HTTP endpoint. | | Endpoint URL | The URL the platform sends requests to, for example, `https://api.example.com/extract`. | | HTTP Method | The HTTP method used for the request, for example, `POST`. | | Pipeline Mode | Controls how the API response is applied to the pipeline. **Replacement** replaces the stage output with the API response. **Transformer** modifies data in place. **Source** provides input. | | Output Type | The expected format of the API response. It can be `Text`, `Pages`, `Chunks`, `Document` and `Enriched Chunks`. | | Entry Point | Determines when the API is called during document processing. | * **Auth & Headers** — Configure authentication (for example, API key, bearer token, basic auth) and custom request headers sent with each call. * **Behavior** — Configure request behavior such as timeout duration, retry attempts, and error handling. * **Test** — Send a test request using sample data to validate the results of the . ##### LLM Enriches content with a large language model call. | Field | Description | | - | - | | Model | The model used to run the prompt, for example, Auto-select fast model. | | Prompt Template | The prompt sent to the model. Click any available field chip (Title, Content Summary, Source Type, Source URL, Created Date, Modified Date, Author, Language, Status, Category, Description, Tags, Priority, Assignee, Department, Project) to insert its value into the template. | | Output | Where the model's response is written, for example, **Save to metadata**. | | Output Field | The target field the output is saved to. | | Advanced | Additional configuration options (collapsed by default). | ##### Custom Script Runs sandboxed JavaScript to transform document content and metadata. Custom Script runs for document files (PDF, DOCX, text, HTML, web pages). It does not run for JSON, CSV, or Excel uploads. Use a structured-ingestion path for JSON, CSV, or Excel content formats. | Field | Description | | - | - | | Run at Phase | When the script runs during processing, for example, **After Extraction (whole document text)** — runs once per document, after extraction, with `content` set to the full extracted text (markdown for PDF/DOCX). Best place to clean or normalize whole-document text. | | Transform Script | A `transform(content, metadata)` function to make changes to the input. Use `return { content?, metadata? }` to update content or metadata or both. The script runs once per chunk/document; you can add your own helper functions and call them from `transform()`. | Content changes made by the script are embedded and searchable. Metadata changes are stored on the chunk/document but are not searchable (canonical) fields yet. Provide sample content and **Run on sample** to test the script behavior. Each Utility Stage also supports an optional **Execution Condition** — a CEL expression that, when set, limits the stage to run only if the expression evaluates to `true`. Click **Apply** to save a stage's configuration, or **Reset** to discard changes. ### Field Intelligence Manage indexed fields and how agents can use them in search. **Summary** — Dashboard of field health: active field count, indexed records, connected sources, open warnings, and quick links to review fields/capabilities. **Fields** — Full inventory of mapped fields, each showing its data type, storage field name, and which search capabilities (Answer, Semantic, Filter, Sort, Aggregate) are enabled on it. **Capabilities** — Review and assign fields to each search capability (Answer, Semantic, Filter, Sort, Aggregate). **Agent View** — Read-only view of what's exposed to agents: coverage %, a confidence breakdown per capability, and the field labels/keys agents can discover. ### Configure vocabulary Vocabulary lets you define aliases for terms that appear in your knowledge base content. When a user queries using an alias, the platform expands the query at runtime to include both the alias and the canonical term — improving search coverage without requiring the user to know the exact terminology used in the source documents. Configure vocabulary when: * Your source documents use formal or technical terminology that users are unlikely to search for exactly. * Your content uses abbreviations or acronyms that users may spell out — or vice versa. * Different teams or user groups use different terms for the same concept. * Search queries consistently miss relevant content despite the content existing in the knowledge base. **Add Vocabulary** To add a vocabulary entry, click Add Entry on the Vocabulary page and provide the following details. | Field | Description | | - | - | | Term | The canonical term as it appears in the document. This is the authoritative form the platform uses for matching. Example: priority, status, assignee | | Aliases | Alternative terms a user might search for, entered as comma-separated values. Maximum 10 aliases per term. Example: pri, urgency, importance | | Description | A brief explanation of what the term represents. Used for internal reference. | | Field Reference | The schema field this term is associated with. Select from the fields detected during schema extraction. Scopes the vocabulary mapping to a specific field in the index. | | Capabilities | Define how the term and its aliases are used in the results. | | Display With | Specify additional fields to show alongside this term in the results. | ### Configure AI This section controls the models and LLM-powered features used for document ingestion, enrichment, and search in the given project. **Tenant Models**: Manage the LLM providers and models available for this workspace. Click Configure Models to add or update model configurations. See Models for details. #### Embedding models Embedding models convert documents into vectors for semantic search. The active model applies to all document ingestion and retrieval in this workspace. BGE-M3 is the default embedding model. Changing the embedding model requires re-ingestion. All documents are automatically re-processed and re-embedded. This operation may take time and incur costs depending on your provider. #### Enrichment features LLM-powered features that run during ingestion to generate summaries, questions, field mappings, and vocabulary. Each feature can be enabled or disabled independently and uses a configurable model. Click Change on any feature to switch its model. * **Progressive Summarization**: Generates 2–3 sentence summaries per chunk using context from previous chunks. Improves retrieval quality for long documents. * **Question Synthesis**: Generates 3–5 answerable questions per chunk. Improves retrieval accuracy by matching user queries to pre-generated questions. * **Field Mapping Suggestions**: Suggests field mappings from the connector schema to the canonical schema during source configuration. * **Domain Vocabulary Generation**: Enriches domain vocabulary with aliases, descriptions, and capabilities to improve search relevance for domain-specific terms. * **Knowledge Graph Extraction**: Extracts entities and relationships to build a knowledge graph. This feature can be enabled upon request. #### Advanced features Higher-cost features for visual and multimodal content analysis. Each feature uses a balanced-tier model and can be enabled or disabled independently. * **Vision Analysis**: Analyzes page screenshots and images to understand visual content. Useful for documents that contain charts, diagrams, or image-heavy pages. * **Multimodal Deep Analysis**: Performs deep analysis of images, tables, and charts with data extraction. Use this for documents where structured visual content needs to be indexed. #### Query pipeline LLM Controls the LLM used at query time to understand and process queries. **Query Intelligence**: When enabled, it automatically assigns the best available model for vocabulary resolution, query classification, filters, aggregations, structured queries, and enhanced search accuracy. Query Intelligence applies to direct search queries only. It is not used when searching through Agent and Tools. ### Knowledge Graph Knowledge Graph classifies documents against a configurable taxonomy, extracts entities from your content, and links them into a searchable graph, in addition to the standard chunk-based search index. Standard knowledge base search retrieves chunks based on text similarity, treating each chunk largely as an independent unit. Knowledge Graph adds structure on top of this. It organizes content into a taxonomy (such as , domain → category → product → attribute) and connects related entities across documents, so the platform understands not just *what text matches a query*, but *how concepts relate to each other*. This is useful when: * Your content naturally maps to a structured taxonomy — such as a product catalog, service categories, or a domain with defined categories and attributes. * You want to answer relationship-aware queries, such as "what products fall under Pharmacy" or "what attributes does this product have" — questions that depend on structure, not just text similarity. * The same entity (a product, term, or concept) appears across multiple documents, and you want it recognized and linked as a single connected entity rather than treated as separate, unrelated mentions. * You want to filter or facet search results by category, product, or attribute, rather than relying purely on keyword or semantic match. ### Setting Up the Knowledge Graph Setting up Knowledge Graph involves: 1. Picking an industry domain (or letting AI generate one). 2. Building a taxonomy of products and attributes for that domain. 3. Automatically classifying every document against the taxonomy. 4. Extracting entities from content and linking them into the graph. * Navigate to **Knowledge Graph** in the left sidebar of the knowledge base. * Click **Select your domain**. * Configure **Taxonomy**: Configuring the taxonomy defines the schema that the Knowledge Graph will classify the content against. | Field | Description | | - | - | | Domain | The industry domain the taxonomy is based on. | | Generate Profile with AI | Automatically generates a taxonomy profile using AI, based on the website provided. | | Organization Name | Optional. Your organization's name, used to help AI generate a more relevant taxonomy profile. | | Which products do you offer? | Optional. Select the products/services relevant to your organization. Available options are grouped by category (for example, Clinical Care, Health Insurance, Pharmacy, Diagnostics for a Healthcare domain) and vary by the selected domain. Click **Show Advanced** for additional options. | * Click **Set Up Taxonomy** to save the configuration and create the taxonomy. Once a taxonomy is configured, click **Run Enrichment** to classify documents and extract entities against the taxonomy, building the knowledge graph. Re-run enrichment after taxonomy changes or when new content has been added to the knowledge base. * **Graph View** - Visualizes the knowledge graph as a network of connected nodes. * **Statistics** — Summary metrics about the documents as per the taxonomy. * **Attributes** — Lists the attributes defined in the taxonomy and their occurrences in the indexed content. ### Search and test The Search and Test tab lets you validate the knowledge base configuration by running queries against indexed content before connecting it to an agent. It shows exactly how the knowledge base will respond to a query, including which chunks are returned, how they are ranked, and how each stage of the search pipeline performed. **Query Settings** Enter a query in the Query field and configure the following settings before running a search: **Query Type** controls how the search matches the query to indexed content. Three options are available: * Hybrid - combines semantic search and keyword search. Returns results that are both conceptually relevant and lexically matched. Recommended for most use cases. * Semantic - finds conceptually relevant content based on meaning, even when the exact words don't appear in the source. * Keyword - finds exact or near-exact matches. Best for structured content where precise term matching is important. **Top K** defines the maximum number of chunks returned per query. Set this based on how much context the agent needs to compose an accurate response. A higher value returns more context but increases token consumption when the agent processes the results. **Run a Test Query** Click Search to execute the query. The platform returns: * Search retrieval time - total time taken and the time per search engine. * Results - ranked list of matching chunks showing relevance score, source file, content preview, and metadata fields. * Resolution Chain Debug, a step-by-step breakdown of how the query was processed. The **Diagnostics** panel on the right provides a real-time view of the knowledge base's health across three areas: * Data and Indexing - Shows the current state of indexed content, document count, chunk count, and the last indexed timestamp. * Enrichment - Shows the configuration status of vocabulary and field mappings. * Pipeline Health - Shows the embedding model in use, the current pipeline status, and whether any index errors have occurred. The **Query** History section shows recent queries run against the knowledge base, along with the number of results returned, query type, and time taken. ### Additional settings Manage knowledge base configuration, view indexing details, configure citation behavior, access API information, and perform administrative actions such as rebuilding or deleting the knowledge base. * General - Basic information about the knowledge base. * Index Configuration - Displays the retrieval and indexing settings used by the knowledge base. * Citations - Controls how source references appear in search results and AI-generated responses. When enabled, response includes references to the content used to generate the answer. | Option | Description | | - | - | | **Direct links** | Provides direct access to the source document when a citation is selected. | | **Time-limited links** | Generates temporary links that expire after a configured period. This helps control access to source content. Provide link expiry time in hours. | | **Click-limited links** | Generates links that can only be accessed a limited number of times before they become invalid. Provide the maximum time and maximum number of clicks for link expiry. | | **No links** | Displays citations without providing clickable links to the source document. | Available link behavior options may depend on the source type. * API & SDK - Provides programmatic access information for the knowledge base. * Rebuild Index - Reprocesses all documents and rebuilds the search index. Time taken to rebuild index depends on the content volume and knowledge base size. * Delete Knowledge Base - Permanently deletes the knowledge base and all associated data. ## Use knowledge base as a tool in agents Every knowledge base in the project automatically has a corresponding knowledge tool. Agents access the knowledge base content by attaching this tool. Once attached, the agent can invoke it at runtime to search the knowledge base and retrieve relevant content. To attach a knowledge tool from the agent configuration: 1. Navigate to the agent and open the **Tools** section. 2. Click **Attach Tool**. 3. From the list of available tools, locate the knowledge tool for your knowledge base — knowledge tools are listed alongside all other project tools. 4. Select the tool to attach it to the agent. **Attach a Knowledge Tool via DSL** To attach a knowledge tool using the DSL editor, follow these steps: 1. Navigate to **Knowledge Tools** at the project level. 2. Open the knowledge tool that corresponds to your knowledge base. 3. Copy the DSL definition shown on the tool page. 4. Open the agent's DSL editor by clicking **DSL** at the top of the agent page. 5. Paste the copied DSL into the **Tools** section of the agent definition. 6. Compile and save the changes. # Install and configure MCP server Source: https://koreai.mintlify.app/agent-platform/mcp-server Agent Platform exposes its features through a Model Context Protocol (MCP) server. Your AI assistant can connect to the platform and perform platform operations to let you build, evaluate, optimize, debug, and analyze agents. Use MCP server, to perform these tasks from within your IDE, command-line, or terminal, without context-switching. To use the MCP server, you install `@koreai/arch-mcp-tools` and configure your AI assistant. ## Prerequisites * **Node.js 18 or later** — We recommend LTS version. The server starts using `npx` command. * Acess to [Agent Platform account](/agent-platform/getting-started#region-specific-urls-and-allowlist-of-ips). When you connect for the first time, you must [authenticate](#authenticate). * MCP-compatible client. ## Install and configure The server (`@koreai/arch-mcp-tools`) is a standard stdio MCP server. The clients launch it using the command: `npx -y @koreai/arch-mcp-tools`. In your client's config, you add it as a server named: `arch-agent-platform`. Provide the environment URL in one of the following ways: * In the configuration file, add as value of `AGENTS_URL` parameter in the `env`. For environment, see the list of [region-specific platform URLs](/agent-platform/getting-started#region-specific-urls-and-allowlist-of-ips) or ask your platform administrator. * Pass `serverUrl` to `platform_connect`. * On the first use, the tool asks you for the environment. ```bash theme={null} claude mcp add arch-agent-platform -- npx -y @koreai/arch-mcp-tools ``` Or add it to `.mcp.json` (project) or `~/.claude.json` (global): ```json theme={null} { "mcpServers": { "arch-agent-platform": { "command": "npx", "args": ["-y", "@koreai/arch-mcp-tools"] }, "env": { "AGENTS_URL": "https://example.com" } } } ``` ```bash theme={null} codex mcp add arch-agent-platform -- npx -y @koreai/arch-mcp-tools --env AGENTS_URL="https://example.com" ``` Or add to TOML file named `~/.codex/config.toml`. ```toml theme={null} [mcp_servers.arch-agent-platform] command = "npx" args = ["-y", "@koreai/arch-mcp-tools"] [mcp_servers.arch-agent-platform.env] AGENTS_URL = "https://example.com" ``` Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global): ```json theme={null} { "mcpServers": { "arch-agent-platform": { "command": "npx", "args": ["-y", "@koreai/arch-mcp-tools"] }, "env": { "AGENTS_URL": "https://example.com" } } } ``` Add to `.vscode/mcp.json`. Note that the top-level key is `servers`, not `mcpServers`: ```json theme={null} { "servers": { "arch-agent-platform": { "command": "npx", "args": [ "-y", "@koreai/arch-mcp-tools" ] } "env": { "AGENTS_URL": "https://example.com" } } } ``` Configure a stdio server with command `npx` and args `["-y", "@koreai/arch-mcp-tools"]`. Optionally, set an `AGENTS_URL` variable to pin an environment. Alternatively, use a version of the following prompt in your AI coding tool's chat for it to do the configuration. ```text theme={null} Add an MCP server named `arch-agent-platform` that runs `npx -y @koreai/arch-mcp-tools`, and put it in this project's MCP config. Config location by tool — Claude Code: `.mcp.json` (`mcpServers`) · Cursor: `.cursor/mcp.json` (`mcpServers`) · VS Code: `.vscode/mcp.json` (`servers`) · Codex: `~/.codex/config.toml` (`[mcp_servers.arch-agent-platform]`). Optionally set `AGENTS_URL` to my environment. ``` ## Authenticate Authentication is automatic when you call `platform_connect` — it tries the following methods in the order: 1. **Explicit token** — pass an `authToken` parameter. 2. **Stored credentials** — reads the same encrypted credential store used by `kore-platform-cli login`. 3. **Device authorization** — opens your browser and polls until approval completes in the same `platform_connect` call. Credentials are stored securely and reused in future sessions. # Memory and state Source: https://koreai.mintlify.app/agent-platform/memory-and-state Agents on the platform use two kinds of memory: session memory and persistent memory. Learn how to declare, access, and manage session variables, persistent memory, and set up auto-recall using the ABL Editor. ## Key concepts | Concept | Description | | - | - | | [Session Memory](#session-memory) | Declare and use variables that hold data within a single conversation — collected values, flags, and intermediate results that are discarded when the session ends. | | [Persistent Memory](#persistent-memory) | Store facts that survive across sessions, such as user preferences and history. Covers user-scoped, project-scoped and execution-tree scoped variables and access control. | | [Recall](#recall) | Automatically loads values from persistent memory into session context at lifecycle events like session start or before a search. | | [Remember Triggers](#remember-triggers) | Automatically stores values into persistent memory when a condition is met. | | [Manipulate State: SET, CLEAR, and TRANSFORM](#manipulate-state-set-clear-and-transform) | Assign values, remove variables, and reshape data arrays during flow execution using `SET`, `CLEAR`, and `TRANSFORM` directives. | | [Best Practices](#best-practices) | Recommended patterns for naming variables, providing fallback values, and managing reset behavior. | ## Session memory Session variables hold data that is relevant only during a single conversation. They are created when the session starts and discarded when the session ends (unless RESET: never is specified). ### Configure session variables in the UI Open the agent, go to **Capabilities** > **Memory** in the left nav, and scroll to the SESSION VARIABLES section. Click **+ Add Variable** to add a new session variable. Each variable has the following fields. | UI Field | Input Type | Description | | - | - | - | | Variable name | Text input | Name of the session variable (top field in card) | | Type | Text input | Value type — string, number, boolean, object, array, date | | Description | Text input | Human-readable purpose for the variable | | Initial value | Text input (optional) | Value assigned when the session starts | | Reset behavior | Dropdown | When to reset — Default reset (`per_session`), `per_step`, or `never`. | ### Declare session variables via ABL Add a `MEMORY:` block with a `session:` section to your agent definition in the ABL editor. ```yaml theme={null} AGENT: Booking_Agent GOAL: "Help users book hotels" MEMORY: session: - selected_booking TYPE: string DESCRIPTION: "The booking ID the customer is currently managing" - action_type TYPE: string DESCRIPTION: "What the customer wants to do" - pending_changes TYPE: object DESCRIPTION: "Proposed changes awaiting confirmation" - quoted_fee TYPE: number DESCRIPTION: "Fee quoted for the pending modification" ``` **Session Variable Properties** | Property | Type | Required | Description | | - | - | - | - | | name | string | Yes | Variable name, used as the key in session context. | | TYPE | string \| number \| boolean \| date \| array \| object | No | Value type for runtime validation and field resolution. | | DESCRIPTION | string | No | Human-readable description of the variable's purpose. | | INITIAL | any | No | Initial value assigned when the session starts. Initial value is `null` by default. | | RESET | per\_session \| per\_step \| never | No | When to reset the variable. Default is `per_session`. See Reset behavior. | **Reset Behavior** | Value | Behavior | | - | - | | per\_session | Variable is initialized at session start and cleared when the session ends. This is the default. | | per\_step | Variable is reset to its initial value at the start of each flow step. Useful for step-scoped accumulators. | | never | Variable persists for the lifetime of the runtime process. Use sparingly; prefer persistent memory instead. | | per\_activation | Variable is reset to `INITIAL` every time the agent is activated, including repeated activations of the same agent. | For simple tracking without type metadata, declare a bare name: ```yaml theme={null} MEMORY: session: - customer_id - order_total ``` Fields with initial values and reset control: ```yaml theme={null} MEMORY: session: - cart_items TYPE: array DESCRIPTION: "Items currently in the shopping cart" INITIAL: [] RESET: per_session - attempt_count TYPE: number INITIAL: 0 RESET: per_step ``` ### Access session variables in response templates Reference session variables by name inside `{{ }}` in `RESPOND` blocks. ```yaml theme={null} FLOW: confirm_booking: REASONING: false RESPOND: | Booking summary: Hotel: {{selected_hotel.name}} Guest: {{guest_name}} Total: ${{quoted_fee}} THEN: COMPLETE ``` ### Access session variables in step conditions Use session variables in `CHECK`, constraint expressions, and `ON_INPUT` transition logic. ```yaml theme={null} FLOW: check_eligibility: REASONING: false CHECK: attempt_count < 3 ON_FAIL: too_many_attempts THEN: process_request ``` ### Troubleshooting * **Variable is undefined in template.** The variable has not been set yet in the current flow. Ensure the step that sets it executes before the step that reads it. * **Variable resets unexpectedly.** Check `RESET`. Variables with `RESET: per_step` clear at each step transition. Use `per_session` or `never` for longer-lived values. * **Type mismatch at runtime.** `TYPE` enables runtime validation. If you declare `TYPE: number` but assign a string, the runtime logs a warning. Coerce tool results and user inputs to the declared type. *** ## Persistent memory Persistent variables are stored in the platform's fact store and survive across sessions. Persistent memory fields are scoped to a user, a execution chain or an entire project. ### Configure persistent variables from UI Navigate to **Memory** > **Persistent Paths**. **Persistent Paths**: Use persistent paths when you want the agent to automatically preserve information without requiring a predefined schema. Examples: ``` user.preference.language user.preference.timezone customer.account_type ``` **Structured Persistent Paths**: Structured Persistent Paths allow you to define persistent memory using a predefined structure or schema. Use structured persistent paths when you want to organize long-term memory into structured objects instead of individual dotted paths. Examples: ```json theme={null} { "userProfile": { "name": "", "email": "", "preferences": { "language": "", "timezone": "" } } } ``` **Read Access Paths**: Stores fields as persistent memory paths with read access. **Write Access Paths**: Stores as persistent memory paths with write access. Use READS/WRITES for quick, intent-clear declarations of access-only paths without other metadata for the fields. ### Declare persistent variables via ABL Add a `persistent:` section under `MEMORY:`. Use dot-notation paths to name each variable and bind them to a scope. ```yaml theme={null} AGENT: Support_Agent GOAL: "Help customers with personalized support" MEMORY: persistent: - user.preferred_language - user.loyalty_tier SCOPE: user - user.booking_history SCOPE: user TYPE: array DESCRIPTION: "Recent booking history" - user.notification_preference TYPE: string DEFAULT: "email" SCOPE: user ``` **Persistent Variable Properties** | Property | Type | Required | Default | Description | | - | - | - | - | - | | path | string | Yes | -- | Dot-notation path (for example. user.preferred\_chains), where the first segment indicates the scope(user, project or workflow) and second indicates the name of the field. | | SCOPE | user \| project \| execution\_tree | No | user | Ownership scope. user is per-user; project is shared across all users. execution\_tree is for an entire execution workflow across agents. If this scope differs from the scope in the path, scope takes precedence. | | ACCESS | read \| write \| readwrite | No | readwrite | Access direction. Constrains whether the agent can read, write, or both. | | TYPE | string \| number \| boolean \| date \| array \| object | No | -- | Value type for runtime validation. | | UNIT | string | No | -- | Unit of the stored value (for example. USD, km). Informational metadata only. | | DEFAULT | any | No | null | Default value when no fact exists in the store. | | DESCRIPTION | string | No | -- | Human-readable description. | ### Scoping rules | Scope | Description | | - | - | | User scope (SCOPE: user) | Values are unique per authenticated user. Two users in the same project see different values for the same path. | | Project scope (SCOPE: project) | Values are shared across all users within the project. Useful for reference data, feature flags, or global configuration. | | Execution-tree scope (SCOPE: execution\_tree) | Values are shared across one workflow / handoff tree only. This is the preferred scope for cross-agent state such as auth tokens, selected account IDs, or in-flight case context. | ```yaml theme={null} MEMORY: persistent: - user.preferred_chains SCOPE: user DESCRIPTION: "User's preferred hotel chains" - user.wire_history_30d SCOPE: user TYPE: array ACCESS: readwrite DEFAULT: [] - project.exchange_rates SCOPE: project ACCESS: read DESCRIPTION: "Current exchange rates (shared across all users)" ``` ### Access control Restrict whether the agent can read, write, or both for each persistent path. ```yaml theme={null} MEMORY: persistent: - user.preferences ACCESS: readwrite - user.account_status ACCESS: read - user.interaction_log ACCESS: write ``` | Value | Behavior | | - | - | | `read` | Agent can read but not modify the value. | | `write` | Agent can write but not read the current value. | | `readwrite` | Agent can both read and modify. (Default.) | ### Access persistent variables in expressions Use the full dot-notation path within `{{ }}` to read persistent variables in `SET` expressions and `RESPOND` templates. ```yaml theme={null} SET: greeting = COALESCE(user.name, "valued customer") RESPOND: "Welcome back, {{greeting}}! Your tier is {{user.loyalty_tier}}." ``` ### Troubleshooting * **Persistent value is null on first session.** The value has not been stored yet. Use `DEFAULT` to provide a fallback, or use `COALESCE()` in expressions. * **Value not persisting across sessions.** Ensure the value is written using a `remember` trigger or `SET` statement. Declaring a persistent path does not automatically populate it. * **Wrong user sees another user's data.** Verify `SCOPE: user` is set. Project-scoped variables are shared. User-scoped variables require user authentication to resolve correctly. *** ## Remember triggers Remember triggers write values to persistent memory when a condition evaluates to `true` during conversation. To configure a Remember trigger, provide the following fields: * When: Specifies the condition under which the trigger is executed and the memory value is stored. * Store Value: The value to be written to persistent memory when the trigger condition is met. * Store Target: The persistent memory field where the value will be stored. * TTL: The duration for which the stored value remains in memory. After the specified time elapses, the value is automatically deleted. This field is optional. If not specified, the value is retained indefinitely. ```yaml theme={null} AGENT: Support_Agent GOAL: "Help customers with personalized support" MEMORY: persistent: - user.name - user.language - user.booking_history remember: - WHEN: user_name IS SET STORE: user_name -> user.name TTL: "90d" - WHEN: preferred_language IS SET STORE: preferred_language -> user.language ``` When `user_name` gets a value during the conversation (from a `GATHER` step, `SET` assignment, or tool result), the trigger fires and stores it to `user.name`. `TTL: "90d"` means the stored fact expires after 90 days. **Remember Trigger Properties** | Property | Type | Required | Default | Description | | - | - | - | - | - | | WHEN | string | Yes | -- | Condition expression that triggers the store operation. Evaluated after each turn. | | STORE | object | Yes | -- | Defines the value expression and the target persistent path. Inline syntax: `value_expr -> target_path`. Use the block form (below) to add array-write modifiers. | | OPERATION | string | No | `overwrite` | Write mode for an **array** target. `overwrite` replaces the whole value (legacy). `append` adds the value to the existing array. Block form only. | | UNIQUE | boolean | No | `false` | When appending, skip values already present in the array (first occurrence kept). Block form only. | | MAXSIZE | number | No | -- | When appending, cap the array length. Oldest items are evicted first (FIFO). Block form only. | | TTL | string | No | -- | Time-to-live for the stored fact. After expiration, the value is deleted. Examples: "30d", "24h". TTL values use a duration string: s - Seconds, m - Minutes, h - Hours, d - Days | ### Store computed values Store a computed object instead of a raw variable value. ```yaml theme={null} MEMORY: remember: - WHEN: action_completed == true STORE: {booking_id: selected_booking, action: action_type, date: now} -> user.booking_history ``` ### Append to an array (accumulate a list) By default a Remember trigger *overwrites* the target each time it fires. To accumulate values into a list instead — booking history, recent searches, visited topics — use the multi-line `STORE` block with `operation: append`. ```yaml theme={null} MEMORY: persistent: - user.booking_history TYPE: array DEFAULT_VALUE: [] remember: - WHEN: action_completed == true STORE: value: {booking_id: selected_booking, action: action_type, date: now} target: user.booking_history operation: append unique: true # skip records already in the list maxSize: 20 # keep only the 20 most recent (FIFO) ``` * **`operation: append`** — adds the value to the existing array instead of replacing it. A scalar is appended as one item; an array value is concatenated. * **`unique: true`** — drops duplicates, keeping the first occurrence. Objects are compared by value. * **`maxSize: N`** — caps the list at `N` items, evicting the oldest first. Array modifiers require the block form of `STORE` shown above — the inline `value -> target` arrow form does not accept them. Without `operation: append`, the trigger keeps the default overwrite behavior. ## Recall Recall automatically stores values into persistent memory when conditions are met, and loads stored facts back into the session at specific lifecycle events. ### Add recall instructions ```yaml theme={null} MEMORY: recall: - ON: session:start ACTION: inject_context PATHS: [user.name, user.language, user.preferred_agent] - ON: session:start ACTION: prompt_llm INSTRUCTION: "Greet the user by name if known" ``` When a new session starts, the platform loads `user.name`, `user.language`, and `user.preferred_agent` from persistent memory and injects them into the session context. **Recall Properties** | Field | Required | Description | | - | - | - | | `ON` | Yes | Canonical event name such as `session:start` or `tool::after`. | | `ACTION` | Yes | `inject_context`, `load_memory`, or `prompt_llm`. | | `PATHS` | No | Durable memory paths to retrieve for `inject_context` or `load_memory`. | | `DOMAIN` | No | Named memory domain used by `load_memory` when paths are domain-scoped. | | `INSTRUCTION` | No | Prompt text used by `prompt_llm` to shape how retrieved context is applied. | **Recall Events** | Event | When it fires | | - | - | | session:start | When a new session initializes, before any user input. | | search:before | Before a search or retrieval operation. | **Recall Actions** | Action | Behavior | | - | - | | `inject_context` | Loads specified persistent paths directly into session variables. | | `load_memory` | Loads all facts in the specified domain into context. | | `prompt_llm` | Passes the instruction text to the LLM as additional context. (Default.) | ### Recall before search operations Load relevant user context before a knowledge base search executes. ```yaml theme={null} MEMORY: recall: - ON: search:before ACTION: load_memory DOMAIN: "travel_preferences" ``` This loads all facts tagged with the `travel_preferences` domain into the session, allowing the search to incorporate user preferences. ### Inject recalled data as an LLM instruction Instead of directly injecting variables, pass an instruction to the LLM on how to use the recalled information. ```yaml theme={null} MEMORY: recall: - ON: session:start ACTION: prompt_llm INSTRUCTION: | Check if you have the user's name and preferences from previous conversations. If so, greet them personally and reference their past interactions. If not, introduce yourself and start fresh. ``` ### Combined remember and recall pattern A complete pattern that stores user preferences during a conversation and recalls them at the start of future sessions. ```yaml theme={null} MEMORY: session: - selected_booking TYPE: string - action_type TYPE: string persistent: - user.booking_history TYPE: array DEFAULT_VALUE: [] - user.preferences remember: - WHEN: action_completed == true STORE: value: {booking_id: selected_booking, action: action_type, date: now} target: user.booking_history operation: append - WHEN: user_preference IS SET STORE: user_preference -> user.preferences recall: - ON: session:start ACTION: inject_context PATHS: [user.booking_history, user.preferences] - ON: tool:list_user_bookings:after ACTION: inject_context PATHS: [user.preferences] ``` ### Troubleshooting * **Remember trigger never fires.** The `WHEN` condition references a variable that is never set during conversation. Verify the variable name matches what your flow or tools produce. * **Recalled value is stale.** The fact may have expired (TTL elapsed). Check the TTL setting and extend it if needed. * **Recall injects null values.** The persistent path has not been written to yet. Use `DEFAULT` on the persistent variable declaration to provide a fallback. *** ## Manipulate state: SET, CLEAR, and TRANSFORM Use `SET`, `CLEAR`, and `TRANSFORM` directives in flow steps to assign values, remove variables, and reshape data arrays during execution. ### SET a variable Use `SET` in a flow step to assign a value to a session variable. The expression is resolved at execution time. ```yaml theme={null} FLOW: calculate_total: REASONING: false SET: - total_price = nights * price_per_night - tax_amount = total_price * 0.12 - final_total = total_price + tax_amount RESPOND: "Your total is ${{final_total}} (including ${{tax_amount}} tax)." THEN: confirm_booking ``` `SET` expressions support arithmetic, string interpolation, dot-notation access, and function calls. **From tool results:** ```yaml theme={null} FLOW: process_search: REASONING: false CALL: search_hotels(destination, checkin_date, checkout_date, num_guests) ON_SUCCESS: SET: - hotel_count = call_result.hotels.length - cheapest_price = call_result.hotels[0].price RESPOND: "Found {{hotel_count}} hotels. Prices start at ${{cheapest_price}}." THEN: select_hotel ``` **Nested objects using dot notation:** ```yaml theme={null} SET: - booking.guest_name = guest_name - booking.check_in = checkin_date - booking.total = final_total ``` **In `ON_INPUT` branches:** ```yaml theme={null} FLOW: select_hotel: REASONING: false ON_INPUT: - IF: abl.is_number(input) AND input >= 1 AND input <= hotels.length SET: - selected_hotel = hotels[input - 1] - need_price_quote = true THEN: collect_guest_info - ELSE: RESPOND: "Please enter a valid hotel number." ``` ### CLEAR a variable Use `CLEAR` to remove one or more variables from the session. After a `CLEAR`, the variable is undefined and any template reference to it renders as empty. ```yaml theme={null} FLOW: restart_search: REASONING: false CLEAR: [destination, checkin_date, checkout_date, search_results] RESPOND: "Let's start fresh. Where would you like to go?" THEN: collect_trip_info ``` **In sub-intents** — allow users to clear and re-enter specific fields: ```yaml theme={null} FLOW: collect_trip_info: REASONING: false GATHER: - destination: required - checkin_date: required - checkout_date: required SUB_INTENTS: - INTENT: "change destination" CLEAR: [destination] RESPOND: "What's the new destination?" - INTENT: "change dates" CLEAR: [checkin_date, checkout_date] RESPOND: "What are your new travel dates?" COMPLETE_WHEN: destination AND checkin_date AND checkout_date THEN: search_hotels ``` ### TRANSFORM an array Use `TRANSFORM` to filter, map, sort, and limit array data in a single pipeline. In the following example `TRANSFORM` creates a new array `affordable_hotels` without modifying the source array. **Full pipeline:** ```yaml theme={null} FLOW: filter_hotels: REASONING: false TRANSFORM: SOURCE: search_results AS hotel INTO: affordable_hotels FILTER: hotel.price <= budget MAP: name: hotel.name price: hotel.price rating: hotel.rating SORT_BY: price asc LIMIT: 5 RESPOND: | Here are {{affordable_hotels.length}} hotels within your budget: {{#each affordable_hotels}} {{add @index 1}}. {{name}} - ${{price}}/night ({{rating}} stars) {{/each}} THEN: select_hotel ``` **Filter only:** ```yaml theme={null} TRANSFORM: SOURCE: all_bookings AS booking INTO: active_bookings FILTER: booking.status == "active" ``` **Map only:** ```yaml theme={null} TRANSFORM: SOURCE: raw_results AS item INTO: formatted_results MAP: title: item.name summary: item.description link: item.url ``` ### Troubleshooting * **`SET` expression produces null.** The right-hand variable may not be defined yet. Verify the data source (tool result, gathered field, or prior `SET`) runs before this step. * **`CLEAR` does not reset a `GATHER` field.** Clearing a variable removes it from the session, but the `GATHER` step may not re-prompt for it unless the step is re-entered. Use `CLEAR` within a `SUB_INTENT` or `DIGRESSION` that resumes the same step. * **`TRANSFORM` produces empty array.** The `FILTER` condition may be too strict, or the source array is empty. Check the source variable and filter expression. *** ## Best practices * **Use meaningful variable names.** Use names like `user_preferences`, `order_history`, or `conversation_context`. Avoid generic names like `var1`, `data`, or `temp`. * **Use `COALESCE()` for persistent variables.** Persistent paths may not be populated on a user's first session. Use `COALESCE(user.name, "valued customer")` to provide a safe fallback. * **Set defaults on persistent variables.** Declare `DEFAULT` values so the agent behaves correctly before any data has been stored. * **Use `CLEAR` within sub-intents to reset state.** Clearing a variable removes it from the session, but a `GATHER` step only re-prompts when the step is re-entered. Pair `CLEAR` with a `SUB_INTENT` or `DIGRESSION` that resumes the same step. * **Prefer `per_session` reset over `never`.** Variables with `RESET: never` persist for the lifetime of the runtime process. Reserve this for counters or flags where cross-session accumulation is intentional. *** **Related articles:** * [Memory & Constraints Reference](/agent-platform/abl-reference/memory-and-constraints) -- Full ABL syntax for MEMORY and CONSTRAINTS blocks. * [GATHER Reference](/agent-platform/abl-reference/gather) -- Collecting user input into session variables. * [Flow Reference](/agent-platform/abl-reference/flow) -- Flow step directives including SET, CLEAR, TRANSFORM. # Optimize with Arch AI Source: https://koreai.mintlify.app/agent-platform/optimize After your agents go live, Arch reads production traces, runs evaluations, and proposes improvements as reviewable patches — continuously, without manual triage. Each iteration raises the baseline quality across the entire project. ## The reinforcement loop Deployment is not the end of the pipeline — it is the start of a continuous cycle. Production evidence feeds evaluations, Arch analysis, reviewable patches, and promotion gates. The platform harness stays constant while models, tools, and workflows improve underneath. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A[Production Traces] --> B[Evaluations] B --> C[Arch Analysis] C --> D[Reviewable Patch] D --> E[Promote] E --> A ``` Each stage produces a defined output that feeds the next: | Stage | What Flows In | What Flows Out | | - | - | - | | **Production Traces** | Live agent sessions | Conversation, handoff, workflow, tool, guard, and retrieval events. (For more information, see [Sessions](/agent-platform/analytics-insights/analytics-explorer#session-explorer).) | | **Evaluations** | Trace events | Quality, safety, accuracy, cost, latency, and compliance scores. (For more information, see [Evaluations](/agent-platform/evals).) | | **Arch Analysis** | Eval results | Root-cause hypothesis and proposed improvement lever. | | **Reviewable Patch** | Arch analysis | ABL, workflow, tool, prompt, policy, or eval diff. | | **Promote** | Approved patch | Deployment behind gates, canary, or rollback. | Each approved patch is deployed to production, generating new traces that feed the next iteration. ### How the loop closes When Arch detects a degradation, a coverage gap, or a routing inefficiency, it: 1. Reads trace events directly from the platform runtime — no separate session or manual trigger required. 2. Forms a root-cause hypothesis from the trace evidence. 3. Proposes a change as a reviewable diff: ABL, prompt, tool binding, policy, or eval. 4. Submits the patch for compiler validation before a developer reviews the diff. Developers must approve every change. The compiler validates the structure of each proposal before it reaches review, so broken references and incomplete policies are caught before promotion. ### Design principles The reinforcement loop is designed around three characteristics that make continuous improvement sustainable at scale. | Loop Characteristic | Description | | - | - | | **Autonomous analysis** | Arch turns signal into concrete improvement proposals. Humans set the bar and retain approval — the analysis loop runs without manual triage. | | **Customer-gated** | Every evaluation and promotion gate is domain-specific. You define what "good" means; the platform enforces the same process for every change. | | **Compounding** | Each run enriches the evaluation suite. Each evaluation sharpens the next optimization. Each patch strengthens the reusable artifact. | ## Arch in the project sidebar Arch lives in the project sidebar — available throughout the agent lifecycle, not just during authoring. From the sidebar, you can monitor project health, act on recommendations, and trigger workflows without context-switching. Project health monitoring and continuous improvement by Arch | Feature | What It Does | | - | - | | **Project Health** | Runs continuous status checks and flags specific warnings when anomalies are detected. | | **Suggested Workflows** | Surfaces contextual next steps — Add Agent, Debug Issue, Improve, Review Topology — based on current project state. | | **Quick Actions** | One-click access to Explore project, Health check, and Review integrations. | | **Auto Recommendations** | Proposes concrete fixes grounded in your actual project, such as missing session memory on a stateful agent. | | **Contextual Help** | Grounds every suggestion in your project — not generic guidance. | # Multi-agent orchestration Source: https://koreai.mintlify.app/agent-platform/orchestrate Multi-agent orchestration distributes tasks across specialized agents, each focused on a specific domain such as billing, authentication, or order tracking. Orchestration relies on three components: * **Routing**: Determines which agent handles a request based on user intent, session state, or custom conditions. The supervisor evaluates routing rules on every message. * **Context propagation**: Gives the target agent the data it needs without asking the user to repeat themselves. Context does not transfer automatically. You must pass it explicitly. * **Control flow**: Determines whether the calling agent keeps control after routing or transfers it permanently. This controls how results flow back and whether the user notices a transfer. Together, these mechanisms let you build systems where the user experiences a single, unified assistant while specialized agents work together behind the scenes. ## Types of orchestration The platform supports four orchestration patterns: | Pattern | When to use | User experience | | - | - | - | | [Supervisor](/agent-platform/orchestrate#supervisor) | Entry point for multi-agent systems; routes by intent. | The user always talks to one agent, which routes the request to a specialized agent as needed. | | [Handoff](/agent-platform/orchestrate#handoff) | Route the conversation to a specialist agent. | The user is transferred to a new agent. | | [Delegation](/agent-platform/orchestrate#delegate) | The agent needs a sub-task done. It sends the sub-task to another agent and gets the result back. | Transparent. The user does not see the delegation. | | [Fan-out](/agent-platform/orchestrate#fan-out) | The agent needs multiple things done in parallel. It runs multiple agents and merges the results. | Transparent. The user sees a single combined result. | | [Escalation](/agent-platform/escalate) | Transfer to a human agent with full context. | The user interacts with a human agent. | **Sample Multi-Agent Orchestration** ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart TD U[End User] --> S[Supervisor Agent] S -- routes to --> B[Billing Agent] S -- routes to --> T[Technical Support Agent] S -- routes to --> SA[Sales Agent] B -- delegates to --> P[Payment Processing Agent] T -- fans out to --> D[Diagnostics Agent] T -- fans out to --> K[Knowledge Search Agent] ``` ## Configure orchestration You can configure orchestration in two ways: using Studio or using the ABL editor. Both methods are equivalent and interchangeable. ### Using the Agent Platform Studio * Navigate to the **Agents** page to view all agents in the project. Orchestration * Use the **Canvas view** to visualize relationships between agents. Orchestration * Select an agent to open its configuration page. * Configure **handoffs or delegation** under the **Coordination** section. * Save your changes. ### Using the ABL editor * Open the agent and switch to **ABL view**. * Define the orchestration logic using ABL constructs such as: * `SUPERVISOR` * `HANDOFF` * `DELEGATE` * Compile and save your changes. * Updates apply immediately after a successful compilation. The UI and ABL are interchangeable. Changes made in ABL appear in the UI, and changes made in the UI appear in ABL. ## Orchestration patterns ### Supervisor A supervisor is a special agent that acts as the central router for a multi-agent system. It does the following: * Receives every incoming message. * Routes each message to a specific agent based on user intent, session state, channel, or custom conditions. * Performs pre-routing actions, such as fetching data or checking authentication state, before forwarding the request to a specialized agent. A multi-agent system can have only one supervisor. It is the entry point for all user interactions. #### How it works When a user sends a message, the supervisor: * Receives the message as the active agent. * Optionally performs a pre-routing action using tools. * Evaluates its `HANDOFF` rules top to bottom. * Routes the message to the first agent whose `WHEN` condition matches. * Regains control when the child agent completes with `RETURN: true`. * Handles the message directly if no `HANDOFF` rule matches and `canRespondDirectly` is enabled. Otherwise, it returns an error. #### How to set up Define a supervisor using the `SUPERVISOR` keyword in the ABL file. ```yaml theme={null} SUPERVISOR: "agent-name" ``` Then define the following properties for the supervisor agent. | Field | Required | Description | | - | - | - | | GOAL | Yes | What the supervisor is responsible for. Helps the LLM understand its role. | | PERSONA | No | Defines the supervisor's tone and communication style. | | LIMITATIONS | No | Specifies what the supervisor can't do to prevent incorrect handling. | | ON\_START | No | Message sent to the user at the start of the session. | | TOOLS | No | Tools the supervisor can invoke before routing. | | HANDOFF | Yes | List of agents to which the request is routed based on the conditions. The Platform evaluates the rules top-to-bottom; first match wins. | | RETURN\_HANDLERS | No | Defines actions when a child agent returns control. | | ESCALATE | No | Conditions that trigger escalation to a human agent. | | ON\_ERROR | No | Fallback behavior when routing fails or an agent is unavailable. | | COMPLETE | No | Responses sent when the session ends or a handoff completes. | | MEMORY | No | Session and persistent memory configuration. | #### Examples **1. Travel Supervisor** ```yaml theme={null} SUPERVISOR: Travel_Supervisor PERSONA: "Professional travel assistant. Friendly and efficient. Routes requests to the right specialist quickly and transparently." GOAL: "Route customers to the right specialist -- booking, sales, support, or human agent -- with full context preservation." LIMITATIONS: - "Cannot make bookings or process payments directly." - "Cannot access user account information directly." ON_START: - RESPOND: "Welcome! I am your travel assistant. I can help you search and book flights, manage a booking, or connect you with support. What can I help you with today?" RETURN_HANDLERS: route_to_booking_manager: CLEAR: [return_to] CONTINUE: true reclassify_intent: CLEAR: [current_intent] RESUME_INTENT: true HANDOFF: - TO: Sales_Agent WHEN: intent.category == "new_booking" OR intent.category == "travel_search" CONTEXT: pass: [search_context, user_preferences, budget] summary: "User looking to book new travel." - TO: Booking_Manager WHEN: intent.category == "manage_existing_booking" CONTEXT: pass: [user_id, booking_context, auth_token] summary: "User managing their reservation." ESCALATE: triggers: - WHEN: user.wants_human_agent == true OR user.frustration_detected == true REASON: "User requests human assistance." PRIORITY: high TAGS: [human_request] COMPLETE: - WHEN: handoff_successful == true RESPOND: "I have connected you with the right specialist." - WHEN: user.session_ended == true RESPOND: "Thank you for using our travel service." ``` **2. Priority-ordered routing** Routing rules are evaluated top to bottom. Use specific rules before general ones. ```yaml theme={null} RETURN_HANDLERS: route_to_booking_manager: CLEAR: [return_to] CONTINUE: true reclassify_intent: CLEAR: [current_intent] RESUME_INTENT: true ESCALATE: triggers: - WHEN: intent.category == "escalation" OR user.frustration_detected == true REASON: "User requests human assistance or is frustrated" PRIORITY: high TAGS: [human_request] - WHEN: intent.category == "complaint" REASON: "Customer complaint requires a human specialist" PRIORITY: high TAGS: [complaint] HANDOFF: - TO: Authentication_Agent WHEN: user.is_authenticated == false AND intent.category == "manage_booking" CONTEXT: pass: [session_context, return_to] summary: "User needs to authenticate" history: summary_only memory_grants: - path: workflow.auth_token access: readwrite - path: user.last_verified_at access: read RETURN: true ON_RETURN: handler: route_to_booking_manager - TO: Booking_Manager WHEN: user.is_authenticated == true AND intent.category == "manage_booking" CONTEXT: pass: [user_id, booking_context, auth_token] summary: "Authenticated user managing reservation" # P4 -- New bookings - TO: Sales_Agent WHEN: intent.category == "new_booking" OR intent.category == "travel_search" CONTEXT: pass: [search_context, user_preferences, budget] summary: "User looking to book" # P5 -- Fallback - TO: Clarification_Agent WHEN: intent.unclear == true OR intent.confidence < 0.5 CONTEXT: pass: [session_context, last_message] summary: "Need clarification" RETURN: true ON_RETURN: handler: reclassify_intent Supervisor with tools: Supervisors can have tools for pre-routing tasks. SUPERVISOR: NOC_Supervisor GOAL: "Triage network alarms and route to specialist agents" TOOLS: get_active_alarms(severity: string = "all") -> {alarms: array, total: number} description: "Retrieve active alarms from the network management system" type: http endpoint: "https://nms.example.com/api/alarms" method: POST auth: bearer HANDOFF: - TO: Network_Triage WHEN: alarm.category IN ["link_degradation", "fiber_cut", "hardware_warning"] CONTEXT: pass: [alarm_id, severity, site_code, category] summary: "New alarm requires triage" RETURN: true - TO: Incident_Manager WHEN: severity == "critical" OR severity == "major" CONTEXT: pass: [alarm_id, severity, site_code, affected_subscribers] summary: "High-severity issue requires incident management" RETURN: true ``` ### Handoff A handoff transfers the conversation from one agent to another. The original agent stops handling the request at the point of handoff. A handoff can originate from a supervisor routing to a specialist agent, or directly from one agent to another for peer-to-peer routing. In both cases, the target agent receives the context and continues the conversation from that point. #### How it works * The calling agent evaluates its `HANDOFF` rules. * When a condition is met, the conversation transfers to the target agent. * The target agent receives the context defined in the `CONTEXT` block. * `RETURN` must be set to `false` for handoff orchestration. #### How to set up Configure the handoff in the originating agent, which is the agent that initiates the transfer. * In the Studio, open the agent and add a handoff under the **Coordination** section. * Alternatively, define the following properties in the ABL editor. | Field | Required | Description | | - | - | - | | TO | Yes | Name of the target agent | | WHEN | Yes | Condition for routing; accepts expressions or natural language. | | CONTEXT | No | Data passed to the target agent (pass, summary, history, memory\_grants). | | RETURN | No | Whether control returns to the supervisor or parent agent after completion (default: false). | | ON\_RETURN | No | Action when the child returns; references a RETURN\_HANDLERS entry. | | PRIORITY | No | Evaluation priority: lower numbers are evaluated first. | Use `HANDOFF` for machine-to-machine agent routing only. Use `ESCALATE` when the target agent is a human operator. **Context properties** | Property | Description | | - | - | | pass | An array of variables to send to the target. The target agent receives these as pre-populated session variables. | | summary | Natural language description of why the handoff occurred. | | memory\_grants | Explicit durable-memory grants (path + access) for the receiving agent. | | history | Conversation history strategy (default: `full`). | **RETURN\_HANDLERS actions** * `CLEAR` - Removes the listed session variables before continuing. * `CONTINUE` - Resumes normal routing after the return. * `RESUME_INTENT` - Re-evaluates the user's intent and re-routes. **History Strategies** | Value | Behavior | | - | - | | `auto` | Use the handoff summary when available; otherwise, pass bounded recent history | | `none` | No conversation history passed | | `summary_only` | Pass only the handoff summary, never raw messages | | `full` | Pass the entire conversation history(default) | | `{ mode: last_n, count: }` | Pass only the last N messages using the typed bounded-history form | #### Examples **1. Handoff from Supervisor** ```yaml theme={null} SUPERVISOR: Support_Supervisor HANDOFF: - TO: Order_Tracking WHEN: intent.category == "order_inquiry" OR intent.category == "shipping" CONTEXT: pass: [customer_id, order_id, session_context] summary: "Customer wants to track or manage an order" RETURN: false ``` **2. Handoff from an agent** ```yaml theme={null} AGENT: Order_Tracking GOAL: "Help customers track orders" HANDOFF: - TO: Returns_And_Refunds WHEN: intent.category == "return" OR intent.category == "refund" CONTEXT: pass: [customer_id, order_id, item_id] summary: "Customer wants to return or get a refund for an item." RETURN: false - TO: Sales_Agent WHEN: reorder_ready == true AND user.ready_to_checkout == true CONTEXT: pass: [customer_id, cart_items, total] summary: "Customer reordering -- cart ready for checkout." RETURN: false ``` **3. Pass variables during handoff** ```yaml theme={null} HANDOFF: - TO: Booking_Manager WHEN: intent.category == "manage_booking" CONTEXT: pass: [user_id, booking_context, auth_token] summary: "Authenticated user managing their reservation" ``` **4. Scoped access to memory on handoff** ```yaml theme={null} HANDOFF: - TO: Authentication_Agent WHEN: user.is_authenticated == false AND action_requires_auth == true CONTEXT: pass: [session_context, return_to] summary: "User needs to authenticate" memory_grants: - path: workflow.auth_token access: readwrite - path: user.last_verified_at access: read RETURN: true ``` **5. Pass complete history as context** ```yaml theme={null} HANDOFF: - TO: Support_Agent WHEN: intent.category == "support" CONTEXT: pass: [customer_id, issue_description] summary: "Customer needs technical support" history: full ``` ### Delegate Delegation is a call-and-return pattern. The parent agent sends a task to a child agent, waits for the result, and then continues its own processing with the returned data. The delegation is transparent. The user does not see it. #### How it works 1. The parent agent evaluates its `DELEGATE` conditions. 2. When a condition matches, the parent sends the defined `INPUT` to the sub-agent. 3. The sub-agent processes the task and returns its result through its `COMPLETE` property. 4. The parent agent maps the returned values to its own session variables using `RETURNS`. 5. The parent agent continues processing using the result, as defined in `USE_RESULT`. 6. If the sub-agent fails or times out, `ON_FAIL` handles the error. #### How to set up Configure the delegation in the originating agent, which is the agent that initiates the transfer. * In the Studio, open the agent and add **Delegate** under the **Coordination** section. * Alternatively, define the following properties in the `DELEGATE` block in the ABL editor. | Property | Required | Description | | - | - | - | | `AGENT` | Yes | Name of the sub-agent to call | | `WHEN` | Yes | The condition that triggers the delegation | | `PURPOSE` | Yes | Description of what the sub-agent should accomplish | | `INPUT` | Yes | Map of parent variables to sub-agent input parameters (sub-agent param → parent session variable) | | `RETURNS` | Yes | Map of sub-agent output fields back to parent session variables (delegate variable → parent session variable) | | `USE_RESULT` | Yes | Instructions for how the parent agent uses the result | | `TIMEOUT` | No | Maximum time to wait for the sub-agent (for example, 10s) | | `ON_FAIL` | No | Action if the sub-agent fails or times out. Options: RESPOND "message" (show a message and continue), ESCALATE (trigger escalation), RETRY count (retry the delegation) | #### Examples **1. Set up delegate from an agent.** ```yaml theme={null} DELEGATE: - AGENT: Fee_Calculator WHEN: action_type == "modify" OR action_type == "upgrade" PURPOSE: "Calculate total fees and price differences for the requested changes" INPUT: booking_id: selected_booking change_type: action_type changes: change_details RETURNS: total_fee: quoted_fee breakdown: fee_breakdown USE_RESULT: "Present fee breakdown to customer before asking for confirmation" TIMEOUT: "10s" ON_FAIL: RESPOND "Unable to calculate fees right now. Let me try again." ``` **2. Corresponding Delegate Agent** ```yaml theme={null} AGENT: Fee_Calculator GOAL: | Calculate all applicable fees for booking changes -- modification fees, price differences, upgrade costs -- and return a clear breakdown. PERSONA: | Precise fee calculation specialist. Returns detailed breakdowns. TOOLS: get_modification_fee(booking_id: string, change_type: string) -> {base_fee: number, currency: string} description: "Get the base modification fee" calculate_price_difference(booking_id: string, original_item: object, new_item: object) -> {price_diff: number, breakdown: object} description: "Calculate price difference between original and new selection" COMPLETE: - WHEN: fee_calculated == true RESPOND: | Fee breakdown: {{#each fee_breakdown}} - {{this.description}}: {{this.amount}} {{this.currency}} {{/each}} Total: {{total_fee}} {{currency}} ``` **3. Pass Context to Delegate Agents** ```yaml theme={null} DELEGATE: - AGENT: Fee_Calculator WHEN: action_type == "modify" PURPOSE: "Calculate fees for the modification" INPUT: booking_id: selected_booking change_type: action_type changes: change_details RETURNS: total_fee: quoted_fee breakdown: fee_breakdown USE_RESULT: "Present fee breakdown to customer" ``` ### Fan-out Fan-out sends a task to multiple agents simultaneously and aggregates their results, such as searching flights and hotels in parallel. The parallel execution is transparent. The user sees a single combined response. #### How it works 1. A user message matches the `WHEN` condition on multiple `HANDOFF` or `DELEGATE` rules simultaneously. 2. All matching agents run in parallel. 3. Each agent runs independently and returns its results. 4. Results merge back into the calling agent's state. 5. The calling agent responds with the aggregated result. 6. If one agent fails, its `ON_FAIL` handler runs independently and the other agents continue. #### How to set up You can implement fan-out in two ways: * **Via Supervisor HANDOFF**: Define multiple `HANDOFF` rules with the same `WHEN` condition and `RETURN: true`. Each matching rule triggers a separate agent. Use `RETURN_HANDLERS` to merge results as each agent returns. * **Via DELEGATE**: Define multiple `DELEGATE` entries with overlapping `WHEN` conditions. Each delegate runs independently and returns its result to the parent agent via `RETURNS`. Use distinct variable names in `RETURNS` for each delegate to prevent one result from overwriting another. #### Examples **1. Via Supervisor HANDOFF - Route to multiple agents from a supervisor** In the following example, when `intent.category == "plan_trip"` matches, all three handoff rules trigger. Each agent runs independently and returns results to the supervisor via `RETURN: true`. Child return data merges back into the supervisor state before the named return handler continues orchestration. ```yaml theme={null} SUPERVISOR: Travel_Planner GOAL: "Help users plan trips by searching flights, hotels, and activities in parallel" RETURN_HANDLERS: merge_flight_results: CONTINUE: true merge_hotel_results: CONTINUE: true merge_activity_results: CONTINUE: true HANDOFF: - TO: Flight_Search WHEN: intent.category == "plan_trip" OR intent.category == "search_flights" CONTEXT: pass: [origin, destination, travel_dates, passengers] summary: "Search for available flights" RETURN: true ON_RETURN: handler: merge_flight_results - TO: Hotel_Search WHEN: intent.category == "plan_trip" OR intent.category == "search_hotels" CONTEXT: pass: [destination, checkin_date, checkout_date, guests] summary: "Search for available hotels" RETURN: true ON_RETURN: handler: merge_hotel_results - TO: Activity_Search WHEN: intent.category == "plan_trip" OR intent.category == "search_activities" CONTEXT: pass: [destination, travel_dates, interests] summary: "Search for activities and experiences" RETURN: true ON_RETURN: handler: merge_activity_results ``` **2. Via DELEGATE** Multiple `DELEGATE` entries with overlapping `WHEN` conditions can execute in parallel. Each delegate returns its results independently. ```yaml theme={null} AGENT: Trip_Planner DELEGATE: - AGENT: Flight_Search WHEN: need_flights == true PURPOSE: "Find available flights for the trip" INPUT: origin: departure_city destination: arrival_city date: travel_date RETURNS: flights: available_flights best_price: cheapest_flight_price USE_RESULT: "Include flight options in the trip plan" TIMEOUT: "15s" ON_FAIL: RESPOND "Flight search is unavailable. Continuing with hotel search." - AGENT: Hotel_Search WHEN: need_hotels == true PURPOSE: "Find available hotels at the destination" INPUT: destination: arrival_city checkin: checkin_date checkout: checkout_date RETURNS: hotels: available_hotels best_price: cheapest_hotel_price USE_RESULT: "Include hotel options in the trip plan" TIMEOUT: "15s" ON_FAIL: RESPOND "Hotel search is unavailable. Continuing with other results." ``` **3. Fan out with partial failure tolerance** Each delegate has its own `ON_FAIL` handler. If one supplier is unavailable, the agent proceeds with partial results from the others. ```yaml theme={null} DELEGATE: - AGENT: Price_Checker_A WHEN: comparison_mode == true PURPOSE: "Check price from supplier A" INPUT: product_id: sku quantity: qty RETURNS: price_a: supplier_a_price USE_RESULT: "Compare with other suppliers" TIMEOUT: "5s" ON_FAIL: RESPOND "Supplier A unavailable." - AGENT: Price_Checker_B WHEN: comparison_mode == true PURPOSE: "Check price from supplier B" INPUT: product_id: sku quantity: qty RETURNS: price_b: supplier_b_price USE_RESULT: "Compare with other suppliers" TIMEOUT: "5s" ON_FAIL: RESPOND "Supplier B unavailable." ``` ### Difference between handoff and delegate | Aspect | Handoff | Delegation | | - | - | - | | Control flow | Transfers the conversation permanently or temporarily to another agent | Always call-and-return. The parent agent always regains control after the sub-agent completes | | User awareness | The user may be aware that they are talking to a different agent | Sub-agent is never visible to the user | | Data flow | CONTEXT pass list and history strategy | Structured INPUT/RETURNS mapping with explicit variable mapping on both sides | | Use case | The user needs to interact with a different specialist agent | Parent agent needs a background task completed — such as a calculation or lookup — before continuing its own flow | ### Difference between delegate and fan-out The key distinction is parallelism and scope. Use delegation when you need a result from one sub-agent before the parent can continue. Use fan-out when you need results from multiple independent agents simultaneously — for example, searching flights and hotels at the same time. | Aspect | Delegation | Fan-out | | - | - | - | | Number of agents | One sub-agent | Multiple agents simultaneously | | Execution | Sequential - parent waits for the result | Parallel - all agents run at once | | Results | Single result returned | Multiple results aggregated | | Partial failure | Single `ON_FAIL` handler | Each agent has its own `ON_FAIL` handler. One agent failing does not stop the others. | | Use case | One background task | Multiple independent tasks | ### Context propagation between agents No data is automatically shared between agents. Every piece of data that a target agent needs must be explicitly passed using one of three mechanisms: session variables, conversation history, or persistent memory grants. | Data type | Handoff | Delegation | Fan-out | | - | - | - | - | | Session metadata | Forwarded (non-internal values) | Available via parent context | Available via parent context | | Conversation history | Configurable (auto default / summary\_only / full / `{ mode: last_n, count }` / none) | Not shared | Not shared | | Gather progress | Not transferred (each agent has its own) | Not transferred | Not transferred | | Custom variables (SET) | Transferred as metadata | Passed explicitly | Passed explicitly | | Workflow-scoped memory | Shared only through explicit memory\_grants | Not shared unless passed | Not shared unless passed | | Agent-specific state | Not transferred | Not transferred | Not transferred | **Memory-based Context Sharing** ```yaml expandable=true theme={null} MEMORY: session: - customer_id - current_intent - routing_history - conversation_summary - session_context TYPE: object DESCRIPTION: "Current session state snapshot passed to child agents on handoff" persistent: - user.name - user.language - user.preferred_agent - user.last_verified_at remember: - WHEN: user_name IS SET STORE: user_name -> user.name - WHEN: user_language IS SET STORE: user_language -> user.language recall: - ON: session:start ACTION: inject_context PATHS: [user.name, user.language, user.preferred_agent] ``` For more details on memory and access scopes, see the [guide on memory management across agents](/agent-platform/memory-and-state). ## Points to note ### Routing and handoff behavior 1. `HANDOFF` rules evaluate top-to-bottom. First match wins. Place specific conditions before general ones. 2. `HANDOFF` is for agent-to-agent routing only. Use `ESCALATE` when the target is a human operator or a queue. 3. `RETURN` defaults to false. Set `RETURN: true` explicitly when you need a call-and-return pattern. 4. The default history strategy is `auto`. Opt into `summary_only` for strict no-transcript transfer. ### Variables and data 1. Null or undefined variables are not passed at handoff time. Set all required variables before the handoff condition fires. 2. In a `DELEGATE` `INPUT` map, the left side is the sub-agent's parameter name and the right side is the parent's session variable. Reversing them causes empty input. 3. In fan-out, if two delegates write to the same `RETURNS` variable, one overwrites the other. Use unique names per delegate output. 4. Persistent memory requires explicit `memory_grants` in the `CONTEXT` block. Without it, the target agent cannot read the value. ### Reliability 1. Always set `TIMEOUT` on every `DELEGATE` block. Without it, a stalled sub-agent blocks the parent indefinitely. 2. Track `handoff_count` in session memory and escalate when it reaches 3 or 4 to prevent routing loops. ## Troubleshoot issues **Routing and Handoff** | Symptom | Fix | | - | - | | Routing goes to the wrong agent | `HANDOFF` rules evaluate top-to-bottom and first match wins. Move specific conditions above general ones. | | No rule matches the user message | Add a fallback rule at the bottom of the `HANDOFF` block that routes to a clarification agent or escalates to a human. | | Agent loops between supervisor and child | Set `RETURN: false` on terminal handoffs. Use `ON_RETURN` with a named handler when `RETURN: true` is used. | | Handoff creates a loop between two agents | Set clear `WHEN` conditions on both sides. Use RETURN: true on one side to establish a parent-child relationship. | | User is bouncing between agents | Track handoff\_count in session memory and add an `ESCALATE` trigger at 3 or 4 handoffs. | | User repeats information after handoff | Use `history: auto` or `history: full` in the `CONTEXT` block so the target agent has prior context. | **Context and Variables** | Symptom | Fix | | - | - | | Target agent does not receive context | Verify that all variables in the pass list are set before the handoff condition is triggered. Null or undefined variables are not passed. | | Too much context slows down the agent | Pass only the variables the target agent needs. Use `history: auto or summary_only` instead of `history: full` for long conversations. | | Persistent memory not accessible | Add an explicit `memory_grants` entry in the `CONTEXT` block. Without it, the target agent cannot read the value. | **Delegation and Fan-out** | Symptom | Fix | | - | - | | Delegate never returns | Set `TIMEOUT` on every `DELEGATE` block. Without it, the calling agent waits indefinitely if the sub-agent stalls. | | Delegate agent does not receive input | Verify the variable names on the right side of the `INPUT` map match existing session variables in the calling agent. | | Return values not mapped correctly | Verify keys in `RETURNS` match the output variable names from the delegate. Left side is the delegate's variable, right side is the parent's session variable. | | Parallel agents not executing concurrently | Verify that multiple `WHEN` conditions match simultaneously for the same user message. | | Results from one agent overwrite those of another | Use distinct variable names in `RETURNS` for each delegate. For example, supplier\_a\_price and supplier\_b\_price, not both price. | | Timeout on slow agents in fan-out | Set `TIMEOUT` on each delegate independently. Use `ON_FAIL` to handle agents that do not respond in time. | # Permissions reference Source: https://koreai.mintlify.app/agent-platform/permissions-reference [Back to role management](/agent-platform/administration/workspace-and-team#role-management) A permission is a single action a role may perform, and the platform identifies each one with a `resource:action` string. You build a custom role by selecting permissions, so this page tells you what each one grants before you grant it. The permission picker shows identifiers and nothing else. Read this page alongside it, because an identifier names the resource but rarely warns you about the consequence. ## Permission groups * **[Agents](#agents)**: Creating, editing, running, and deleting agent definitions. * **[Tools](#tools)**: Authoring the tools agents call, and invoking them directly. * **[Workflows](#workflows)**: Authoring multi-step workflows, plus running published and draft ones. * **[Versions](#versions)**: The version lifecycle from creation through review, approval, and publication. * **[Prompt library](#prompt-library)**: Authoring, testing, and promoting shared prompts. * **[Modules](#modules)**: Building modules, publishing them to the catalog, and importing them. * **[Namespaces](#namespaces)**: The namespaces that organize project resources. * **[External agents](#external-agents)**: Registering agents that run outside the platform. * **[Field intelligence](#field-intelligence)**: Field intelligence configuration and the data behind it. * **[Knowledge base](#knowledge-base)**: Knowledge content, and who can reach it. * **[Agent tables](#agent-tables)**: Structured tables agents read and write, including sensitive columns. * **[Lookup data](#lookup-data)**: Reference data for field validation and fuzzy matching. * **[Attachments](#attachments)**: Files that move through conversations. * **[Survey templates](#survey-templates)**: Feedback surveys agents present to users. * **[Sessions](#sessions)**: Live conversation sessions, including sending into one. * **[Recordings](#recordings)**: Reaching recordings, changing recording settings, and the restricted immutability grant. * **[Human tasks](#human-tasks)**: Work items routed to people, and who assigns or claims them. * **[Approvals](#approvals)**: Approval requests agents and workflows raise. * **[Outbound calls](#outbound-calls)**: Calls the platform places rather than receives. * **[Email operations](#email-operations)**: Email channel operations, recovery, and remediation. * **[HTTP async](#http-async)**: Asynchronous HTTP operations. * **[Voice playground](#voice-playground)**: Live voice testing and live session debugging. * **[Solution Workbench](#solution-workbench)**: Workbench configuration and the release path. * **[Deployments](#deployments)**: Creating and retiring deployments. * **[Environments](#environments)**: Environment configuration and deploying into one. * **[Environment variables](#environment-variables)**: The runtime values agents resolve. * **[Channels](#channels)**: Channel definitions. * **[Channel connections](#channel-connections)**: Live connections to external messaging platforms. * **[Config](#config)**: General project configuration. * **[Runtime config](#runtime-config)**: Project-wide runtime behavior settings. * **[Model config](#model-config)**: Model selection and feature routing. * **[Auth profiles](#auth-profiles)**: Stored credentials, and the separate grant that decrypts them. * **[Credentials](#credentials)**: Credential records, including rotation and assignment. * **[Connections](#connections)**: Connections to external systems. * **[Proxy](#proxy)**: Proxy configuration for outbound traffic. * **[External secret sources](#external-secret-sources)**: Connections to external secret managers. * **[Platform keys and secrets](#platform-keys-and-secrets)**: Read access to platform-level keys and secrets. * **[Guardrails and PII](#guardrails-and-pii)**: Guardrails on agent output, and PII detection patterns. * **[Privacy](#privacy)**: Grants that reverse redaction and expose withheld data. * **[KMS](#kms)**: Encryption key management. * **[Analytics and simulate](#analytics-and-simulate)**: Analytics access and simulation runs. * **[Evals](#evals)**: Authoring and running evaluations. * **[Experiments](#experiments)**: Experiment configuration and results. * **[Optimization and auto-loop](#optimization-and-auto-loop)**: Optimization runs, and applying their recommendations. * **[Workspace](#workspace)**: Workspace settings, membership, and billing. * **[Governance](#governance)**: Governance policy, the audit trail, and the qualification gate. ## How to read this reference The platform groups permissions by resource, and the groups follow the picker's categories so you can follow the on-screen permissions. Selecting a group header selects every permission inside it. Make sure that you only provide the necessary and required permissions when granting group-level permissions. Two behaviors affect what you can actually select. * **Grant scope:** Your can grant permissions as per your role. The picker may display some permissions that it doesn't let you select in case these permissions are outside your own role's scope. * **Restricted permissions:** Only a workspace owner or a higher role can grant a few permissions. It is possible that some permissions can't be selected even by an admin. ## Build agents These permissions cover authoring work: the agents themselves, the tools and workflows they call, the prompts behind them, and the version history that tracks every change. ### Agents These permissions govern agent definitions. Reading an agent and running one are separate grants, so a role can exercise an agent without seeing how you built it. | Permission | Description | | - | - | | `agent:read` | Lets the role open agent definitions and configuration. | | `agent:create` | Lets the role add new agents to the project. | | `agent:update` | Lets the role change existing agent definitions. | | `agent:delete` | Lets the role remove agents. | | `agent:execute` | Lets the role run an agent. Grant it to testers who need to exercise behavior without editing it. | ### Tools These permissions govern the tools agents call. Execution stays separate from authoring here too, because a tool that reaches an external system acts on real data whoever triggers it. | Permission | Description | | - | - | | `tool:read` | Lets the role open tool definitions, including endpoints and auth bindings. | | `tool:write` | Lets the role create and change tools. | | `tool:delete` | Lets the role remove tools. | | `tool:execute` | Lets the role invoke a tool directly. | ### Workflows These permissions govern multi-step workflows. Draft invocation sits apart from ordinary execution, so you can let someone test an unreleased workflow without letting them run published ones. | Permission | Description | | - | - | | `workflow:read` | Lets the role open workflow definitions. | | `workflow:write` | Lets the role change workflow definitions. | | `workflow:create` | Lets the role add new workflows. | | `workflow:update` | Lets the role update existing workflows. | | `workflow:delete` | Lets the role remove workflows. | | `workflow:execute` | Lets the role run a published workflow. | | `workflow:draft_invoke` | Lets the role run a workflow before anyone publishes it, which is what testers need ahead of a release. | ### Versions These permissions govern the version lifecycle, from cutting a version through review, approval, and publication. Each stage carries its own permission so that different people can author and approve, which is the point of having a review stage at all. | Permission | Description | | - | - | | `version:read` | Lets the role open version history and version contents. | | `version:create` | Lets the role cut a new version from current work. | | `version:update` | Lets the role change a version that nobody has published yet. | | `version:delete` | Lets the role remove a version. | | `version:publish` | Lets the role publish a version, which makes it available to deploy. | | `version:qualify` | Lets the role mark a version as qualified, recording that it met the checks your process requires. | | `version:review` | Lets the role review a version and record the outcome. | | `version:approve` | Lets the role approve a version. Keep it separate from `version:create` where your process needs a second pair of eyes. | | `version:archive` | Lets the role archive a version, which takes it out of active use but keeps it. | ### Prompt library These permissions govern shared prompts. Promotion sits apart from editing, so prompt authors can iterate freely while a smaller group decides what becomes canonical. | Permission | Description | | - | - | | `prompt:create` | Lets the role add prompts to the library. | | `prompt:read` | Lets the role open prompts and their versions. | | `prompt:update` | Lets the role change existing prompts. | | `prompt:delete` | Lets the role remove prompts. | | `prompt:test` | Lets the role run a prompt to judge its output. | | `prompt:promote` | Lets the role promote a prompt version to the one other work uses. | ### Modules These permissions govern reusable modules, both the ones this workspace publishes and the ones it imports. Publishing exposes your work to other projects, so scrutinize that grant hardest. | Permission | Description | | - | - | | `module:read` | Lets the role open modules and their contents. | | `module:manage` | Lets the role create and change modules. | | `module:publish` | Lets the role publish a module to the catalog, where other projects can adopt it and inherit later versions. | | `module:import` | Lets the role import a module into this project. | ### Namespaces These permissions govern the namespaces that organize project resources. | Permission | Description | | - | - | | `namespace:read` | Lets the role open namespaces. | | `namespace:create` | Lets the role add namespaces. | | `namespace:update` | Lets the role rename or reconfigure namespaces. | | `namespace:delete` | Lets the role remove namespaces. | ### External agents These permissions govern the agents that run outside the platform and take delegated work from this project. | Permission | Description | | - | - | | `external_agent:create` | Lets the role register an external agent. | | `external_agent:read` | Lets the role open external agent registrations. | | `external_agent:update` | Lets the role change an external agent registration. | | `external_agent:delete` | Lets the role remove an external agent registration. | ### Field intelligence These permissions govern field intelligence configuration and the data it draws on. | Permission | Description | | - | - | | `field_intelligence:read` | Lets the role open field intelligence configuration. | | `field_intelligence:preview` | Lets the role preview results without applying them. | | `field_intelligence:write` | Lets the role change field intelligence configuration. | | `field_intelligence:refresh` | Lets the role refresh the underlying data. | | `field_intelligence:import` | Lets the role import field intelligence definitions. | ## Knowledge and data These permissions cover the content agents answer from and the structured data they read and write. ### Knowledge base These permissions govern knowledge content and who can reach it. Writing documents and writing permissions stay separate, so a content author doesn't also control access. | Permission | Description | | - | - | | `knowledge_base:read` | Lets the role open knowledge bases and their contents. | | `document:write` | Lets the role add and change documents in a knowledge base. | | `permission:write` | Lets the role change who can reach knowledge base content. | ### Agent tables These permissions govern the structured tables agents read and write. Hold back the reveal permission, because it exposes values the table otherwise masks. | Permission | Description | | - | - | | `table:read` | Lets the role open table structure and contents that carry no sensitive marking. | | `table:write` | Lets the role add and change table records. | | `table:delete` | Lets the role remove tables or records. | | `table:sensitive:reveal` | Lets the role see values in columns marked sensitive. Grant it only where someone's job requires the underlying data rather than a masked view of it. | ### Lookup data These permissions govern the reference data behind field validation and fuzzy matching. | Permission | Description | | - | - | | `lookup_data:read` | Lets the role open lookup tables and their values. | | `lookup_data:write` | Lets the role create and change lookup data. | | `lookup_data:delete` | Lets the role remove lookup tables. | ### Attachments These permissions govern the files that move through conversations. | Permission | Description | | - | - | | `attachment:read` | Lets the role open and download attachments. | | `attachment:write` | Lets the role upload attachments. | | `attachment:delete` | Lets the role remove attachments. | ### Survey templates These permissions govern the feedback surveys agents present to users. | Permission | Description | | - | - | | `feedback-survey:create` | Lets the role add survey templates. | | `feedback-survey:read` | Lets the role open survey templates. | | `feedback-survey:update` | Lets the role change survey templates. | | `feedback-survey:delete` | Lets the role remove survey templates. | | `feedback-survey:write` | Lets the role change survey content. | ## Conversations and operations These permissions govern live conversations and the operational work around them. They reach production traffic, so they carry more day-to-day risk than the authoring grants above. ### Sessions These permissions govern conversation sessions. Sending a message into a live session means speaking as the agent in front of a real user, so treat it as an operational grant rather than a diagnostic one. | Permission | Description | | - | - | | `session:read` | Lets the role open session records and transcripts. | | `session:create` | Lets the role start a session. | | `session:write` | Lets the role change session data. | | `session:close` | Lets the role end an active session. | | `session:execute` | Lets the role run agent turns inside a session. | | `session:delete` | Lets the role remove session records. | | `session:send_message` | Lets the role put a message into a live session. | ### Recordings The platform generates recordings automatically during a call, so none of these permissions creates one. They govern who reaches a recording, who changes the recording settings, and who places a recording beyond deletion. Where an action lives matters as much as who holds the permission. The **Sessions** page offers playback and download only. Deletion and immutability live on the **Recordings** page, so a role holding `recording:delete` still finds no delete control on Sessions. | Permission | Description | | - | - | | `recording:read` | Lets the role play and download recordings. Two conditions have to hold: the project must have recording enabled, and the role must carry this permission. A role holding it still reaches nothing while recording stays off for the project. | | `recording:write` | Lets the role change recording configuration, covering whether recording runs, how long the platform keeps recordings, and the remaining recording settings. It controls configuration rather than creation, because the platform already creates recordings on its own. | | `recording:delete` | Lets the role delete recordings from the Recordings page. | | `recording:mark_immutable` | Lets the role mark a recording immutable, which puts it beyond deletion for retention or legal hold. The platform restricts this grant, so an admin building a custom role can't select it. It comes from the workspace owner or arrives with the highest-level project role. | ### Human tasks These permissions govern the work items the platform routes to people. Assigning and claiming differ: you assign work to someone else, and you claim work for yourself. | Permission | Description | | - | - | | `human_task:read` | Lets the role open human tasks and see their state. | | `human_task:assign` | Lets the role assign a task to someone else. | | `human_task:claim` | Lets the role take an unassigned task. | | `human_task:resolve` | Lets the role complete a task and record the outcome. | ### Approvals These permissions govern the approval requests that agents and workflows raise. | Permission | Description | | - | - | | `approval:read` | Lets the role open approval requests and see their status. | | `approval:write` | Lets the role record an approval decision. | ### Outbound calls These permissions govern the calls the platform places rather than receives. | Permission | Description | | - | - | | `outbound_call:initiate` | Lets the role place an outbound call. The call reaches a real person, so this belongs with operational roles rather than build roles. | | `outbound_call:read` | Lets the role open outbound call records. | ### Email operations These permissions govern email channel operations, including recovery and remediation. Several of them act on messages already in flight, which is why they sit apart from ordinary channel permissions. | Permission | Description | | - | - | | `email:proactive_send` | Lets the role send email that the agent starts rather than sends in reply. | | `email:diagnose` | Lets the role investigate delivery problems. | | `email:replay` | Lets the role push a message through the pipeline again. | | `email:delivery_recovery` | Lets the role retry failed deliveries. | | `email:quarantine_release` | Lets the role release a quarantined message, which overrides the check that held it. | | `email:message_erase` | Lets the role erase a message permanently. Nothing recovers it afterward, so keep this grant narrow. | | `email:health_read` | Lets the role open channel health metrics. | | `email:alert_policy_update` | Lets the role change the policies that trigger email alerts. | | `email_exchange:delta_reset` | Lets the role reset the Exchange delta sync state, which forces a full resynchronization. | | `email_exchange:subscription_cleanup` | Lets the role clear stale Exchange subscriptions. | ### HTTP async These permissions govern asynchronous HTTP operations. | Permission | Description | | - | - | | `http_async:read` | Lets the role open asynchronous request state. | | `http_async:manage` | Lets the role configure asynchronous request handling. | | `http_async:send_message` | Lets the role send a message through an asynchronous channel. | ### Voice playground These permissions govern live voice testing. Live debugging exposes conversation content as it happens, which puts it closer to a privacy grant than a testing one. | Permission | Description | | - | - | | `playground:use` | Lets the role run voice conversations in the playground. | | `session:debug:live` | Lets the role inspect a live session while it runs. | ## Deployment and configuration These permissions govern what reaches each environment and how the runtime behaves once it arrives. ### Solution Workbench These permissions govern the workbench and its release path. The release permission works with the release environment scoping described above, so grant both together and scope them deliberately. | Permission | Description | | - | - | | `workbench:read` | Lets the role open workbench configurations. | | `workbench:write` | Lets the role change workbench configurations. | | `workbench:configure` | Lets the role change workbench settings. | | `workbench:integration_test` | Lets the role run integration tests. | | `workbench:release` | Lets the role release configurations, limited to the environments the role's scoping allows. | | `workbench:restore` | Lets the role restore a previous configuration. | ### Deployments These permissions govern deployments. Retiring one takes an agent out of service, so it reaches live traffic. | Permission | Description | | - | - | | `deployment:read` | Lets the role open deployments and see their status. | | `deployment:create` | Lets the role create a deployment. | | `deployment:retire` | Lets the role retire a running deployment. | ### Environments These permissions govern deployment environments. | Permission | Description | | - | - | | `environment:read` | Lets the role open environment configuration. | | `environment:deploy` | Lets the role deploy into an environment. | ### Environment variables These permissions govern the runtime values agents resolve. Agents resolve variables at runtime, so a change here reaches new sessions without a redeploy. | Permission | Description | | - | - | | `env_var:read` | Lets the role open variable keys and any value the project doesn't mark secret. | | `env_var:create` | Lets the role add variables. | | `env_var:update` | Lets the role change variable values. | | `env_var:delete` | Lets the role remove variables. Anything still referencing a removed variable hits a runtime resolution error. | ### Channels These permissions govern channel definitions. | Permission | Description | | - | - | | `channel:read` | Lets the role open channel configuration. | | `channel:create` | Lets the role add a channel. | | `channel:update` | Lets the role change channel configuration. | | `channel:delete` | Lets the role remove a channel. | ### Channel connections These permissions govern the live connections between channels and external messaging platforms. | Permission | Description | | - | - | | `channel_connection:read` | Lets the role open connection configuration and status. | | `channel_connection:create` | Lets the role establish a connection. | | `channel_connection:update` | Lets the role change a connection. | | `channel_connection:delete` | Lets the role remove a connection, which stops traffic on that channel. | ### Config These permissions govern general project configuration. | Permission | Description | | - | - | | `config:read` | Lets the role open configuration values. | | `config:write` | Lets the role change configuration values. | ### Runtime config These permissions govern runtime behavior settings, which apply project-wide to every deployment. | Permission | Description | | - | - | | `runtime_config:read` | Lets the role open runtime configuration. | | `runtime_config:write` | Lets the role change runtime configuration. One change here reaches every agent in the project at once. | ### Model config These permissions govern which models the project uses and how features route to them. | Permission | Description | | - | - | | `model_config:read` | Lets the role open model selection and feature routing. | | `model_config:write` | Lets the role change model selection, tier bindings, and feature overrides, which moves both cost and output quality. | ## Access, security, and privacy These permissions govern credentials, encryption, and access to protected data. Grant them most sparingly, because several expose material that other controls exist to hide. ### Auth profiles These permissions govern stored credentials. Decryption stands apart from every other action here, because reading a profile's configuration differs from reading its secret. | Permission | Description | | - | - | | `auth-profile:read` | Lets the role open profile configuration. Secrets stay masked. | | `auth-profile:create` | Lets the role create auth profiles. | | `auth-profile:write` | Lets the role change auth profiles. | | `auth-profile:delete` | Lets the role remove auth profiles. | | `auth-profile:decrypt` | Lets the role see the stored secret. This grant turns credential management into credential access. | ### Credentials These permissions govern credential records. | Permission | Description | | - | - | | `credential:read` | Lets the role open credential metadata. | | `credential:write` | Lets the role create and change credentials. | | `credential:delete` | Lets the role remove credentials. | | `credential:manage` | Lets the role administer credentials, including rotation and assignment. | ### Connections These permissions govern connections to external systems. | Permission | Description | | - | - | | `connection:read` | Lets the role open connection configuration. | | `connection:write` | Lets the role create and change connections. | | `connection:delete` | Lets the role remove connections. | ### Proxy These permissions govern proxy configuration for outbound traffic. | Permission | Description | | - | - | | `proxy:read` | Lets the role open proxy configuration. | | `proxy:write` | Lets the role change proxy configuration. | | `proxy:delete` | Lets the role remove proxy configuration. | ### External secret sources These permissions govern connections to external secret managers. | Permission | Description | | - | - | | `secret-source:read` | Lets the role open external secret source configuration. | | `secret-source:write` | Lets the role configure an external secret source. | | `secret-source:delete` | Lets the role remove an external secret source, which breaks anything that resolves secrets through it. | ### Platform keys and secrets These permissions give read access to platform-level keys and secrets. Both stay read-only, because you create and rotate these through the pages that own them. | Permission | Description | | - | - | | `api_key:read` | Lets the role open API key metadata. | | `secret:read` | Lets the role open stored secrets. | ### Guardrails and PII These permissions govern the guardrails that constrain agent output and the patterns that detect personal data. | Permission | Description | | - | - | | `guardrail:read` | Lets the role open guardrail configuration. | | `guardrail:write` | Lets the role change guardrails, which changes what the platform blocks in production. | | `pii-pattern:read` | Lets the role open PII detection patterns. | | `pii-pattern:write` | Lets the role change PII patterns. Weakening one pattern cuts redaction everywhere that pattern applies. | ### Privacy These permissions open data that the platform redacts or withholds by default. Every one of them reverses a protection, so grant them individually and only for a stated purpose. | Permission | Description | | - | - | | `pii:reveal` | Lets the role see personal data that redaction would otherwise mask. | | `sip-ladder:reveal` | Lets the role see SIP signalling detail for voice calls. | | `session:debug:payload:read` | Lets the role read raw session payloads, including content that ordinary session views leave out. | | `provider-payload:read` | Lets the role read the raw payloads the platform exchanges with model providers. | ### KMS These permissions govern encryption key management. | Permission | Description | | - | - | | `kms:admin` | Lets the role administer key management, including provider selection, rotation policy, and failure policy. | ## Analysis and quality These permissions cover measurement and improvement work: reading analytics, running evaluations, and applying optimizations. ### Analytics and simulate These permissions govern analytics access and simulation runs. | Permission | Description | | - | - | | `analytics:read` | Lets the role open analytics data and dashboards. | | `simulate:execute` | Lets the role run simulations on agents. | ### Evals These permissions govern evaluations. Authoring an evaluation and running one stay separate, so a tester can run a suite without changing what it measures. | Permission | Description | | - | - | | `eval:read` | Lets the role open evaluation definitions and results. | | `eval:write` | Lets the role create and change evaluations. | | `eval:run` | Lets the role run evaluations. | ### Experiments These permissions govern experiments. | Permission | Description | | - | - | | `experiment:read` | Lets the role open experiment configuration and results. | | `experiment:write` | Lets the role create and change experiments. | ### Optimization and auto-loop These permissions govern automated optimization. Applying an optimization changes agent behavior, which is why it sits apart from running one. | Permission | Description | | - | - | | `optimization:read` | Lets the role open optimization runs and their recommendations. | | `optimization:run` | Lets the role start an optimization run. | | `optimization:apply` | Lets the role apply an optimization's recommendations to the agent. | ## Workspace and governance These permissions cover the workspace itself and the controls that bound what everyone else can do. ### Workspace These permissions govern workspace settings, membership, and billing. | Permission | Description | | - | - | | `tenant:read` | Lets the role open workspace configuration. | | `tenant:update` | Lets the role change workspace details. | | `tenant:manage_settings` | Lets the role change workspace-level settings. | | `tenant:manage_members` | Lets the role add and remove workspace members and change their roles. | | `billing:read` | Lets the role open billing and usage information. | | `billing:write` | Lets the role change billing configuration. | ### Governance These permissions govern governance policy and the audit trail. Weigh the qualification gate bypass separately from everything else here, because it removes a control rather than configuring one. | Permission | Description | | - | - | | `governance:write` | Lets the role change governance configuration. | | `governance:audit-read` | Lets the role read the governance audit trail. Avoid pairing it with an operational role, so the person reviewing activity isn't the person performing it. | | `production:bypass_qualification_gate` | Lets the role release to production without the qualification the gate demands. It exists for genuine emergencies, and any role that holds it permanently defeats the gate. | | `approval_policy:write` | Lets the role change the policies that decide what needs approval, which hands it control over the approval requirement itself. | # PII protection Source: https://koreai.mintlify.app/agent-platform/pii-protection PII Protection enables you to detect, redact, and control access to personally identifiable information (PII) across your project. You can configure built-in or custom patterns to identify sensitive information, define how detected values are rendered, and configure consumer access controls for the project. To configure PII protection: 1. Go to **Govern** > **PII**. 2. Enable **PII Detection**. 3. Configure the [PII protection settings](#pii-protection-settings). 4. Enable and configure [built-in patterns](#built-in-patterns) or create [custom patterns](#custom-patterns). *** ## PII protection settings The following project-level settings control PII detection behavior. | Setting | Description | | - | - | | **PII Detection** | Enables configurable PII detection for the project. When disabled, configured patterns do not run. Credential and secret scrubbing remains enabled. | | **Output Redaction** | Redacts detected PII in agent responses before they are returned to users. | | **Non-secret Field Allow-list** | Specifies output fields that bypass credential and secret scrubbing. Use this only for fields that are verified to be safe to expose. | | **Confidence Threshold** | Specifies the minimum confidence score required for model-based detection. Lower values detect more entities but may increase false positives. | | **Model Detection Time Budget** | Specifies the maximum time allowed for model-based detectors to complete. Increasing the value may improve detection accuracy but can increase processing latency. | Credential and secret scrubbing for API keys, access tokens, passwords, and similar credentials is always enabled and cannot be disabled. *** ## Custom dimensions Custom dimensions used in session analytics can be configured to allow raw PII values to be stored. By default, PII protection rejects detected PII in custom dimensions. You can configure the default behavior for custom dimensions to either: | Behavior | Description | | - | - | | **Protect PII** | Rejects detected PII before it is stored. | | **Allow raw PII** | Stores custom dimension values as provided, including detected PII. | You can also define exceptions for individual custom dimensions when their behavior should differ from the default policy. > **Note:** Allowing raw PII is an opt-in configuration. Use it only when your use case requires the original PII value to be stored. *** ## Built-in patterns Built-in patterns use platform-managed detection logic for commonly used PII types, including Email Address, Phone Number, Social Security Number, Credit Card Number, and Date of Birth. Each pattern can be enabled individually and configured with its own context anchoring, redaction strategies, consumer access settings, and live test configuration. | Section | Description | | - | - | | **Basics** | Enable or disable the pattern. The pattern name and description are read-only. | | **Context Anchoring** | Improves detection accuracy by evaluating context words near a potential match. See [Context Anchoring](#context-anchoring). | | **Redaction Strategies** | Determines how detected PII is rendered before it is shared with a consumer. See [Redaction Strategies](#redaction-strategies). | | **Consumer Access** | Controls how detected PII is rendered for different consumers. See [Consumer Access](#consumer-access). | | **Live Test** | Validates the pattern before saving. Enter sample text and select **Test Pattern** to verify detection and preview how detected PII is rendered for each configured consumer. | ## Custom patterns In addition to built-in patterns, you can create custom patterns to identify organization-specific or domain-specific PII. Select **Add Pattern** to create a custom pattern and configure the following settings. | Section | Description | | - | - | | **Basics** | Specify the pattern **Name**, **Description**, and enable or disable the pattern. | | **Regex-based Detection** | Identifies PII using regular expressions. Supports:

• **Built-in Library** – Uses a platform-managed regular expression from the built-in pattern libr/> •.
• **Custom Regex** – Uses a user-defined RE2-compatible regular expression.

When using Custom Regex, you can optionally configure a **Validation Expression** to validate detected matches. | | **Entity-based Detection** | Identifies PII using entity recognition. Supports:

• **Inbuilt Entity (KoreML)** – Uses the platform's built-in ML entity extraction capability for supported entity ty/> •.
• **Model-based** – Uses a selected model to identify custom entity types.
  • **Model** – Specifies the model used for entity detection.
  • **Detection Prompt** – Describes the entity that the model should identify and any contextual rules for detection.
  • **Ignore values** – Specifies values that should not be treated as the configured entity, even when detected. Use this to exclude known values and reduce false positives.

Entity-based detection complements regex-based detection and does not replace existing regex-based patterns. | | **Context Anchoring** | Improves detection accuracy by evaluating context words near a potential match. For more information, see [Context Anchoring](#context-anchoring). | | **Redaction Strategies** | Determines how detected PII is rendered before it is shared with a consumer. For more information, see [Redaction Strategies](#redaction-strategies). | | **Consumer Access** | Controls how detected PII is rendered for different consumers. For more information, see [Consumer Access](#consumer-access). | | **Live Test** | Validates the pattern before saving. Enter sample text and select **Test Pattern** to verify detection and preview how detected PII is rendered for each configured consumer. | Model-based detection sends the text being evaluated to the configured external model. Review your organization's data handling requirements before enabling this detection method. Custom regex replacement labels support capture-group backreferences using `$1` or `\1`. Use `$$` to insert a literal dollar sign `$`. For example: * Pattern: `(John)\s(Doe)` * Replacement Label: `Mr. $1*** $2***` * Result: `Mr. J*** D***` *** ## Context anchoring Context anchoring improves detection accuracy by evaluating context words that appear near a potential match. | Setting | Description | | - | - | | **Context Words** | Comma-separated keywords associated with the pattern, such as *ssn*, *social security*, or *tax id*. Detection confidence increases when these words appear near a potential match. | | **Base Confidence** | Specifies the confidence score assigned when the pattern matches without additional context. | | **Context Boost** | Specifies the additional confidence applied when the configured context words are found near the match. | | **Context Window** | Specifies the distance from the detected value within which context words are evaluated. | *** ## Redaction strategies Redaction strategies determine how detected PII is rendered before it is shared with a consumer. | Strategy | Description | | - | - | | **Predefined Label** | Replaces the detected value with a configured label such as `[REDACTED]`. | | **Masked** | Partially obscures the detected value while preserving a configurable number of leading and trailing characters. | | **Random Replacement** | Replaces the detected value with a synthetic value using the selected character set. | Only enabled strategies are available when configuring render modes. *** ## Consumer access Consumer Access controls how detected PII is rendered for different consumers. | Setting | Description | | - | - | | **Default Render Mode** | Specifies the render mode applied to all consumers unless overridden.

Available render modes include:

• **Original** – Passes the original detected value to the consu/> •.
• **Redacted** – Replaces the detected value with the configured prede/> •ed la/> •.
• **Masked** – Applies the configur/> •maski/> •setti/> •.
• **Random Replacement** – Replaces the detected value with a /> •erate/> •eplacement value.
• **Tokenized** – Replaces the detected value with a stable token.

Render modes that depend on a disabled redaction strategy are unavailable for selection. | | **Per-consumer Overrides** | Overrides the default render mode for selected consumers, such as **Users**, **Tool Input**, **Tool Output**, and **Logs**. | Large language model (LLM) consumers never receive plaintext PII. If Original is selected, the platform automatically replaces the detected value with a stable token before it is sent to the model. # Create and manage project Source: https://koreai.mintlify.app/agent-platform/projects Create, configure, and manage Projects in Studio. Projects are the top-level container for agents, tools, knowledge bases, workflows, and deployment configuration on Agent Platform. ## About Studio Studio is the primary web interface for building, testing, deploying, and monitoring AI agents on the Agent Platform. It provides a unified workspace where teams can manage every phase of the agent lifecycle, from initial design through production operations. When you sign in, Studio displays the Projects dashboard - a grid of project cards showing all projects in your workspace. Each card displays the project name, description, orchestration pattern badge, agent count, and last-updated timestamp. From the dashboard, you can: * Search projects using the search bar at the top of the page. * Create a new project using the "New Project" option. * Open a project by clicking any project card, which navigates into that project's workspace. * View projects using the available categories: Active, My Projects, Shared with Me, Project Access, Pinned, and Archived. ## What's a project A project is the top-level container in Studio. It groups agents, supervisors, tools, knowledge bases, workflows, integrations, and configuration into a single workspace, and it defines the isolation boundary for deployments, access control, and analytics. Every agent you build belongs to exactly one project. A project holds seven categories of resources and their associated settings. | Category | Contents | | - | - | | **Build** | Agents, workflows, and the project overview. | | **Resources** | HTTP tools, code sandbox tools, MCP servers, knowledge bases, integrations. | | **Evaluate** | Evaluations and experiments. | | **Operate** | Sessions, deployments, inbox, alerts, transfer sessions. | | **Insights** | Dashboards, analytics, agent performance, quality monitoring, customer insights, feedback, voice analytics, and pipelines. | | **Govern** | Guardrails and governance policies. | | **Settings** | Members, API keys, models, config variables, Git, runtime config, and more. | Because the project is the boundary for access control and deployment, resources defined in one project aren't visible in another. Moving assets between projects requires the [export and import](#exporting-and-importing-projects) flow. ## Before you begin Before creating or managing projects, confirm the following. * **Project access.** You must be signed in to Studio with a user account that belongs to the target workspace. * **Role.** Creating projects, managing members, and deleting projects require administrator permissions on the workspace. Developers without admin rights can still build inside a project they have been added to. * **LLM provider (optional).** To use AI-guided project creation, a workspace administrator must configure an LLM provider under **Admin > Models**. If the provider is not configured, the AI Architect panel shows an in-panel banner instead of the wizard. ## Accessing the projects dashboard When you sign in, Studio opens the **Projects dashboard**. This is the entry point for all project management operations. The dashboard displays a grid of project cards. Each card shows the project name, description, orchestration pattern badge, agent count, and last-updated timestamp. From the dashboard you can: * Search existing projects using the search bar in the header. * Create a new project from the **New Project** dropdown. * Open a project by clicking its card, which loads the project workspace. * Access the three-dot menu on any card to delete that project. To return to the dashboard from anywhere in Studio, click the product logo in the top-left corner or follow the breadcrumb trail back to **Projects**. ## Create a project Studio supports three creation methods. Choose the one that fits your starting point. | Method | When to Use It | | - | - | | [**AI-guided (Architect)**](#method-1-ai-guided-creation) | You are new to the platform, or you want the AI Architect to scaffold agents from a described use case. | | [**Blank project**](#method-2-blank-project) | You know exactly which orchestration pattern and agents you need, and you want full manual control. | | [**From template**](#method-3-from-template) | A predefined template matches your use case (for example, customer service or IT help desk). | ### Method 1: AI-guided creation Use this method when you want the AI Architect to scaffold agents, tools, and project structure from a described use case. It is the recommended starting point for new users. **To create a project with the AI Architect:** 1. On the Projects dashboard, click **New Project > Start with AI Architect**. 2. Read the **Welcome** screen and click **Next** to begin the interview. 3. In the **Interview** phase, answer the Architect's questions about your use case, target audience, channels, and success criteria. 4. In the **Upload** phase, optionally attach documents that describe your domain — for example, knowledge base content, API specifications, or existing agent scripts. The Architect uses these to inform agent and tool design. 5. Wait for the **Generating** phase to complete. The Architect produces agent definitions, tool configurations, and an initial project structure. 6. In the **Reveal** phase, inspect the generated structure and adjust agent roles, names, or connections as needed. 7. In the **Review** phase, confirm that the configuration is correct. 8. Click **Create**. Studio provisions the project and opens its workspace. The richer the context you provide during the interview and upload phases, the more accurate the generated agents will be. Upload real documentation rather than summaries where possible. ### Method 2: Blank project Use this method when you want manual control over every aspect of the project. **To create a blank project:** 1. On the Projects dashboard, click **New Project > Blank Project**. 2. In the creation dialog, enter the following fields. | Field | Required | Description | | - | - | - | | **Name** | Yes | A descriptive project name. Appears on the dashboard card and in breadcrumbs. | | **Description** | No | A short summary of the project's purpose. | | **Orchestration pattern** | Yes | The coordination model for agents in this project. See the table below. | 3. Select an orchestration pattern based on your use case. Refer to [Choosing an orchestration pattern](#choosing-an-orchestration-pattern). 4. Click **Create**. Studio opens the project workspace with an empty overview. ### Method 3: From template Use this method to start from a predefined project that already contains agents and resources for a common use case. **To create a project from a template:** 1. On the Projects dashboard, click **New Project > From Template**. 2. Browse the template catalog and select a template that matches your use case. 3. Review the included agents, tools, and orchestration pattern. 4. Provide a name and optional description. 5. Click **Create**. Studio clones the template into your workspace as a new project. Templates come pre-configured with the orchestration pattern and agent roles appropriate for the use case, so you typically only need to edit prompts, tool credentials, and knowledge sources before testing. ### Choose an orchestration pattern The orchestration pattern determines how agents coordinate with each other. It is set at project creation time and pre-configures agent roles and coordination blocks for new agents. | Pattern | How It Works | Best For | | - | - | - | | **Concierge** (recommended) | A single front-facing agent handles the user and delegates to specialist agents behind the scenes. | Customer service, help desk, general-purpose assistants. | | **Router** | A triage agent classifies the request and transfers the user directly to the appropriate specialist. | Multi-department support, intent-based routing. | | **Tiered** | A triage agent routes to L1 agents, which can escalate to L2 agents. Supports de-escalation back to a lower tier. | Technical support with defined escalation paths. | | **Custom** | No predefined structure. Agents can be connected in any topology. | Advanced use cases requiring flexible orchestration. | Choosing a pattern does not lock you in. It only pre-configures coordination defaults. You can still add, remove, or reconnect agents within a project at any time. ## Navigate a project workspace Clicking a project card opens the project workspace. The workspace has three persistent elements: the header bar, the project sidebar, and the main content area. ### Project overview page The first page you see inside a project is the **Overview**. It adapts to the maturity of your project. | Project State | What You See on the Overview | | - | - | | **Empty** (no agents) | A get-started prompt guiding you to create your first agent or import existing agents. | | **Building** (agents, no deployments) | Metric cards for agent count, session count, and deploy ratio; an agent list; and a resource summary panel. | | **Live** (active deployments) | Full metrics for sessions, messages, tokens, cost, and deployments; active deployment list; activity timeline; and quick action cards. | The Overview also exposes the **Import** and **Export** buttons for moving agents between projects. See [Exporting and importing projects](#exporting-and-importing-projects). ### Find the project ID Every project has a unique project ID. Studio doesn't display the project ID as a field on any page. The ID appears in the browser address bar whenever you have the project open. You need the project ID when you do the following: * Call the Agent Platform public APIs, which take the project ID as the `projectId` path parameter. The API key you use must be bound to the same project. * Configure the Artemis CLI or the MCP server against a specific project. * Raise a support request about a specific project. **To find the project ID:** 1. On the Projects dashboard, select the card of the project you want. Studio opens the project workspace. 2. In the browser address bar, locate the 36-character identifier in the URL. The identifier uses the UUID format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`. ```text theme={null} https://///overview ``` 3. Select the identifier and copy it. The host name and the surrounding path segments differ between environments, but the project ID is always the UUID segment. The project ID stays the same for the life of the project. If you delete a project and recreate it, the new project receives a different ID. The [project switcher](#project-switcher) changes the project ID in the URL. After you switch projects, confirm that the project name in the header matches the project you intend to query before you copy the ID. ### Project sidebar The left sidebar provides hierarchical navigation inside a project. It's organized into three top-level sections, with **More** option expanding the grouped sub-pages. **Build** | Item | Purpose | | - | - | | Overview | Adaptive project home page with metrics, agent list, and quick actions. | | Agents | Agent list, agent editor, and in-editor testing. | | Workflows | Workflow builder and execution monitoring. | **Resources** | Item | Purpose | | - | - | | Tools | HTTP tools, code sandbox tools, and MCP servers. | | Knowledge Bases | Document ingestion, indexing, and search configuration. | | Integrations | Third-party connector catalog and active connections. | **More** (expandable groups) | Group | Pages | | - | - | | Evaluate | Evaluations, Experiments. | | Operate | Sessions, Deployments, Inbox, Alerts, Transfer Sessions. | | Insights | Dashboard, Analytics, Billing and Usage, Agent Performance, Quality Monitor, Customer Insights, Feedback, Voice Analytics, Agent Transfer, Pipelines. | | Govern | Guardrails, Governance. | | Settings | Members, API Keys, Models, and more. | You can expand a group to see its child pages. Use the back button to return to the top-level navigation. To increase horizontal working space on the UI, you can collapse the sidebar to an icon-only mode. Also, it auto-collapses when the agent editor is active. ### Project switcher The project switcher dropdown sits at the top of the sidebar and lets you move between projects without returning to the dashboard. It shows a scrollable list of all your projects with the current project highlighted. ### Command palette Press **Cmd + K** (macOS) or **Ctrl + K** (Windows/Linux) to open the command palette. The palette provides fuzzy search across: * **Navigation** — jump to any page within the current project. * **Agents** — open a specific agent by name. * **Actions** — reset session, clear events, and other quick commands. The command palette is the fastest way to navigate large projects. Type a few characters of a page or agent name to filter results instantly. ## Monitoring project health The project Overview aggregates the key runtime metrics for the project. Use it as your first stop when checking project health. | Metric | What It Measures | | - | - | | **Agent Count** | Total number of agents in the project. | | **Session Count** | Conversations processed by the project's agents. | | **Messages** | Total messages exchanged across all sessions. | | **Tokens** | LLM token usage across all agents. | | **Estimated Cost** | Cost computed from token consumption and configured model pricing. | | **Deployed Ratio** | Number of agents with active deployments versus total agents. | For deeper analysis, use the pages under **More > Insights** (Dashboard, Analytics, Agent Performance, Quality Monitor, Customer Insights, Feedback, Voice Analytics, Agent Transfer, Pipelines). For operational issues, use the pages under **More > Operate** (Sessions, Alerts, Inbox). ## Exporting and importing projects Export and Import let you move agent definitions and configuration between projects or environments. Both actions are available from the project Overview. ### Export project Use the **Export Project** dialog to package an agent project into a portable .zip file. The archive contains the project's agents, tools (including code, http, knowledge, MCP, and workflow tools), and any optional layers you select—such as guardrails, workflows, and channels. You can [import the project archive](#import-project) into another environment or create its backup as a versioned snapshot. Before you select the **Export** option, review the project contents in the export dialog. * **Project Summary**: Overview of the project, including project name and number of agents and tools in the project. * **Agents**: List of agents in the project. * **Tools**: List of tools in the project. * **Export Warnings**: If the platform detects any conditions that affect the export, such as automatic normalization of tool formats, it surfaces them in the Export Warnings. All warnings are informational and don't block the export process. Review warnings before exporting to understand what the platform has automatically adjusted. * **Export Layers**: Include or exclude different parts of the project from the exported archive. * **Export Options**: Include or exclude all published agent, config-variable, and workflow versions. - You must configure the credentials in the target environment after import. The export only includes the structural requirements and metadata (such as which profile types/auth fields are needed). - Any hardcoded secret values remaining in text/YAML files are replaced with `{{config.X}}` references before archiving. - The export includes the complete workflow definition, including nodes, edges, versions, triggers, and tool bindings. ### Import project To import an agent project or individual agent files, use the **Import Project** dialog. Navigate to **Project** > **Overview** > **Import**. The import supports merge-based import, stale content removal, environment reference adaptation, and three-way conflict detection. #### Upload import file Drag a file into the upload area, or click the upload area to open a file browser. The platform accepts the following formats: | Field or Option | Description | Notes | | :- | :- | :- | | `.zip` | A compressed archive containing a full project folder structure. | Most common for full project imports. | | `.agent-bundle.json` | A JSON bundle that packages agent definitions and metadata. | Produced by the platform's Export function. | | `.ablenc` | An encrypted ABL (Agent Behavior Language) file. | Used for secure distribution of agent logic. | | `.abl` | A plain-text ABL source file for a single agent. | Individual agent import. | | `.yaml / .yml` | A YAML-formatted agent or configuration file. | Individual agent or config import. | Expect the following folder structure in a `.zip` archive of a project. ```text theme={null} your-project/ ├── project.json # Manifest ├── abl.lock # Integrity ├── agents/ # Core - agent definition │ ├── supervisor.agent.abl │ └── booking_agent.agent.abl ├── tools/ # Core - tool definitions, including code tools, http tools, workflow tools, knowledge base tools, mcp tools. │ └── hotels-api.tools.abl ├── config/ # project configuration └── core/ # Core - mcp server config ├── connections/ # Connections ├── guardrails/ # Guardrails ├── workflows/ # Workflows └── search/ # Knowledge base and index configuration └── indexes/ └── knowledge-bases/ ``` #### Import mode The Import Mode section controls how the incoming project is reconciled with the existing, target project in the platform. The default mode is to merge. **Merge with existing project (default)** Matching records from the archive are updated in the target project. Records that exist in the target but are absent from the archive are left untouched. This is a non-destructive operation; no existing content is deleted unless explicitly overridden by the Replace modifier below. **Replace project contents** When checked, this modifier changes how each import layer (for example. agents, tools, connections) is handled: any content belonging to that layer that is present in the target but absent from the archive is removed. Content that exists in both the archive and the target is updated normally. Replace does not switch the import to a full overwrite. It is a modifier on top of Merge. Records that are in both the archive and the target are merged; records only in the target are deleted per layer; records only in the archive are added. | Record exists in | Merge only | Merge + Replace | | - | - | - | | Archive only | Added to target | Added to target | | Both archive and target | Updated (merged) | Updated (merged) | | Target only | Kept unchanged | Removed from target | #### Adapt references for this environment When enabled, the platform performs post-import normalization steps to make the imported project compatible with the current tenant and environment. * Portable bindings - Resolves any environment-portable connection or resource references so they point to the correct endpoints for this tenant. * Eval references - Normalizes evaluation dataset or metric references so they're valid within the current environment's evaluation framework. If a referenced modelId doesn't exist in the target tenant, the user is prompted to pick a replacement. * Post-import validation - Runs a validation pass on the imported project after the above steps complete, surfacing any remaining reference errors like auth profiles, environment variables, connector credentials, MCP server auth, etc. #### Use a base export for conflict checks Enables three-way conflict detection during import. When active, the platform compares three states. * The base snapshot - the original export of the project before any edits were made. * The archive being imported - the version with the author's changes. * The current target - the live state of the project in the platform. When enabled, attach the base export file. This allows the platform to distinguish between a true conflict (where both the archive and the target have independently changed the same record from the base) versus a non-conflicting divergence (where only one side changed). Without a base snapshot, any difference between archive and target is treated as a conflict. #### How to import a project 1. Navigate to the project where you want to import content and select **Import**. 2. Drag your file into the upload area, or browse and select a file. Supported formats are: .zip, .agent-bundle.json, .ablenc, .abl, .yaml. 3. Choose your **Import Mode** options. Merge is always active. Check **Replace project contents** if you want to remove layer-level content from the target that's absent from the archive. 4. Optionally, check **Adapt references for this environment** to normalize bindings and references for the current tenant. 5. Optionally, check **Use a base export for conflict checks** to enable three-way conflict detection. 6. Select **Import** to start the import. 7. Review any validation errors or conflict reports surfaced by the platform after import completes. Imported workflows include the complete workflow definition, including nodes, edges, versions, triggers, and tool bindings. As a result, workflows are functional immediately after import and do not require manual reconfiguration. ## Delete a project Deleting a project removes it and all of its contents permanently. **To delete a project:** 1. Open the Projects dashboard. 2. Click the three-dot menu on the card of the project you want to delete. 3. Select **Delete**. 4. In the confirmation dialog, type the project name exactly as shown. 5. Click **Delete**. Deleting a project permanently removes all agents, tools, knowledge bases, workflows, sessions, and deployment configurations in that project. Deleted projects are archived for 30 days. ## Features and permissions Some project features depend on workspace configuration or your assigned role. If a feature appears unavailable, check the conditions below. | Feature | Requirement | | - | - | | **AI Architect** | An LLM provider must be configured under **Admin > Models**. Otherwise an in-panel banner is shown. | | **Platform Admin** link | Visible only to users with super-admin permissions. | | **Admin pages** (Members, Models, Security) | Require workspace administrator permissions. | | **Some Insights pages** | Agent Performance, Quality Monitor, and Customer Insights may show a "coming soon" placeholder if not yet enabled for your workspace. | If you expect access to a feature and don't have it, contact your workspace administrator. # Prompt library Source: https://koreai.mintlify.app/agent-platform/prompts It's a central place to create, manage, and reuse prompt templates across your agents and workflows — with version tracking, response testing, and reference lookup built in. Navigation: select a project, then go to **Resources** > **Prompt Library**. ## Prompt lifecycle A typical prompt lifecycle in Prompt Library: ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR A[Create Prompt] --> B[Test and Compare] B --> C{Satisfactory?} C -- No --> D[Refine and Version] D --> B C -- Yes --> E[Activate Prompt] E --> F[Reuse and Manage] ``` | Stage | Description | | - | - | | Create Prompt | Create a reusable prompt template and define its instructions and content. | | Test and Compare | Test the prompt across one or more models and compare responses, token usage, and response times. | | Refine and Version | Update the prompt based on test results and save new versions as needed. | | Activate Prompt | Promote a validated version to active status for production use. | | Reuse and Manage | Reuse the prompt across the project and manage it through version history and references. | ## Create and manage prompts The Prompt Library page lists all prompts in the selected project. For each prompt, you can: * Define prompt instructions and content. * Add tags for categorization. * Save prompts as drafts. * Promote validated prompts to the active version. Create prompts ## Manage prompt versions Each prompt supports multiple versions. Use the **Versions** tab to: * View all available versions. * Track revisions over time. * Compare changes between versions. * Promote a selected version to active status. ### Prompt states | State | Description | | - | - | | Draft | A work-in-progress version used for editing and testing. | | Active | The published version available for production use. | ## Test and compare prompts Use **Test & Compare** to evaluate prompt behavior across multiple language models before deployment. You can: * Select a prompt version. * Choose models for comparison. * Provide a test user message. * Upload an optional context file. * Run the prompt against selected models simultaneously. Test prompts Results appear side by side with response metrics including response time and token usage. ### Use context during testing You can upload files to include additional context during a test run. The uploaded content is available during prompt execution, letting you evaluate how the prompt handles real-world, project-specific information. ## View prompt references The **References** tab shows where a prompt is used within the project. Use it to: * Understand prompt dependencies. * Assess the impact of changes before modifying an active prompt. * Identify agents and workflows that rely on the prompt. ## Best practices * Use descriptive names that clearly indicate a prompt's purpose. * Add tags to improve organization and searchability. * Save changes as drafts while iterating. * Test prompts across multiple models before promoting to active status. * Review references before modifying active prompts. * Use version history to track prompt evolution and preserve previous revisions. *** # Artemis release notes Source: https://koreai.mintlify.app/agent-platform/release-notes/recent-updates Stay up to date with the latest platform updates, new features, and enhancements.

Agent builder experience

PostgreSQL connector

Agents and workflows can now connect to PostgreSQL databases. Supported actions include Get Tables, Get Table Schema, Find Rows (with column filters, ordering, and pagination of up to 500 rows), Insert Row, Update Rows, Delete Rows, and Run Query with parameterized binds. Update and Delete operations refuse an empty match to prevent accidental bulk changes.

Microsoft Exchange on-premises connector (EWS)

Agents and workflows can now connect to on-premises Microsoft Exchange Server mailboxes through Exchange Web Services (EWS). Supported actions include sending, finding, and reading emails, retrieving attachments, and running custom SOAP requests. A polling trigger watches for new emails. This is separate from the existing Microsoft Outlook connector, which reaches Exchange Online through Microsoft Graph.

Auto Loop approval modes

Auto Loop now supports three approval modes: Autopilot applies changes automatically, Copilot applies them after your approval, and Advisor suggests changes only. You can set the mode when starting a loop or change it mid-run, and it carries over when you restart a loop or switch the source evaluation. The server also automatically advances stalled loops.

Router auth profiles and credential testing

Auth profiles now support router mode (`MODE: router`), which selects another auth profile for each tool call based on parameter values, such as brand or country. Parameters used for routing can be marked as Auth only, so they aren’t sent to the API. Router profiles don’t store credentials themselves. Test Credentials now supports request-signing profiles, including SigV4 and DPoP, and shows which profile a router selects. Guard conditions can use ABL expressions to route requests based on response status and other supported context.

Guardrail access to tool arguments and results

Output guardrail checks can now reference the pending tool call's arguments, this turn's tool results, and the full session state in their conditions.
[Learn more →](/agent-platform/abl/reference/guardrails#content-session-state-and-tool-result-variables)

Hand off to a specialist in ON\_START

ON\_START now accepts a `DO` list that includes `HANDOFF`, so an entry agent can route the session to a specialist before the first user message arrives. This is useful for inbound voice calls where the channel or caller data determines which agent should handle the call. After the specialist returns or fails, the entry agent resumes with its own model settings.

Output schemas for MCP tools

MCP tools now display and preserve output schemas alongside input parameters. When an MCP server declares an output schema for a tool, Studio shows it on the tool detail page, the compiler carries it through compilation, and the agent definition retains it. This lets you see exactly what each MCP tool returns, without consulting the server’s own documentation.

Access raw speech transcripts in flow steps

A new read-only `input_transcript` value gives flow steps, hooks, and reasoning logic the speech provider’s raw transcription of the current caller turn. This lets voice agents access what the caller said, without relying solely on the model’s interpretation.
[Learn more →](/agent-platform/abl/reference/expressions-and-functions#in-set-assignments)

Control which memory entries the model sees

You can now mark individual automatic memory entries as hidden from the model. Hidden entries remain available in expressions and tool calls but are excluded from the model’s prompt, so sensitive or noisy context does not influence the model’s reasoning. Set visibility per memory field in the agent editor or in ABL.

Set interaction language in ON\_START

ON\_START handlers now accept `SET_INTERACTION_LANGUAGE` and `CLEAR_INTERACTION_LANGUAGE`. You can set the conversation language during startup, so the first response already uses the correct language without waiting for the caller to speak. This is especially useful for voice agents whose channel or SIP headers indicate the caller’s language.

Module agents limited to declared tools

An agent imported from a reusable module now receives only the tools declared in their own definition, not every tool the module exports. This prevents an agent from seeing or calling tools its author never assigned to it, matching standalone agents' behavior.

Credential header validation for HTTP tools

The compiler now warns when an HTTP tool’s headers contain a credential-shaped value, such as an Authorization or API-key header written as plain text instead of a secret reference. Studio shows these diagnostics on the tool detail and list pages, letting you find and fix exposed credentials before deploying. This is a warning, not a blocking error, so an existing tool with an inline credential still compiles and runs.

Redesigned feedback templates experience

Feedback templates in Studio now have card and list views with type-based filters (NPS, CSAT, Like/Dislike, Comment), a read-only summary page with a full-width interactive preview, and a single-page creation flow. Selecting a template opens its summary, and you can edit it from the overflow menu.

Arch AI is usable on mobile devices

The Arch AI interface now adapts to phone and tablet screens. On smaller displays, chat and artifacts fill the width behind a toggle instead of splitting the screen, and the status bar scrolls horizontally so the primary action stays visible.

Unsupported requests excluded from multi-intent cap

Requests that no agent can serve are no longer counted toward the multi-intent maximum, so they do not push valid requests into overflow. The Runtime Config panel also groups the multi-intent settings under clearer labels.

Warnings for invalid ESCALATE trigger formats

The compiler now warns when an ESCALATE trigger uses a format it cannot read, such as `condition:` or `keywords:` items instead of the required `WHEN:` syntax. Previously, these triggers were silently ignored, so the agent compiled without any escalation triggers, even though the author believed keyword escalation was configured. The new warnings (W355-W357) name the problem and its line number.

Clearer ENABLED\_WHEN availability rules

Multi-line `ENABLED_WHEN` conditions that use the documented block indicator syntax no longer trigger a spurious warning. Missing-data handling in availability conditions is also corrected: a value compared with `== null` or `IS NOT SET` counts as known, while other missing values disable the route. The coordination UI for delegates and handoffs now reflects these rules.

Knowledge base

Business rules for knowledge base search

You can now add business rules to a knowledge base search tool in your agent’s ABL definition to control what a search returns, such as restricting results to certain sources, boosting values, or hiding internal content. Rules declared in an agent apply only to that agent’s searches, so two agents that share one knowledge base can get different results. Rules that fail validation are skipped, not applied.

KB agents use cross-channel conversation history

Knowledge base agents now use conversation history recalled from other channels when answering. If a customer already provided details on Teams and continues on the web, the KB agent uses that context instead of asking again.

Field mapping for knowledge base connectors

When setting up a knowledge base source, you can now map source fields to index fields in the connector wizard. This lets you control which document metadata is indexed and how it is stored, so search results reflect your content structure. Field mapping is available for the SharePoint, Confluence, Jira, Google Drive, Salesforce, ServiceNow, S3, SFTP, and Box connectors.

Per-object field scoping for Salesforce connector

The Salesforce Standard connector now lets you choose which fields to index for each Salesforce object. In the connector wizard, select objects, then refine the field scope per object so only the fields your agents need are indexed and searched. This reduces noise in search results and keeps your index focused on relevant content.

Enhanced field intelligence for knowledge bases

The field intelligence experience in knowledge bases now shows complete field capability metadata and offers an improved JSON field selection dialog, making it easier to map structured data fields to search indexes.

Voice experience

Grok realtime voice models

Grok realtime voice models from xAI are now available as a speech-to-speech voice provider. You can select Grok realtime when setting up a realtime LLM voice channel, alongside the existing OpenAI, Gemini Live, and Nova Sonic options.

Microsoft Speech deployment modes and Custom Neural Voice

Microsoft Speech services now support several deployment modes: Azure public cloud, Government, China sovereign cloud, Private endpoint, and Container. You can choose whether a credential is used for STT, TTS, or both, and configure Custom Neural Voice with a deployment ID and declared voices. The new settings appear in Voice Services and in each voice channel’s configuration.

Per-field speech recognition settings

Voice flow `GATHER` fields can now carry their own speech recognition configuration, so each field can use a different ASR provider, language, or vocabulary. For example, a name field can use a broad vocabulary, while an account-number field uses a tighter one. The deployment preflight validates these settings.

Extended voice filler sequences

Voice fillers now keep callers informed while Runtime processes responses, including during long-running operations and agent handoffs. * Voice fillers can now play up to eight lines per turn in the low-latency voice profile, up from five. On pipeline voice channels, fillers continue across agent handoffs and automatically replenish with up to two new lines when tool or model calls take longer than expected. Project-level settings take precedence over profile defaults. * For Gemini Live calls that use runtime-owned speech with the generative native filler, Runtime can play up to four short fillers per caller turn while preparing the response. Other Gemini calls continue to play one filler.

ON\_START runs on OpenAI and Azure realtime channels

OpenAI and Azure speech-to-speech channels now run your `ON_START` handler and entry flow before the first turn, matching the behavior of Gemini Live channels. Previously, these providers skipped authored startup logic, so session initialization, language settings, and welcome messages set in `ON_START` did not take effect.

Queued caller speech is joined into one turn

On KoreVG voice channels, when the agent is busy processing a response, additional caller speech is now combined into a single follow-up turn, instead of being handled as separate turns. Previously, the agent could respond separately to fragments such as “Hello?” and “Are you still there?” before addressing the caller’s actual question. Keypad input, active gathers, secure input, hold phrases, and language changes still keep their own turns.

Bind secure input results to session variables

The `SECURE_INPUT CALL` statement now accepts `AS`, `RESULT_FIELDS`, and `SUCCESS_WHEN` parameters. You can bind the delivery result to a session variable, choose which response fields are allowed out of the secure boundary, and define an exact-match success condition. Misplaced lines inside the block now raise a warning instead of failing silently.

Voice speech delivery is now a project setting

Streaming speech delivery on pipeline voice channels is now controlled by a Voice Speech Delivery toggle in project Runtime Config, replacing the previous environment variable. New projects have it on by default. Streaming delivery gives each response its own playback lifecycle, so the gateway resolves speech cleanly after barge-in, hold music, and filler transitions.

Inbound SIP header trust and transfer reliability

Voice channels now include an Inbound SIP Header Trust toggle that controls whether inbound SIP headers from the carrier are treated as SBC-asserted or untrusted. SIP REFER transfers with User-to-User URI headers are now correctly bracketed, resolving 488 rejections from some SBCs. The transfer trace also records whether the destination came from the agent definition or from a caller-supplied SIP header.

Post-transfer surveys from flow steps

Post-transfer surveys can now be configured in a `FLOW` step’s `FEEDBACK` block, without requiring an agent-level survey configuration. When a human agent closes the conversation, the platform uses the step-level survey if no agent-level survey is configured and either only one step defines a survey or all steps use the same survey template. Survey ratings now retain the survey template, agent name, and agent version, letting them appear correctly in per-survey Feedback Insights. Survey cards also show the survey expiry and the configured star or emoji rating style. The transfer record remains pending until the survey expires, then closes.

Welcome message barge-in control for voice channels

Pipeline voice channels now have an *Allow caller to interrupt the welcome* toggle in the channel configuration. When disabled, the agent’s welcome greeting plays fully before the channel accepts caller speech, so callers cannot talk over it. The setting applies only to channel-level welcomes and is preserved when you export and import the project.

Voice survey speaks the caller's language

The post-agent CSAT survey on voice channels now plays in the caller’s language instead of the channel default. For example, a French-speaking caller hears the survey questions and closing lines in French, with a matching TTS voice, based on the agent’s locale catalog and language conditions.

Speech provider errors shown in session traces

Voice channels now accept custom speech settings. When a speech provider fails (for example, because of an invalid ASR configuration), the error appears as a clear, translated message in the session trace. Previously, these failures weren't surfaced, making misconfigured speech pipelines hard to diagnose.

Clearer Gemini realtime voice settings

Studio now labels which filler and voice settings apply to Gemini realtime calls and which do not. The project’s Fillers-enabled and Voice-fillers switches are identified as master controls, and a notice appears on the Gemini channel panel when they are off. Proxy MOS is described as a ‘timing estimate’ on all surfaces.

Greeting Audio interrupt requires channel Barge-in

The Greeting Audio interrupt toggle on voice channels now appears only when the channel has Barge-in enabled. Previously, enabling interrupt with Barge-in off had no effect and could suppress the text greeting fallback. The runtime now treats the greeting as non-interruptible unless Barge-in is active.

Digital experience

Agent calls and screen sharing in the Web SDK

When a conversation is transferred to a human agent, the Web SDK now supports audio/video calls and screen sharing through the SmartAssist contact center. The SDK loads the WebRTC stack on demand and shows a shared media panel for these capabilities. Media events are relayed across runtime pods, so the feature works in multi-pod deployments.

Microsoft Agent 365 channel

Agents can now be deployed to Microsoft Agent 365 through a dedicated channel in the Deployments section. Configure the connection in its own setup tab, and the platform handles message routing between your agent and the Agent 365 ecosystem. The channel is available when enabled for your deployment.

Redesigned channel setup with inline guides

The Deployments section is now redesigned. Each channel connection shows a single setup guide with step-by-step instructions alongside the configuration form, replacing the previous tab layout. Every channel type, from Slack and WhatsApp to AudioCodes, A2A, and AG-UI, has its own detailed guidance.

Slack sender identity and per-thread sessions

The Slack channel now makes the sender’s user ID available to agent tools through session metadata, letting a tool look up the person who sent the message. Channel messages also get their own session per thread, so separate requests no longer share state. Direct messages continue as a single conversation.

LLM and models

New models: Claude 5, GPT-6, GPT-5.6, and Gemini 3.5 to 3.8

The model catalog now includes Claude 5 (Fable, Opus, Sonnet), GPT-6 (Sol, Luna, Astra), GPT-5.6 variants, Gemini 3.5 through 3.7 Flash, and Gemini 3.8 Flash as a text model. GPT-5 models are also available on AWS Bedrock, with a new Bedrock API key credential option. New Azure GPT-5.6 Luna connections default to the Responses API, which tool-calling agents need.

Project-level model parameter overrides

You can now override behavior parameters for each selected model at the project level. Set reasoning effort, max tokens, temperature, compaction threshold, and other parameters per model without affecting the shared workspace configuration. Only parameters the model supports are shown, and the runtime rejects overrides it cannot honor. The controls are on the Models page in project settings.

Model connections validated on creation

When you add a model connection, the platform now validates that the provider endpoint is reachable and the credentials work before saving. If validation fails, the platform cleans up the credentials and does not create the connection. Connections that can’t reach the provider are no longer saved in a broken state.

Connection labels for same-model credentials

When a model has more than one connection, each connection now shows a stable Connection 1 or Connection 2 label. The label appears in the Model Catalog, project, feature config, and eval model pickers, on admin connection cards, in Feature Config’s *Resolves to* column, and in the *Why this model?* section. Numbers never change or get reused when a connection is deleted. Models with one connection look the same as before.

Insights

Accurate turn timing and latency profile in Sessions

The Sessions trace view now shows accurate turn-level timings, including a latency profile, time to first token, and step-by-step timing offsets. Nested execution spans carry identity information, letting you trace exactly where time is spent. Lifecycle markers and session handoff events no longer inflate step durations.

Hook tool calls visible in traces

Lifecycle hook `CALL` actions now appear as separate tool call chips in the Observatory, showing the HTTP URL, request payload, response, duration, and error status. Previously, hook tool calls were hidden inside the hook row with no detail. The turn waterfall also groups tool calls correctly, letting you see the full sequence of model-initiated and hook-initiated calls in one view.

TTS first-audio latency in voice analytics

Voice interactions now capture the time from a text-to-speech request to the first audio byte reaching the caller. You can see this TTS first-audio latency in the Observatory interaction waterfall, alongside STT and LLM latency, to pinpoint where voice response delays originate.

Governance

JWT App credentials for API authentication

You can now authenticate to platform APIs using JWT App assertions, a new credential type alongside API keys and SDK tokens. Create a JWT App in Studio, configure key rotation, and optionally enable payload encryption. The platform verifies each assertion against your JWKS endpoint and protects against replay attacks.

Live PII masking in agent preview

The secure-preview and project-preview pages now mask sensitive user input in real time, matching the masking behavior in the deployed Web SDK. Previously, preview pages echoed the raw typed value in the user bubble, even when PII redaction was enabled. The platform now replaces PII with a placeholder on send and resolves it from the runtime’s masked output.

Monitor and trace PII redaction enforced

PII fields configured for redaction are now consistently scrubbed from the Monitor live stream, workflow execution traces, and observability logs. Previously, certain paths didn’t apply the configured redaction rules, so sensitive values could appear in real-time monitoring views.

SSO just-in-time provisioning with role mapping

SSO configurations now support just-in-time (JIT) provisioning with automatic role assignment. When a user signs in through your identity provider for the first time, the platform creates their workspace membership and assigns a role based on rules you define from SSO claims. Existing members keep their current role, and the JIT policy always includes the owner domain.

Centralized voice gateway credentials

Platform administrators can now provision and manage voice gateway credentials centrally in the Admin console, then assign them to tenants. Projects use the platform credential by default, with an option to bring your own. Credential sets support rotation, tenant-level assignment, and retirement tracking, so you no longer need to configure voice credentials per project.

Secret config variables stay out of session state

Project configuration variables marked as secret are no longer copied into session state. They are held only in the runtime’s secure context and resolved at call time, so secret values never appear in execution snapshots, the Studio state stream, tool result embeds, or model prompts. Existing agents that reference secret config through template expressions still work; only the storage path has changed.

Workflow secrets excluded from execution traces

Secret configuration values used in workflow executions are no longer visible in execution traces or the Monitor. The runtime now passes secret references instead of decrypted values, so workflow traces show only the key names. This closes a path where secret configuration could appear in execution snapshots.

Audit events now attribute the acting user

Studio audit log entries for channel connections, SDK channels, webhook subscriptions, SearchAI mutations, auth profiles, and evaluation events now consistently record the user who performed the action and their IP address. Previously, some of these paths fell back to a generic system actor.

Richer model and connection audit entries

Model and connection changes in the Audit Log now include the model name, provider model ID, credential name, and before/after values for properties such as tier, default status, and display name. Previously, entries showed only field names or raw IDs, which made it hard to see what changed.

APIs and SDK

Standardized public Tool API

The public Tool API now uses three canonical paths, a standard response envelope, and a complete OpenAPI specification. The older per-environment paths are removed from the specification, so newly generated clients no longer include them. Creating a JWT App now requires a platform key with the sensitive-operations permission, and workflow invoke and status routes now check project scope.

Larger agent context for SDK sessions

SDK sessions can now pass up to 64 KB of agent context at bootstrap, removing the previous limit of 32 keys with 512-character values in `customAttributes`. The context is stored encrypted and delivered to the agent before the welcome message, so `ON_START` workflows have the full profile immediately.

Filter analytics sessions by caller number

The public analytics Sessions and Conversation History APIs now accept a `callerNumber` filter. Pass a phone number with its international prefix to retrieve sessions from that caller. Malformed or bare-digit numbers return a 400 error, and projects that have disabled caller identity in their reporting configuration receive a 403 error.

Agent builder experience

Configuring multi-intent request completion

Two new settings for managing multi-intent interactions have been added to Runtime Config: * **When a request is finished:** Determines whether a request ends when the agent replies or only when the agent confirms it is done, so follow-up questions no longer move the conversation to the next request too early * **Capturing every request in one routing decision:** The supervisor lists all of the customer's requests at once, so a second request isn't lost when the model routes only one.

Auto strategy no longer runs requests in parallel

The auto multi-intent strategy now handles independent and dependent requests one after another, and asks the customer to choose when the requests are ambiguous. To run independent requests at the same time, set the strategy to parallel.

Clearer tool confirmation questions

When a tool call needs approval, the customer now sees a plain question built from the tool's consent action and readable parameter labels, such as the account and amount. Sensitive values are masked, tool names and internal instructions are never shown, and you can change the wording in the agent's `MESSAGES` block.
[Learn more →](/agent-platform/abl/reference/tools#what-the-customer-is-asked)

Model context for tool-hook responses

A tool's `on_result` or `on_error do:` action can now include a short `MODEL_CONTEXT` beside its `RESPOND`. The customer still gets the full response, while later model calls see only your summary, keeping large responses out of the model's own conversation context.
[Learn more →](/agent-platform/abl/reference/tools#result-transformation) ```yaml theme={null} TOOLS: list_products() -> object MODEL_CONTEXT: FIELDS: [products[].id, products[].name, total] on_result: do: - RESPOND: "{{result.widget}}" MODEL_CONTEXT: "Displayed {{result.total}} products in order: {{result.product_summary}}." COMPLETE: on_error: do: - RESPOND: "Product search is unavailable." MODEL_CONTEXT: "Product search failed; no products were displayed." COMPLETE: ```

Project imports keep model selections

Project exports now include the project's selected models. When you import the project or install it as a template, those models are linked, and the archive's default model is applied. The import preview lists which models will be linked, and imported search pipelines keep their custom embedding model.

Knowledge base

Web crawler works with more protected sites

When a site answers the crawler with a browser challenge page, the crawler now retries with a browser profile. Sitemap crawls also keep your robots.txt setting, and recrawls keep their original settings.

Voice experience

Pre-written fillers continue long waits

On voice, the configured static filler pool now continues a long wait after the AI-generated lines run out, up to the per-turn limit, so callers no longer hear silence after the first filler. Custom filler prompts can produce up to three lines, the same as the built-in prompt, and a phrase is never repeated in one turn. Runtime Config shows an instruction you can copy into a custom prompt to get more than one line.

Recover unanswered Gemini caller turns

Gemini realtime channels have a new *Recover unanswered caller turns only* switch under Silent-turn recovery. When Gemini hears the caller but doesn't answer within eight seconds, the platform restarts the session with the caller's words. It doesn't re-prompt after tool results.
[Learn more →](/agent-platform/channels/voice#google-gemini-live)

Insights

Wider table widgets in custom dashboards

Table and detail widgets in custom dashboards can now save up to 200 columns, up from 20, so wide SQL results no longer need to be split across widgets.

Agent builder experience

Control what the model sees from tool results

You can now decide how much of each tool result the model keeps in the conversation: the full result, selected fields only, or a short summary built from a template. Flow steps and completions also accept a note describing what the customer received, for later reasoning. The customer-facing response doesn't change, and large tool payloads no longer crowd out the rest of the conversation history.

Idempotency header for HTTP tools is now opt-in

HTTP tools no longer send an idempotency header on every call by default, which resolves errors from providers such as Salesforce that reject unsupported headers. If your provider supports idempotency, such as Stripe, Adyen, or PayPal, enter its header name in the new Idempotency Header field in the tool's resilience settings. The platform still protects retried calls from running twice.

Filtered bulk delete in Agent Tables

Agent Tables can now delete many rows at once by filtering on any column, not only a unique key. Grant the new bulk delete operation on a table tool, run `DELETE ... WHERE` in the query console, or use the API. A configurable row limit of up to 10,000 rows guards against accidental large deletes.

Duplicate-run protection for workflow and MCP tools

Workflow and MCP tools are now treated as having side effects unless you mark them otherwise, so retried calls no longer run the same operation twice. As a result, confirmation policies that apply to side-effecting tools now cover these tools too.

Clearer deployment preflight errors

Deployment checks now follow the same model rules as the runtime, so agents that work in testing no longer fail deployment with unexpected errors. When there is a real problem, the message names the agent and points you to the setting to fix, instead of showing a generic error count.

Oracle Database connector

Agents and workflows can now connect to Oracle Database 12.1 and later. Supported actions are Run Custom SQL, Insert Row, Insert Rows, Update Row, Delete Row, and Find Row, with results returned 500 rows at a time. Set up the connection with an auth profile, then use it from tools and workflows.

Knowledge base

FAQ connector

Knowledge bases can now ingest FAQ content through a dedicated FAQ connector. Upload question-and-answer pairs as a JSON or CSV file, or add them manually in a guided wizard that detects fields for you. You can exclude fields from indexing and split list values into separate entries. FAQ answers are also tuned so exact question matches rank well.

Voice experience

Caller transcription settings for speech-to-speech channels

OpenAI and Gemini speech-to-speech channels now include a Caller Transcription section where you can choose the transcriber, set the language callers speak, and list words to expect, such as brand names. Setting the caller language stops the model from guessing it on every turn, which could otherwise switch English speech into another script.
[Learn more →](/agent-platform/channels/voice#set-up-realtime-llm-voice-channel)

Continuous filler updates during long waits

Voice agents now prepare several filler lines at once: an opener, then timed progress updates spaced by a configurable follow-up gap after each line finishes. Callers hear steady updates during long lookups, instead of one filler followed by silence. Fillers follow the conversation's language, and you can set a default or override language in Runtime Configuration.

Voice safeguards are now opt-in

The voice grounding guards, built-in guardrail bindings, and realtime channel defaults introduced in v1.7.0 are now off by default, so existing agents keep their previous behavior. You can enable them per agent with new execution settings. Realtime channels also gain an Advanced voice behavior section, with options such as which caller wording the agent acts on.

Step-level no-input prompts

Voice flow steps can now have their own prompts for when the caller stays silent, instead of sharing one agent-wide sequence. For example, a greeting step and a payment step can each respond to silence in a way that fits the moment. [Learn more →](/agent-platform/abl/reference/flow#step-level-no-input-caller-silence) ```yaml theme={null} FLOW: ask_account: REASONING: false RESPOND: "What's your account number?" GATHER: - account_number: required VOICE: ON_NO_INPUT: timeout_ms: 10000 messages: - text: "Could you tell me your account number, {{memo_name}}?" THEN: complete ```

Localized secure input prompts and tool arguments

Secure input prompts and tool call arguments can now use entries from your locale catalog, following the caller's language with the usual fallback order. If a translation is missing, the flow receives a clear result it can handle, instead of raw template text.

More reliable tool calls on Gemini Live

Gemini Live agents no longer stall or lose tool results when the provider restarts its session mid-turn. If Gemini cancels a tool call and asks for it again after the caller speaks, the platform reuses the call already running, avoiding duplicate actions such as a repeated booking.

Governance

Payload encryption for public analytics APIs

Platform keys used with the Sessions, Conversations, LLM Logs, and Traces APIs can now encrypt request and response payloads. Turn on request encryption to get a key for encrypting what you send, and add your RSA public key to receive encrypted responses in a standard JWE envelope. The new Payload encryption section is in the platform key settings, and existing keys continue to work unchanged.

Agent builder experience

Agent Tables on non-production deployments

Agent Tables can now be used from deployments in every environment, not only production. All deployment environments currently read and write the same production rows; per-environment table data is not yet available.

Voice experience

Background audio behavior with barge-in disabled

Background audio settings on Pipeline Voice channels now indicate when the audio actually plays. Between-sentence background audio requires barge-in to be enabled on the channel and a streaming text-to-speech voice. When barge-in is disabled, background audio plays only before the agent starts responding, because the reply is spoken as one continuous block with no gaps between sentences.
[Learn more →](/agent-platform/administration/general-settings#background-audio)

Caller identity on outbound calls

On outbound calls, the customer is now identified by the number the platform dialed, so contact, memory, and conversation history are attributed to the right person.

Emotech hotwords and Deepgram custom vocabulary controls

Custom vocabulary controls on voice channels now adapt to the speech provider you select: * **Emotech channels** add a Hotword Weight field that applies to every hotword in the list, with a recommended range of 5–10. Clearing the list turns hotwords off. * **Deepgram channels** show the vocabulary limit inline (up to 100 words) and warn when a saved phrase contains a comma, since the voice gateway splits comma-separated text into separate words.
[Learn more →](/agent-platform/channels/voice#set-up-pipeline-voice-channel)

Agent-spoken messages in the conversation transcript

Messages the agent speaks outside a normal turn — handoff announcements, no-input prompts, and farewell messages — now appear in both the transcript and the Conversation view. Previously, these reached the caller but were missing from the transcript, making voice sessions harder to review and audit.

Tool response scheduling default for Gemini Live

The Tool response scheduling setting on Google Gemini Live realtime channels now defaults to **When idle**, so the model finishes its current response before processing a tool's final result. Progress updates for a pending tool call and native filler audio now always wait until Gemini is idle — regardless of this setting — so they no longer interrupt a response that's already underway. Channels that relied on the previous immediate-response behavior can switch to **Interrupt** to restore it.

Governance

Runtime secret context for workflow tools

Workflow tools can now resolve secret references from the runtime secret context, as HTTP tools already do. The tool retrieves the credential at call time, and the secret value never enters the model prompt, execution logs, or traces — so it's never exposed to the agent or persisted anywhere.

Agent builder experience

Higher scenario and persona limits for evaluation suites

Evaluation suites can now include up to 500 scenarios and 10 personas, up from 3 and 3, letting you cover far more conversation paths in a single suite. The evaluator limit stays at 12 per suite, and suites generated automatically with Arch still start at 3 scenarios and 3 personas by default.

Unified human task handling

Process workflows can now handle approvals and data collection through a single Human Task node. It pauses the workflow, notifies assignees, and can collect structured data through a reusable Form before resuming based on the selected outcome. You can define SLAs, configure reminders and escalation steps, and specify how to handle SLA breaches. Forms can be created once as reusable, project-level assets and used across workflows. This gives process workflows a simpler way to manage human decisions, data collection, and task deadlines while reducing duplicate form creation.
[Learn more →](/agent-platform/forms)

Scenario-level session variables in Evals

Evaluation scenarios can now define their own session variables, not just personas. When a scenario and a persona share a variable, the scenario's value wins by default, unless the persona marks that key as priority, in which case the persona's value is kept. This lets you test the same persona across different contexts without cloning it for each one.
[Learn more →](/agent-platform/evals#scenarios)

Conditional routing in ESCALATE blocks

ESCALATE routing can now be an ordered list of destinations, each guarded by a condition such as channel or language and evaluated in order, with a required default as the last entry. Previously, routing to different destinations per channel — for example, a voice transfer versus a digital handoff — required duplicating the entire agent. Now, one agent can express both.

Runtime-Guard for realtime orchestration

A new Runtime-Guard mode lets a realtime model stay in control of the conversation, proposing actions and extracting values naturally, while the runtime validates every context write and business action against the active flow step, and rejects anything invalid, stale, duplicate, or out of order. This keeps realtime conversations flexible without giving up control over side effects; the stricter runtime-owned mode remains available where it's still needed.

Knowledge base

Knowledge base included in import/export

Project export now includes each knowledge base's configurations, including source configurations and pipeline stages, instead of just an empty tool shell pointing at a missing index. On import, the knowledge base, its search index, and its sources are provisioned automatically, and the knowledge base tool is reconnected to the new index.

Voice experience

PCI-compliant secure DTMF input with voice channel suppression

Flow authors can now collect sensitive DTMF input, such as credit card numbers, while fully suppressing the voice channel: no speech reaches ASR models and no audio is captured in call recordings during collection.

Amazon Nova Sonic (S2S) support via Bedrock

Amazon Nova Sonic is now available as a realtime speech-to-speech voice provider via AWS Bedrock, alongside the existing OpenAI and Gemini Live options, with barge-in and live transcription support.

Configurable static filler messages per language

Static filler messages played during voice processing pauses can now be added or updated per language directly from Studio's Runtime Configuration panel, with no platform deployment required.

Call-status reason fields for unanswered outbound calls

Agents and campaign workflows can now branch on why an outbound call ended - busy, no answer, network timeout — instead of only knowing that it failed.

Low-latency customer-service voice profile

Voice projects can now apply a low-latency profile tuned for customer-service calls, using faster model tiers and disabled reasoning by default, in exchange for quicker responses. Studio flags when a project's settings deviate from the recommended values and lets you apply them individually or all at once.
[Learn more →](/agent-platform/administration/general-settings#voice-low-latency-profile)

Voice channel recovery on LLM and tool failures

The platform now classifies voice and tool failures into specific recovery codes, such as an LLM timeout, an unavailable tool, or a provider disconnect, instead of collapsing everything into a generic failure, with configurable retry and fallback behavior per channel. When a tool call is running slowly, the runtime can also generate a response speculatively, so the caller hears something sooner instead of dead air.

Insights

Credential attribution in LLM logs

LLM Logs now record which credential or auth profile served each model call, so you can attribute token and cost consumption to a specific source rather than just a project or model. This includes distinguishing two credentials that share a display name across projects. [Learn more →](/agent-platform/analytics-insights/analytics-explorer#llm-performance)

Governance

Improved AWS KMS BYOK support

AWS KMS bring-your-own-key configurations now support AssumeRole authentication, matching the structured setup Azure KMS already had, and no longer silently fall back to the platform's own AWS credentials if a tenant's are incomplete.
[Learn more →](/agent-platform/administration/workspace-and-team#aws-kms)

Auto-accept workspace invites option for SSO

Workspace admins can now enable automatic invite acceptance for SSO sign-ins, so a user invited to a workspace no longer hits an authentication error if they log in through their identity provider before separately accepting the invite. The option is configurable per workspace under SSO Settings.
[Learn more →](/agent-platform/administration/workspace-and-team#single-sign-on)

GCP Workload Identity Federation support

GCP Workload Identity Federation is now supported for LLM and voice credentials, letting projects with a no-static-keys security policy authenticate to Gemini and Vertex AI, including Gemini Live for voice, without a long-lived service-account key file.

Knowledge base

PDF formula extraction

Knowledge bases can now extract mathematical formulas from PDF documents. Previously, formulas weren't recognized as either text or images during extraction, so they were left out of the indexed content. They're now captured along with the rest of the document. This is enabled by default for newly created knowledge bases, improving retrieval quality for technical and scientific content. Existing knowledge bases keep their current extraction configuration.

SharePoint connector: quick actions and permission-aware search

The SharePoint connector's overview screen in Studio now includes a Quick Actions bar for Sync, Pause, Resume, Edit Config, Re-Auth, Health Check, and Search, all directly from the connector overview.
[Learn more →](/agent-platform/connectors/sharepoint)

Voice experience

Gemini Live: configurable context-window compression

For long voice conversations on Gemini Live, you can now configure context-window compression directly from the channel's configuration panel in Studio — turn compression on or off, set the token count at which it triggers, and the token count it compresses down to. This helps longer conversations retain relevant context without hitting the model's context limit.

Voice experience

Contact Centre lifecycle events from CCAI

The platform now derives Contact Centre lifecycle events (agent connected, agent disconnected, ACW completed) from CCAI's conversation\_info payload, removing the need for explicitly pushed event entries. Canonical `transfer_initiated` events are now emitted for voice SIP transfers. The `dispositionCode` and `wrapUpNotes` field mapping has been corrected, and close events now include queue and assignment fields.

Language-based voice channel routing

Callers can now select their preferred language at the start of a call (via DTMF, for example, press 1 for English, 2 for Spanish) and are routed to a language-specific voice channel with matching ASR/TTS settings. The selected language is retained for the entire session, preventing the agent from unexpectedly switching between languages mid-conversation.

Runtime-driven dynamic language switching

Callers can now switch languages mid-conversation with a simple spoken request (for example, "continue in French" or "speak Greek"), instead of only selecting a language once at the start of the call. Runtime validates and owns the language state, so switches only happen on clear, explicit requests — casual mentions like "I have a French account" won't trigger a change. The selected language is preserved consistently across handoffs, reconnects, and session resumption, and voice fillers (for example, "one moment") now play back in the caller's active language instead of defaulting to English. This also extends language-consistency support to Gemini-based realtime voice sessions, alongside the existing pipeline voice channels.

Dynamic Welcome Messages for Agent Assist

The Kore Agent Assist channel now renders templated ON\_START welcome messages using live session data. By initializing the session and running ON\_START setup routines (SET, CALL) upfront, the platform ensures authored responses populate with real values. Static welcome message configurations for agents remain fully supported and unaffected.

Transcript grouping timeout

Flow authors can now configure a transcript grouping timeout, controlling how long the platform waits before grouping transcript segments into a single turn.
[Learn more →](/agent-platform/channels/voice#set-up-pipeline-voice-channel)

Gemini 3.8 Live voice model support

Gemini 3.8 Live is now available as a realtime voice model option.
[Learn more →](/agent-platform/supported-models#google-ai-models)

Governance

Deployment lifecycle audits

Studio now surfaces deployment lifecycle audits, giving visibility into the deployment process for governance and compliance. Deployment audit metadata now includes additional context, and catalog compatibility is preserved across audit entries.
[Learn more →](/agent-platform/administration/workspace-and-team#audit-logs)

Agent builder experience

Comment-only survey templates and simplified follow-up rules

Feedback templates in Studio can now be authored without a numeric rating, so you can collect comments on their own. Per-rating follow-up questions can also inherit their visibility rule from the survey's primary control, instead of being configured separately.

Voice experience

Per-turn TTS delivery policy for protected voice responses

Flow authors can now configure the `STREAMING` delivery mode for voice responses, alongside the existing `barge_in` control. This lets authors protect important spoken responses — such as compliance disclosures or handoff completion messages — from being interrupted, truncated, or affected by tool calls, reconnects, or mid-call capability changes.
[Learn more →](/agent-platform/abl/reference/flow#voice-configuration)

Voice experience

Amazon Nova Sonic speech-to-speech voice

Voice channels can now use Amazon Nova Sonic, a speech-to-speech (S2S) model served through AWS Bedrock, as a realtime voice provider. Configure it in Studio's Voice Services page, using either access-key or IAM-role-based authentication.
[Learn more →](/agent-platform/channels/voice#set-up-realtime-llm-voice-channel)

Vertex AI Gemini Live for realtime voice

Realtime LLM voice channels can now be powered by Vertex AI's Gemini Live models, in addition to the direct Google Gemini Live provider. Model resolution and credential handling for Vertex use the same Voice Services configuration flow as other realtime providers.
[Learn more →](/agent-platform/channels/voice#set-up-realtime-llm-voice-channel)

Native multilingual voice profiles

Voice channels now support per-language voice profiles: each supported interaction language can have its own ASR and TTS provider and voice selection, with the voice runtime handling automatic cutover and fallback. [Learn more →](/agent-platform/channels/voice#set-up-pipeline-voice)

Voice CSAT survey on the return leg

Voice transfers over SIP now support a post-agent CSAT survey played back on the call's return leg after a human agent completes the interaction, using a reserved `x-callee-number` SIP header to identify the transfer target.

GCP authentication for Vertex AI and Gemini

Auth profiles for Vertex AI and Gemini now support: * Google Cloud Workload Identity Federation (WIF): Authenticate without long-lived service-account keys. * GCP service-account authentication: Configure Vertex AI auth profiles with an uploaded GCP service-account key and test the connection.

Gemini Live JSON tool results and non-blocking tool fillers

Gemini Live channels now return tool results in JSON mode and can play non-blocking filler audio while a tool call is still in progress, reducing dead air during longer tool executions.

Warning on imported model selection

Studio now warns when a project selects an imported model that may not be fully compatible with the current configuration, so teams can catch mismatches before deployment.

Digital experience

Post-transfer CSAT survey

After an agent-to-agent transfer, the Web SDK can now present an end-of-transfer CSAT survey to the user, with responses captured through a new webhook survey resolver.

Governance

Realtime model visibility and health monitoring

Workspace administrators now have clearer visibility into which models are available for realtime voice interactions across projects. The platform also continuously verifies realtime model connectivity through an internal health-monitoring service, helping ensure configured realtime models are available for voice use.
[Learn more →](/agent-platform/administration/ai-configuration#llm-providers)

Agent builder experience

Per-server rate limits for MCP tools

Rate limits can now be configured per MCP server, with Studio controls to set the limit and automatic enforcement across tenants, so one server's traffic can't overwhelm the rest. [Learn more →](/agent-platform/tools/mcp#configure-connection-settings)

Automatic indentation validation and repair in the ABL editor

The ABL editor now detects indentation problems as you write and offers an auto-fix, with clearer diagnostics when something can't be repaired automatically.

Link eval suites to a version, deployment, or environment

Eval suites can now run against a specific project version, deployment, or environment. This helps teams determine whether a score change came from a project edit or another change, and evaluate what is actually deployed.
[Learn more →](/agent-platform/evals#evaluation-suite-components)

Workflow versions, settings, and a redesigned monitor experience

Workflows now support versioning and a dedicated settings tab, alongside a redesigned monitor experience in Studio with clearer run detail, timeline, status, outcomes, and payload views.
[Learn more →](/agent-platform/workflows)

Move workflows between projects

Workflows can now be exported from one project and imported into another, with a preview of any conflicts before the transfer completes.
[Learn more →](/agent-platform/workflows#export-and-import-workflows)

Customize webhook trigger request and response formats

You can now customize the structure of webhook trigger requests and responses. Use the default format or define a custom format for the request payload the workflow receives and the response payload it returns.
[Learn more →](/agent-platform/workflows#configure-triggers)

See why an agent isn't ready to run

Studio now surfaces structured readiness diagnostics before a chat starts, explaining what's blocking execution — such as a validation issue or incomplete topology — instead of only failing at runtime.

Higher-fidelity execution token budget alerts

Studio's debug panel now warns when a run's execution approaches its token budget, with the limit enforced consistently from the compiler through the core parser — so you catch runaway prompts during testing instead of in production.

Redesigned architecture review experience

The Arch AI architecture review now shows staged review progress as it works, clearer proposal summaries, and suggested next steps once you create a review — making it easier to follow what Arch AI checked and what to do next.

Import and export projects using a public API

Projects can now be imported and exported using a public API authenticated with a platform key and processed asynchronously. This helps external systems and pipelines move projects across deployments without using the Studio.
[Learn more →](/agent-platform/api-reference/project-export-get)

Improved model resolution

Model resolution now supports non-reasoning models for named-entity recognition tasks, with additional provider options and an improved resolver for query-time model selection.

Agent tables: durable structured data store

Agent tables are a new structured data store you define in Studio, which agents read and write at runtime through a built-in table tool. Built on Postgres, tables are a durable, typed, queryable store for state that needs to persist and be shared across channels and sessions, rather than living only inside a single conversation.

Knowledge base

Scheduled web crawls for SearchAI

SearchAI can now crawl configured websites on a recurring schedule, not just on demand, with a Studio panel to set up and manage crawl schedules. [Learn more →](/agent-platform/knowledge#web-crawl)

Exact SharePoint site targeting

The SharePoint connector for SearchAI lets you scope to specific sites via SharePoint's ‘Sites.Selected’ permissions, featuring a Studio interface for direct site selection rather than requiring broader site-collection access. [Learn more →](/agent-platform/connectors/sharepoint)

Voice experience

Richer handoff context for ServiceNow, Five9, and Salesforce agent transfers

Agent transfers now resolve handoff metadata at runtime and project it into SIP headers for ServiceNow, with matching adapter support for Five9 and Salesforce, so more conversation context reaches the receiving system. [Learn more →](/agent-platform/escalate#routing-configuration)

SIP termination URI support for BYOC voice provider

Bring-your-own-carrier voice channels can now be configured with a SIP termination URI, including provisioning and cleanup handled from Studio.

Author realtime voice delivery policy directly in Studio

Studio now lets you author the delivery policy for realtime voice sessions — controlling how responses are delivered as the conversation happens — directly in the agent editor, instead of only through the underlying ABL definition.

Reference SIP data in voice transfer destinations

Voice call transfer destinations can now include `{{sip.}}` placeholders that resolve to SIP data at runtime, with validation to make sure only the permitted values are forwarded.
[Learn more →](/agent-platform/abl/reference/tools#trusted-sip-transfer-destinations)

Scoped barge-in control for voice interruptions

Barge-in behavior (whether a caller can interrupt the agent mid-response) can now be scoped to an individual step or flow instead of only a project-wide setting, giving you finer control over which parts of a voice agent are interruptible.

Outbound dialing from voice agents

Voice agents can now place outbound calls, not just receive them, using a new public API that triggers a call on demand over telephone or SIP through a configured voice channel. Each call is tracked with an ID and logged for later review.

Support for phone numbers with a leading plus sign

BYOC voice channels now accept E.164 phone numbers with a leading plus sign by default, closing a gap where such numbers could be rejected.
[Learn more →](/agent-platform/administration/agent-behavior-settings#voice-gateway)

Choose an OpenAI speech-to-text model for voice channels

Voice channel configuration now lets you select which OpenAI speech-to-text model to use, in addition to the existing provider options.
[Learn more →](/agent-platform/supported-models#supported-ai-models)

Bring your own custom speech provider

Tenants can now register and use their own custom speech provider for voice channels, alongside the platform's built-in speech provider options.
[Learn more →](/agent-platform/administration/ai-configuration#byo-custom-speech-providers)

On-premises Deepgram support for voice channels

Voice channels can now connect to an on-premises Deepgram deployment using shared gateway speech credentials, instead of only Deepgram's cloud endpoint.
[Learn more →](/agent-platform/channels#voice)

Connect greeting audio now plays by default

You can now upload and manage the greeting audio callers hear when a voice channel connects, with a preview before publishing. New deployments now play a connection greeting by default, rather than requiring you to enable it.

Filler noise for more natural-sounding voice agents

Voice agents can now play configurable filler noise, such as ambient background sound, with its own playback settings, so pauses feel less abrupt.

Speech-to-Speech (S2S/V2V) model support

Speech-to-speech (S2S) voice models now go through the same Model Hub selection path as every other model, instead of being locked to workspace-level Voice provider settings. This enables per-project keys and configuration for S2S, matching text and speech-to-text models.

Digital experience

Verified user identity for Slack channel connections

Slack channel connections can now enforce a policy requiring verified Slack user identity before a conversation proceeds, configurable from Studio.
[Learn more →](/agent-platform/channels/slack)

Improved async handoff for AI for Work channels

AI for Work (AI4W) channel connections now support overriding activity visibility for streamed events, with an improved resumption service for conversations that hand off asynchronously.

Consent support for omnichannel live sessions

The embedded web SDK widget now supports collecting user consent during live, omnichannel sessions, improving the handoff experience across channels.
[Learn more →](/agent-platform/administration/agent-behavior-settings#omnichannel)

New channel: Exchange Graph Direct email

Email is now supported as a deployment channel through Microsoft Exchange Graph, with mailbox management, message erasure support for compliance, and a dedicated channel editor in Studio.

Upload attachments without presigned URLs

File attachments can now be uploaded as base64 content and delivered via a signed URL, giving developers an alternative to handling presigned URLs directly.

Insights

Full feedback survey templates

You can now build feedback surveys from templates with configurable scoring, condition rules, and localization, and view the results through a dedicated analytics dashboard. Agents can also collect surveys by voice, and a public API exposes them.
[Learn more →](/agent-platform/feedback-template)

PII controls for custom analytics dimensions

PII policy can now govern custom analytics dimensions. The platform enforces the policy wherever it writes or displays that data, and a new Govern PII card in Studio shows policy status.
[Learn more →](/agent-platform/pii-protection#custom-dimensions)

Filter sessions by end-user phone number

Use the caller phone number filter to trace a specific caller's history across sessions, rather than searching session by session.
[Learn more →](/agent-platform/sessions#session-identification-and-metadata)

Custom date ranges for Sessions and Traces

The Sessions and Traces pages now support a custom date range picker, matching the one already available on the Insights page.

New session and KPI metrics

Analytics now includes a unique-sessions metric with support for ungrouped aggregates, and workspace-level KPI weighting and error-rate calculations are now more accurate.

Governance

Workspace-level controls for model access

Workspace administrators can now audit and enforce which AI models a tenant can use, with idempotent controls to manage model access consistently across a workspace.
[Learn more →](/agent-platform/administration/ai-configuration#model-catalog)

Smoother SSO invite and lockout recovery

Pending workspace invites are now accepted automatically when a user signs in through SSO, instead of requiring a separate acceptance step, and memberships locked due to a lockout are automatically restored once the lockout expires.
[Learn more →](/agent-platform/administration/workspace-and-team#security-and-compliance)

OAuth2 Resource Owner Password Credentials auth profile

Auth profiles now support the OAuth2 Resource Owner Password Credentials grant type, available consistently across the runtime, compiler, and Studio, for integrations that need this flow instead of an authorization-code or client-credentials grant.

External secrets via Azure Key Vault

You can now connect an external Azure Key Vault as a secret source, with runtime routing to resolve secrets from it and a Studio UI to connect and browse available secrets, instead of storing every credential directly in the platform
[Learn more →](/agent-platform/administration/workspace-and-team#azure-key-vault)

Guard against accidental encryption provider switches

A new safeguard prevents accidentally switching between platform-managed and bring-your-own-key encryption for a workspace's KMS configuration.

DTMF input can now be classified as PII

Keypad (DTMF) input during a voice session can now be explicitly classified as PII, with policy enforcement applied the same way it is for other PII types.
[Learn more →](/agent-platform/administration/general-settings#pii-redaction)

Organization-wide proxy egress

Organizations can now configure a proxy for all outbound traffic across the runtime, Studio, compiler, and editor, with built-in connectivity testing, policy invalidation when settings change, rate limiting, and observability into proxy behavior.
[Learn more →](/agent-platform/administration/workspace-account#proxy-configs)
This release includes bug fixes and platform improvements. This release includes bug fixes and platform improvements.

Agent builder experience

Support for Base64-encoded file attachments

Alongside the existing `multipart/form-data` upload path, the platform now accepts Base64-encoded attachments in `application/json` requests, using `filename`, `mimeType`, and `contentBase64`. Successful uploads can return a short-lived signed delivery URL. If the delivery URL expires, you can request a new one without re-uploading the file.

Workflow Agent node accepts attachment IDs

The Agent node's canvas attachments field now accepts attachment IDs directly, in addition to https URLs, so a workflow can hand an already-uploaded attachment to an agent node without re-hosting it as a URL first.
[Learn more →](/agent-platform/workflows#agent-node)
This release includes bug fixes and platform improvements.

Agent builder experience

Artemis CLI

A new customer-facing CLI is now available, letting developers automate the full Agentic lifecycle, including authentication, project sync, eval runs, and deployment, from external systems, without Studio.
[Learn more →](/agent-platform/cli)

Microsoft Foundry model catalog support

Microsoft Foundry is now available as a unified provider, replacing the previous Anthropic-only integration. Select models from multiple publishers, including OpenAI, Meta, Mistral, Cohere, and Anthropic, or enter custom deployments. The configuration also supports Azure AD Service Principal authentication with automatic token discovery.

Recurring evaluation run schedules

Eval runs can now be scheduled to recur daily, weekly, or monthly, with time zone selection and flexible start and end conditions.
[Learn more →](/agent-platform/evals#configure-run-settings)

Exact timestamps on eval runs

Eval runs in Studio now display exact start and completion timestamps alongside the existing relative time labels, across Studio.

Unsaved-changes warning in the ABL editor

Studio now warns you before you lose unsaved work when leaving the agent editor, with the option to save, discard, or stay.

In-app Help Center and first-time user welcome experience

Studio now includes a built-in Help Center with guided documentation, a platform primer, feature highlights, and support resources. It also offers a unified welcome screen for first-time users with quick-start actions—creating a project, using a template, connecting Artemis MCP, inviting team members, and accessing docs and support—reopenable anytime from the profile menu.

Knowledge base

Improved OCR and language handling for SearchAI document extraction

* SearchAI now automatically detects a document's language and selects the corresponding RapidOCR model, rather than relying on a single fixed model regardless of script. This improves results for Arabic, Thai, Urdu, and Latin-script scans. * Mixed-language documents are kept in their source language rather than translated. * SearchAI detects pages with unusable or sparse text (scanned images, glyph-name artifacts, collapsed text, or vertical right-to-left output) and runs a full-page OCR fallback to recover readable text. * Explicit language and OCR-language settings now carry through the SDK, connectors, and every extraction path (sync, async, split-PDF, streaming URL, extraction-only), so configured language hints are honored consistently. * Automatic OCR now runs only when a PDF's embedded text layer is genuinely unusable, is disabled for pure-text formats by default, and caps fallback retries rather than retrying indefinitely. * A visual enrichment or OCR failure no longer causes the whole document to fail — extraction continues downstream with any usable text.

Connector enhancements: Custom base URLs and OAuth authentication

Multiple connectors now support custom base URLs and expanded OAuth 2.0 Client Credentials authentication, enabling connections to on-premises deployments and sovereign cloud tenants instead of relying only on public cloud endpoints: * Jira: Custom base URL and product-version selection for Jira Data Center deployments * Confluence: Custom base URL, OAuth 2.0 Client Credentials authentication, and product-version selection * SharePoint: Custom base URL and OAuth 2.0 Client Credentials authentication through custom Azure AD endpoints, including sovereign clouds such as .de * ServiceNow: Custom base URL, OAuth 2.0 Client Credentials authentication, and product-version selection * Salesforce: OAuth 2.0 Client Credentials authentication for non-interactive, scriptable authentication * Microsoft 365 (Excel): OAuth 2.0 Client Credentials authentication for non-interactive Microsoft 365 authentication

Voice experience

Voice Playground for softphone-based voice testing in Studio

Playground now supports voice testing alongside text testing, allowing developers and QA teams to initiate a softphone interaction, speak with the agent, view the live transcript and call state, and open the session in Observatory for detailed voice debugging. [Learn more →](/agent-platform/testing)

Voice call transfer and escalation to a live agent

The Escalation block in Studio now supports voice call transfers, in addition to the existing chat hand-off to an agent desk. Voice calls can be escalated to a human agent by integrating the call\_transfer tool with the ESCALATE block; previously, escalation supported only chat-based handoff. Developers can choose a call transfer type (SIP, PSTN, or relay) and a transfer method (invite, refer, or bye/release-link) directly in the Escalation editor, to prevent failure messages from being announced twice. [Learn more →](/agent-platform/escalate) Voice agents can also transfer calls to external phone numbers, not just SIP endpoints, by configuring a PSTN phone number as an agent transfer destination in the project voice settings. [Learn more →](/agent-platform/administration/agent-behavior-settings#voice-gateway)

Genesys Cloud Audio Connector for voice bots

Voice calls can now connect to Genesys Cloud through the Audio Connector, using the AudioHook protocol, with support for both Pipeline and Realtime voice modes.
[Learn more →](/agent-platform/channels/genesys-audio-connector)

Configure language-specific TTS profiles for multilingual voice agents

Voice Pipeline channels can now define an explicit TTS profile for each configured language, including provider, voice, model, output format, pace, and temperature. When the interaction language changes, the agent switches to the configured profile for that language, instead of inferring a voice or provider from the default configuration. [Learn more →](/agent-platform/channels/voice#set-up-pipeline-voice)

Amazon Connect live-agent transfer via SIP BYE with generated conversation summary

Voice calls routed from Amazon Connect to an Artemis agent can now be transferred to a live agent, along with a generated conversation summary. The platform then releases the bot's call leg via SIP BYE, allowing Amazon Connect to resume its contact flow and route the caller with context intact.
[Learn more →](/agent-platform/administration/agent-behavior-settings#voice-gateway)

Validation of STT timeout configuration against provider-specific limits

Voice channel configuration now validates the speech/segmentation-silence timeout against the selected STT provider's actual supported range (for example, Microsoft's 100–5000 ms), instead of accepting any positive value, and rejects invalid values rather than silently clamping or forwarding them.

Forward custom outbound SIP headers on call transfer

Projects can now configure a set of allowed outbound SIP headers to forward during voice call transfers. Administrators select which predefined headers (for example, x-queue, x-skill) a project permits, and agents can attach values for those headers when initiating a SIP REFER or INVITE transfer. Enforcement, sanitization, and drop telemetry are applied at runtime. [Learn more →](/agent-platform/sending-custom-sip-headers)

Multi-gateway BYOC SIP support

BYOC (Bring Your Own Carrier) voice channels now support configuring multiple SIP gateway IP addresses and CIDR ranges, replacing the previous single-IP field. Each entry is provisioned as its own Jambonz SIP gateway, with full lifecycle management for create, update, rollback, and cleanup.

Digital experience

Genesys Cloud Open Messaging integration for digital channel agent handoff

Conversations on Web Chat, WhatsApp, and other messaging channels can now be handed off to a live agent through Genesys Cloud.
[Learn more →](/agent-platform/channels/genesys-open-messaging)

Add Support for Microsoft Teams and Copilot channel

Microsoft Teams and Copilot are now supported as a new deployment channel. Users can create a Teams channel instance from the Studio deployments screen, configure credentials and identity policies, and have the runtime enforce a verified Teams identity on inbound messages.
[Learn more →](/agent-platform/channels/ms-teams-copilot)

File attachment support in the API channel and web SDK chat widget

The file attachment option in the chat widget can now be shown or hidden per project, and the API channel now passes along caller-supplied context to the agent.
[Learn more →](/agent-platform/channels/web-sdk-channel)

Auto-disconnect idle chat widget sessions

The embedded chat widget can now be configured to automatically disconnect or end a session after a period of user inactivity.
[Learn more →](/agent-platform/channels/web-sdk-channel)

Insights

One place to see every LLM call

A new Generations view shows every model call, from Studio, search, and live traffic, in one place, with cost, latency, and token usage.
[Learn more →](/agent-platform/analytics-insights/analytics-explorer#generations)

Filter analytics by environment and channel

Analytics dashboards is now filetered by environment (development, staging, production) and channel, so testing data doesn't get mixed in with production numbers.
[Learn more →](/agent-platform/analytics-insights/analytics-explorer#narrow-the-list)

See who gave feedback

The feedback table and exported reports now show which user submitted each piece of feedback, plus any custom tags.
[Learn more →](/agent-platform/analytics-insights/customer-voice#feedback-table)

Public analytics APIs for sessions, traces, and LLM usage

External systems can now pull conversation history, session and session-summary reporting data, messages, execution traces, and LLM cost and usage ledger data directly through public APIs.

Governance

Simpler guardrail threshold configuration and scanner configuration

Guardrail policies now provide a simpler way to configure detection thresholds at the project level. [Learn more →](/agent-platform/safety-and-guardrails#guardrail-policies)
Workspace administrators can configure guardrail providers and their available scanners, while project authors can select which configured providers the project can use. Guardrail providers also support a wider range of classifier response formats. [Learn more →](/agent-platform/administration/ai-configuration#guardrail-providers)

Entity-based PII detection and anonymization

PII detection and anonymization now support entity-based detection. Authors can create PII patterns using entity recognition (built-in or model-backed) instead of regex. An ignore-values list lets authors exclude known values from detection and reduce false positives.
[Learn more →](/agent-platform/pii-protection#custom-patterns)

Auth Profile fields to reference deployment environment variables

Project Auth Profile fields — Token URL, Refresh Token URL, Scopes, Client ID, and Client Secret — can now be populated from environment variables defined under Deployments, instead of requiring these values to be entered directly into the Auth Profile.

OAuth and PKCE support for remote MCP servers

Remote MCP server authentication profiles now support OAuth configuration, including authorization, token, and refresh URLs, scopes, and PKCE. PKCE can be enabled with the S256 challenge method.

Simpler Azure encryption setup

Customers using their own Azure encryption keys no longer need to register an app in their Azure tenant — the platform now manages that identity, and key rotation is simpler. This also adds support for additional compliance certifications (GDPR, SOC2, NIST, FIPS-140-3).
[Learn more →](/agent-platform/administration/workspace-and-team#policies)

Voice

Author SIP transfer destinations directly in the agent DSL

You can now declare a trusted SIP transfer destination directly on the built-in `call_transfer` tool, instead of relying solely on the project-level transfer target. The destination is authored as a non-model default (`default_source: config`), so it's never visible to or fillable by the LLM. The compiler validates it at publish time — catching blank values, unresolved templates, and malformed SIP URIs before an agent goes live.
[Learn more →](/agent-platform/abl-reference/tools#trusted-sip-transfer-destinations)

Configure a minimum STT confidence threshold

You can now set a project-level minimum speech-to-text (STT) confidence threshold for voice interactions. Transcripts below the threshold are treated as no input, preventing low-confidence speech recognition results from triggering agent responses. Set the threshold between 0 and 1; set it to 0 to disable it.
[Learn more →](/agent-platform/administration/general-settings#runtime-configuration)

Tools

Build dynamic batch payloads with HTTP body templates

HTTP tools now support `{{#each input.X}}` in body templates, letting you dynamically generate batch payloads from array inputs in a single request. You can also use session secrets in the generated payload, while keeping secret values protected from the model.
[Learn more →](/agent-platform/abl-reference/tools#reuse-secrets-across-tools-secretref)

Security and Compliance

Simplified Azure BYOK setup with a platform-owned identity

Azure Bring-Your-Own-Key (BYOK) encryption for same-tenant Azure Key Vault no longer requires customers to register or maintain their own Entra app registration or a federated credential configuration. Artemis now uses a platform-owned Workload Identity Federation application — customers grant it a scoped Key Vault role and provide their key location, with no client ID, secret, or certificate ever entered or stored.
[Learn more →](/agent-platform/administration/workspace-and-team#azure-key-vault)

New KMS compliance controls for BYOK

Workspaces using BYOK/KMS can now select a compliance level (GDPR, SOC2, or NIST), which the platform enforces at the point of any KMS configuration write. Key rotation is now split into two distinct, independently controlled actions — DEK retirement and KEK rewrap — each with its own preflight check before running.
[Learn more →](/agent-platform/administration/workspace-and-team#policies)
This release includes various updates to enhance the stability and resilience of agent handoffs, long-duration calls, and authentication flows, as well as improvements to conversation routing and recording playback. This update includes new features and enhancements summarized below.

Tools

Secure secret handling for tools and workflows

Tools can now return sensitive values — API keys, tokens, credentials — without exposing them to the LLM. These values are captured into an encrypted, session-scoped vault and kept invisible to the model and the conversation transcript. Use the new `{{_secret.X}}` syntax to restore a captured secret into a downstream tool's HTTP headers or request body.
[Learn more →](/agent-platform/abl-reference/tools#reuse-secrets-across-tools-secretref)

Voice

Configure a PSTN transfer target number

You can now configure a dedicated Public Switched Telephone Network (PSTN) transfer target number for agent-initiated call transfers, alongside the existing SIP transfer configuration. Outbound PSTN transfers now correctly present the provisioned caller ID.
[Learn more →](/agent-platform/administration/agent-behavior-settings#voice-gateway)

Channels

New API scope for closing sessions

Integrators using the API channel can now issue a platform API key scoped specifically to closing sessions (close-session), instead of requiring a full administrative key to terminate a conversation programmatically.
[Learn more →](/agent-platform/deployment#scopes)

Microsoft Teams: verified identity and thought streaming

Microsoft Teams is now supported as a verified-identity channel, with identity enrichment, auth-profile-based credential mapping, and a new channel setup experience in Studio. Teams conversations can also now stream the agent's thinking or progress updates in real time, with a new streaming toggle in the Teams channel configuration.
[Learn more →](/agent-platform/channels/digital#configuration)

Knowledge base

Box and Amazon S3 connectors

You can now connect Box and Amazon S3 as knowledge sources, making it easier to index and search content stored in these repositories. These connectors enable seamless ingestion of documents from your cloud storage into your knowledge base.
This update includes new features and enhancements summarized below.

Workflows

Set a default value for data entry fields in Workflows

Workflow builders can now set the default value of fields in a Data Entry node using either static text or expressions. Expressions can reference data from workflow triggers or previous workflow steps. For example, a reviewer's form can be pre-filled with an AI-generated draft for approval or editing.
[Learn more →](/agent-platform/workflows#data-entry-node)

SDK

Control attachment visibility in the Web SDK

The Web SDK Theme Builder now has a toggle to show or hide the attachment icon in the chat widget's compose bar. This lets FAQ and informational AI agents offer a cleaner, text-only interface when file uploads aren't needed, while keeping attachments enabled by default elsewhere.

Analytics

Control SQL query operations for public analytics widgets

Admins can now allow or restrict specific query operations for public-facing analytics dashboard widgets, from a new SQL Validation section in the Public Widget Data Policy settings. Supported restrictions include SQL comments, multiple SELECT statements, JOIN clauses, common table expressions (CTEs), set operations (UNION, INTERSECT, EXCEPT), and OR conditions.
[Learn more →](/agent-platform/administration/security-observability-settings#sql-validation)
Feature enhancements and new capabilities introduced in this release are summarized below, followed by the [breaking changes](#breaking-changes) that may affect existing projects — review the migration steps before recompiling or republishing them on 1.4.0.

Agent building (ABL/Studio)

Rich-content buttons can open a link on click

Message ACTIONS buttons and carousel card buttons can be configured with a URL so that clicking it opens the link directly in a new tab, instead of posting an action back to the agent. It is supported on the Web SDK/widget, Telegram, and LINE; other channels automatically fall back to a normal button.
[Learn more →](/agent-platform/abl-reference/rich-content#interactive-actions)

Git integration for agent projects

Developers can connect a Git repository to a project to sync agent definitions and localization assets with version control, including push and pull from the Studio editor and conflict-resolution tools for simultaneous edits.
[Learn more →](/agent-platform/administration/integrations-settings)

Inline ABL editor on the agent canvas

You can now edit agent logic directly in an inline code editor without leaving the visual canvas.

Voice

Configurable caller-silence ("no-input") handling for pipeline voice agents

Voice agents on the KoreVG/Jambonz pipeline can now set re-prompt messages, retry counts, and timeout windows for silent callers, with locale-aware prompts and a final action (hang up, custom message, or transfer).

Custom vocabulary for speech-to-text

You can tune speech recognition to recognize domain-specific terms such as product names and jargon. It leads to improved accuracy in transcriptions.

Filler-agent context and language routing for IVR

Filler ("thinking") audio during voice calls now carries conversation context and follows the caller's active language.

No more silent gaps during backend tool calls

Updates to filler and no-input handling with Google Gemini Live now reduce dead air, repeated speech, and lost language context while a voice agent waits for a backend tool call. The updates require no configuration change.

Channels

Microsoft Teams: user-identity and channel-template support

Microsoft Teams connections can now resolve caller identity and add user details like email address, display name, or user ID to the context.
[Learn more →](/agent-platform/channels/ms-teams-copilot)

HTTP Async (webhook) channel: interruption and streaming support

The HTTP Async channel now supports mid-response interruption and streamed delivery of agent responses.
[Learn more →](/agent-platform/channels)

Multiple message rendering

Agents can now send several distinct messages in one turn, each rendered as a separate bubble instead of one combined block.

APIs and SDK

Agent transfer uses webhook channel now. Agent transfer supports transfering attached documents. The allowed file formats and file size limits are defined in project-level attachment settings. SDK sessions now receive a runtime-controlled `activityVisibilityPolicy` that decides updates of which customer-facing activity to show. It lets you control fillers like `Searching...`, safe thought summaries, action progress, system notices, and redacted tool previews. Raw reasoning, secrets, and unsafe tool data is hidden.

Analytics and Insights

Public API for saved dashboard-widget data

External tools, BI connectors, and scheduled exports can now fetch the data behind any saved custom-dashboard widget through a public REST endpoint, without re-implementing its underlying query. It supports both synchronous requests and asynchronous jobs with polling and cursor-based pagination.

PII entity preview

You can now preview which entities in a conversation will be detected and masked as PII before publishing an agent.

Integrations and Single Sign-on

Workspace sign-up, SSO, and access requests have new controls

Tenantless users are now routed to onboarding instead of being left in an ambiguous state, workspace SSO is separated from org SSO, and sign-up or workspace creation can be gated by feature flag. Review these settings after upgrading, especially if your org uses SSO or gated sign-up. Invited and password-recovery SSO users are now routed through separate flows, and Azure SSO app selection and Azure BYOK metadata are represented as structured fields instead of opaque JSON — review these if your workspace uses Azure SSO or BYOK.

Workflows

Workflow enhancements

Workflows now have better support for array-typed inputs/outputs, per-workflow rate limits, scheduled (cron) triggers, a JSON step editor, and an improved workflow overview page.
[Learn more →](/agent-platform/workflows)

LLM and Model Configuration

Adaptive thinking config in Anthropic models

The platform now supports adaptive thinking as a configuration for Opus 4.7 and Opus 4.8 models.

Breaking changes and what to check before moving to 1.4.0

The changes below affect existing projects when you move to 1.4.0. Review the items relevant to your role [developer](#agent-developers) or [admin](#administrators), and resolve them before promoting or republishing projects on the new version.

For agent developers

**Supervisor role is now explicit** `canRespondDirectly` is deprecated as the primary way to set supervisor behavior. New agents should declare `BEHAVIOR: role: router` (route only) or `role: orchestrator` (combine results and respond). Existing agents keep working through compatibility mapping, but a role that conflicts with the old boolean setting now fails to compile. ```yaml expandable=true theme={null} Before (1.3.3): SUPERVISOR: Benefits_Router canRespondDirectly: false HANDOFF: - TO: Claims_Agent WHEN: intent.category == "claims" After (1.4.0): SUPERVISOR: Benefits_Router BEHAVIOR: role: router HANDOFF: - TO: Claims_Agent WHEN: intent.category == "claims" ``` **Routers can no longer own returned work** A router can only select the next agent. If the parent needs to use a child agent's returned result, declare it an orchestrator instead. Agents that use `DELEGATE` inside a router will no longer compile. ```yaml expandable=true theme={null} Before (1.3.3) — DELEGATE inside a router, now a compile error: SUPERVISOR: Benefits_Router canRespondDirectly: false DELEGATE: - TO: Plan_Lookup_Agent WHEN: intent.category == "benefits" RETURNS: plan_summary: object After (1.4.0) — declared as an orchestrator instead: SUPERVISOR: Benefits_Coordinator BEHAVIOR: role: orchestrator DELEGATE: - TO: Plan_Lookup_Agent WHEN: intent.category == "benefits" RETURNS: plan_summary: object INSTRUCTIONS: | Use plan_summary to answer the member. Do not expose unrelated session memory. ``` **Compiler warnings can now block publishing** Diagnostics that used to be silently accepted — conflicting role and flag settings, routers containing DELEGATE, unmapped handoff inputs, unsupported tool array schemas — now stop a project from compiling or publishing. Review and resolve any outstanding warnings before promoting existing projects on 1.4.0. **Handoff and delegate inputs must resolve, or the call fails** A handoff or delegate no longer runs with a missing or stale input value. Add an explicit `MEMORY` read for any value the child needs, and use `ON_RETURN` to map back the results the parent depends on. For deterministic FLOW parents, `HANDOFF` with `RETURN: true` now suspends at the transition boundary and resumes from the saved next step once the child returns — review verifier, auth, eligibility, and multi-agent flows where a parent depends on a child's returned values. ```yaml expandable=true theme={null} Before (1.3.3): HANDOFF: - TO: Authentication_Agent WHEN: intent.category == "auth" RETURN: true INPUT: memberId: '{{member_id}}' After (1.4.0): MEMORY: member_id: type: string access: read HANDOFF: - TO: Authentication_Agent WHEN: intent.category == "auth" RETURN: true INPUT: memberId: '{{member_id}}' ON_RETURN: action: resume_intent map: authentication_status: result.authentication_status member_id: result.member_id ``` **Tools must declare the memory they read and write** A tool that needs conversation memory must declare `context_access.read` and/or `context_access.write`. If the declared memory isn't available, the tool doesn't run — check tools that previously assumed memory was always present. ```yaml expandable=true theme={null} Before (1.3.3): TOOLS: get_plan_information(memberId: string) -> { success: boolean, plans: array } type: http endpoint: "/eligibilitymembersinfo/query" method: POST After (1.4.0): MEMORY: authentication_status: type: string access: read member_plan_summary: type: object access: write TOOLS: get_plan_information(memberId: string) -> { success: boolean, plans: array } type: http endpoint: "/eligibilitymembersinfo/query" method: POST context_access: read: [authentication_status] write: [member_plan_summary] ``` **Declare the item type for array inputs** Workflow array inputs still work without an item type, but declaring one — for example `itemType: "string"` — is now recommended. Workflow and connector array parameters are also projected into the model-facing tool schema with an items definition in 1.4.0, so some model providers (for example Gemini/Vertex) can reject the whole tool declaration if a typed array doesn’t declare its item type. ```yaml expandable=true theme={null} Before (permissive array): { "name": "available_days", "type": "array", "required": true } After (typed array): { "name": "available_days", "type": "array", "itemType": "string", "required": true } ``` **Review PII detection settings** New projects start with no built-in PII detection enabled by default (`enabled_builtins: []`). If your agent relied on default PII protection, explicitly enable the detection types you need before publishing. **Re-check SearchAI metadata and title filters** Filters that were previously dropped or ignored — unknown standard filters, metadata filters, and title-only filters — are now honored correctly. Review saved knowledge base and tool filters, since results may return different, more accurate documents after upgrading. **Gather validation outputs are now canonical** FLOW `gather` validation and retry-exhaustion fields are now lowered into the canonical execution model instead of relying on display-only alias names or implicit retry fallbacks. Make gather validation outputs explicit and don't rely on a label as if it were a runtime variable name. **Terminal output is held until the final response is confirmed** Runtime now defers final output until response checkpoints pass, which cuts down on duplicate terminal responses and output arriving after cancellation — but it also surfaces incorrect output-ownership wiring earlier. Re-test multi-agent flows with async fan-out, remote handoff returns, delayed voice responses, or structured `COMPLETE` payloads without text. **Review agents that branch on channel or transfer state** Message metadata, channel identity, and transfer state now persist more consistently across reconnects, cancellation, escalation, and remote handoff returns. If an agent branches on channel identity or resumes after a remote or human-agent transfer, re-test it on 1.4.0.

For admins

**Recompile and re-import migrated projects** Projects moved between tenants or environments now go through stricter namespace validation before they can be published. Re-import or recompile any migrated projects on 1.4.0 and confirm there are no outstanding namespace warnings. **Validate workflows triggered outside of Studio** Start-input defaults and required-input validation for workflows are now applied server-side across every trigger path — cron, webhook, connector-polling, and agent-triggered — not just Studio's Run dialog. Validate promoted workflow versions on each of these trigger paths, not only by running them manually in Studio.

Voice

Improved Microsoft streaming TTS reliability

Voice channels using Microsoft streaming text-to-speech now provide more reliable voice playback following periods of silence, reducing interruptions and helping ensure agent responses continue to stream as expected.

New voice provider: Sarvam AI

Sarvam AI is now available as a native speech-to-text and text-to-speech provider with tenant-scoped credential management.
[Learn more →](/agent-platform/channels#voice)

Multi-agent and Arch AI

Real-time Arch AI activity feedback

The Arch AI activity panel now shows live, readable updates on each action as it happens, instead of delayed or raw output.
[Learn more →](/agent-platform/arch-ai)

Arch loop evaluation and optimization

Arch AI now supports an iterative eval-optimize loop, allowing agents to be evaluated and refined through repeated automated passes, including voice-agent evaluation scenarios.
[Learn more →](/agent-platform/evals)

Arch AI engine tracing

Arch AI now produces structured trace events for each internal step of agent generation, optimization, and evaluation, giving visibility into Arch's multi-step reasoning process for debugging and optimization.
[Learn more →](/agent-platform/arch-ai)

Voice diagnostics knowledge for Arch AI

Arch AI can now offer diagnostic guidance for voice issues like missing audio, TTS failures, or STT misconfiguration.

Persistent memory: Append, unique, and max-size modifiers

Agent memory (`REMEMBER STORE`) can now append values to arrays, with optional deduplication and a size cap, rather than overwriting them. This makes it easier to build agents that accumulate history, like preferences or past interactions, over time.
[Learn More →](/agent-platform/memory-and-state#append-to-an-array-accumulate-a-list)

Voice

Call recording

Voice agents now support call recording through the Kore Voice Gateway, with project-level recording configuration and enterprise recording policy enforcement. Recordings can be stored in Azure Blob Storage in addition to the platform's default storage.
[Learn more →](/agent-platform/administration/agent-behavior-settings#recording)

Outbound DTMF tones

Voice agents can now send outbound DTMF tones during a call, enabling interaction with IVR systems and other DTMF-driven telephony endpoints.

Genesys Cloud integrations

Artemis now integrates with Genesys Cloud on two fronts: the **Audio Connector** for live voice calls routed from Genesys Cloud, and **Open Messaging** for digital/text conversations routed through Genesys Cloud's contact center.
[Learn more →](/agent-platform/channels)

Voice transcript sync and pre-transfer history on handoff

When a voice agent hands off a call to Kore SmartAssist/CCAI, the conversation transcript is synced to the receiving system, and a new API exposes the full pre-transfer conversation history, giving the receiving agent complete context.
[Learn more →](/agent-platform/transfer-sessions)

Voice agent transfer via Kore Voice Gateway

Voice agents can now transfer live calls via SIP through the Kore.ai Voice Gateway, carrying conversation context to the transfer target.
[Learn more →](/agent-platform/channels/voice)

Custom vocabulary and keyword boosting for speech-to-text

Voice channels now support custom vocabulary and keyword boosting for speech-to-text providers, improving recognition accuracy for domain-specific terms — configurable at the project level and per deployment channel.

Locale-aware agent responses

Reasoning agents can now reference project locale assets directly in their ABL responses, enabling multi-language agent output driven by the session locale without hardcoding translations.
[Learn more →](/agent-platform/administration/general-settings)

Channel-appropriate response formatting

A single reasoning agent can now reliably produce channel-appropriate output—Markdown formatting for web and natural spoken text for voice—from one agent definition, without needing separate agents per channel.

Workflows and integrations

SOAP/WSDL integration support

Workflows and tools now support SOAP integrations: upload a WSDL file, browse its available operations, and register them as SOAP-type HTTP tools for agents and workflows — with a dedicated WSDL asset repository that supports versioning.

Integration actions as agent tools

Pre-built connector actions (for example, Jira or Slack operations) can now be added directly as agent tools — pick a connector and action from a catalog, and the platform handles authentication and parameter mapping automatically.

Webhook transport for contact-center handoff

Artemis now supports a webhook-based transport to hand off conversations to CCAI/SmartAssist and exchange ongoing customer messages during a live escalation.
[Learn more →](/agent-platform/escalate)

Knowledge base and Search AI

New packaged connectors: Confluence, ServiceNow, SFTP, and Google Drive

The knowledge base connector catalog now includes packaged connectors for Confluence Cloud (with permission-aware delta sync), ServiceNow, SFTP, and Google Drive (with folder-level selection), in addition to existing custom HTTP connectors.
[Learn more →](/agent-platform/connectors/custom-connector)

Custom script pipeline stage

SearchAI ingestion pipelines now support a custom 'Transform Script' stage, letting authors write sandboxed JavaScript to transform document content and metadata during ingestion.

Search query debugging controls

The knowledge base Search Test page now includes independent toggles to disable vocabulary resolution and query preprocessing, making it easier to isolate and debug individual stages of the query pipeline.
[Learn more →](/agent-platform/knowledge)

Knowledge base export in reusable agent modules

Reusable agent modules can now include knowledge base content (indexes, connector configuration, and source metadata) when published, so consumer projects reference the source module's indexed content without duplicating storage.
[Learn more →](/agent-platform/projects)

Security, identity, and access

Workspace-aware enterprise SSO

Enterprise SSO now supports workspace-level configuration, allowing each workspace to maintain its own SAML/OIDC authentication settings independently of the organization-level SSO configuration, with new admin governance controls.
[Learn more →](/agent-platform/administration/workspace-and-team)

Custom roles

Workspace owners and admins can now create custom roles with granular permissions beyond the built-in role set and assign them to project members for fine-grained access control.
[Learn more →](/agent-platform/administration/workspace-and-team)

Expanded Azure Key Vault BYOK authentication options

Bring Your Own Key (BYOK) with Azure Key Vault now supports three authentication methods: typed credential fields (replacing raw JSON configuration), certificate-based service-principal authentication with a pre-save connection test, and Workload Identity Federation for secretless, cross-tenant access.
[Learn more →](/agent-platform/administration/workspace-and-team)

Per-tenant Platform-Managed key governance

Platform administrators can now view and manage Platform-Managed (Kore-owned) encryption key configuration on a per-tenant basis, with server-side enforcement and audit logging of key lifecycle events.

Custom PII redaction with capture-group backreferences

Custom PII redaction patterns now support regex capture-group backreferences (`\1` / `$1`), enabling partial redaction that preserves non-sensitive parts of matched text — for example, masking all but the last four digits of an account number.
[Learn more →](/agent-platform/administration/security-observability-settings)

Analytics and insights

Output-item analytics

A new analytics subsystem captures structured data items extracted from agents' final responses and includes a Studio authoring UI for defining output-item analytics configurations.

Enhanced failure alerting

Aggregate live-session failure alerts now include failure-type summaries.

Custom metrics and dimensions for workflow analytics

Workflow analytics now support custom metrics and dimensions, allowing teams to define and query workflow-specific measurements throughout the analytics pipeline.
[Learn more →](/agent-platform/workflows)

Dashboard data export

Analytics dashboard widgets can now be exported as CSV directly from the Studio UI for offline analysis and reporting.

Export feedback

Collected user feedback (ratings, comments, and session references) can now be exported as a .csv file from the Insights → Feedback page for offline analysis.

Per-widget dimension filters

Custom Dashboard widgets now support per-widget dimension filters, allowing different widgets on the same dashboard to apply independent custom-dimension filters.
[Learn more →](/agent-platform/analytics-insights/custom-dashboards)

Administration

Vertex AI via GCP service-account authentication

The platform now supports Google Cloud Vertex AI models — both managed (MaaS) and dedicated-endpoint deployments — using GCP service-account credentials with automatic token refresh, configurable directly in Studio.
[Learn more →](/agent-platform/supported-models)

Auth profiles can reference environment variables

Auth profile fields can now reference project environment variables (for example, `{{env.CLIENT_SECRET}}`) instead of hardcoding credentials, so auth profile definitions can be shared safely across environments.

Usability improvement

Active workspace indicator

The account menu now displays the name of the currently active workspace, making it clear which workspace you're operating in when switching between multiple workspaces.

Public-key JWE authentication for Hosted Exchange Web SDK

Hosted Exchange Web SDK now supports public-key JWE authentication, enabling secure authentication using customer-managed public keys while maintaining compatibility with existing authentication flows.
[Learn more →](/agent-platform/sdk/web-sdk-end-to-end-auth-setup)

Environment-specific SAML Entity IDs

Each deployment environment now uses a unique SAML Service Provider (SP) Entity ID, allowing administrators to configure separate SAML applications within their identity provider (IdP) for development, staging, and production. Existing SAML SSO configurations must be updated with the new SP Entity ID after upgrading to this release.

Arch AI enhancements

Arch AI now provides a more reliable experience for authoring, generation, and evaluation. Improvements include support for larger source documents, more reliable preservation of handoffs across multiple sources, refined validation to reduce unnecessary generation failures, and non-blocking diagnostics to help identify issues without interrupting the generation process.

Language-aware ON\_START messages

The Web SDK session interaction language is now accessible within the `ON_START` event, enabling language-specific welcome messages without requiring workflow-based workarounds. This simplifies multilingual agent design, reduces unnecessary latency, and allows startup logic to use standard ABL conditional expressions.

Multi-agent orchestration

Enhanced delegation and handoff workflows

Multi-agent workflows now support both returning and non-returning delegation patterns, enabling more flexible interruption, escalation, and transfer scenarios. `DELEGATE` and `HANDOFF` behaviors are now distinguished, making it easier to implement workflows where conversations either return to the originating flow or are permanently handed off to specialized agents.

Smarter Supervisor direct responses

Supervisors can now respond directly to user requests by default, reducing unnecessary routing between agents. This ensures delegated responses are returned to users while still allowing Supervisors to operate as routing-only orchestrators when needed via the `canRespondDirectly` setting.

Web SDK

Web SDK multi-language UI customization

The Web SDK chat widget now supports language-based customization of UI elements such as chatbot names and typing hints, making it easier to support multilingual deployments.

Simplified Web SDK integration

Web SDK deployments can now be configured with public API keys and origin allowlists, eliminating the need to generate per-channel embed tokens and simplifying setup with greater control over embedded deployments.

Web SDK distribution via NPM and CDN

The Web SDK is now available through npm and CDN distribution channels, making it easier to integrate into modern web development workflows and deployment pipelines. The SDK is available [here](https://www.npmjs.com/package/@koredev/artemis-web-sdk).

Voice

Enhanced SIP header support for voice channels

SIP header information from inbound and transferred voice calls is now available, enabling routing and business logic based on telephony metadata — including queue information, customer identifiers, language preferences, campaign data, and transfer context.

Security and compliance

OCI Vault support for Customer-Managed encryption keys

Bring Your Own Key (BYOK) now supports Oracle Cloud Infrastructure (OCI) Vault Key Management Service (KMS), in addition to AWS KMS, Azure Key Vault, Google Cloud KMS, and external REST-compatible KMS, providing greater flexibility to manage customer-controlled encryption keys and compliance requirements.

Search AI and knowledge management

Enhanced Search Tool configuration

Search Tool parameters, such as filters and retrieval settings (for example, topK), can now be configured as mandatory or optional, providing greater control over knowledge retrieval behavior and ensuring that required parameters are consistently applied during tool execution — improving retrieval accuracy and reducing reliance on prompt-based instructions.

Custom HTTP connector for knowledge bases

Content can now be ingested into a Knowledge Base from custom HTTP-based data sources. The connector supports scheduled synchronization, schema discovery, field mapping, and secure connectivity options including mTLS, making it easier to integrate proprietary enterprise systems and external data sources with RAG applications. Ingestion workflows also support JavaScript-based content transformations, allowing data to be preprocessed before chunking and indexing.

AI engineering tools

Expanded model support

The Platform now supports Anthropic's Claude Opus 4.8 model.
[Learn more →](/agent-platform/supported-models)

Other improvements

SSO enhancements

The platform now auto-generates SAML and OIDC endpoint URLs for identity provider configuration. These system-managed URLs ensure consistent setup, reduce manual errors, and are available only to organization owners and admins.
[Learn more →](/agent-platform/administration/workspace-and-team#single-sign-on-sso)
This release includes bug fixes and usability improvements across the platform. This release includes bug fixes and usability improvements across the platform.

Updates to Web SDK experience

Improved the Web SDK experience with welcome-message lifecycle enhancements, theme refinements, markdown rendering improvements, and footer-link fixes. Enhanced the Web SDK session handling by preserving and securely resuming idle sessions instead of recreating conversations.

Updates to SearchAI and knowledge management

Simplified the SearchAI Knowledge tool DSL, improved knowledge-tool binding and resolution behavior, and enhanced structured-data processing and knowledge ingestion reliability.

Updates to tool and gather definitions

Added support for the `effect` and `provides` properties in AgentTool definitions and the `satisfiedBy` property in GatherField definitions.

Credential management updates

Introduced Config Variables (`{{config.X}}`) and Auth Profiles as the new credential management model, replacing Tool Secrets and the Tool Secrets Management API.

New memory and handoff capabilities

Added support for the `per_activation` reset mode for session variables. Enhanced handoff workflows with `ON_RETURN.set`, `ON_RETURN.map`, and typed handoff history support.

Validation and tool execution enhancements

Added batched LLM validation for fields using `VALIDATION_PROCESS: LLM` and clarified `last__result` storage behavior, including `store_result` semantics.

Project import and tooling improvements

Added support for tool-stub synthesis during project bundle import, allowing tool definitions to be generated from inline agent `TOOLS:` signatures and re-exported automatically.

ABL syntax and field updates

Updated the ABL expression syntax with method-call operators such as `input.contains()`, `.matches()`, and `abl.is_number()`. Updated the human-agent escalation fields to use `user.wants_human_agent`.

Updates to filler messages

Enhanced Filler Messages with contextual filler history support. Administrators can now configure how much recent conversation history and how many previously generated filler messages are used when generating new filler responses. The filler prompt also supports the new `{{conversationHistory}}` and `{{previousFillers}}` placeholders for more context-aware filler generation.

Tool readiness evidence in Arch AI

Arch AI now shows readiness evidence for the tools it adds while building your agents. Each tool is clearly labeled as backed by a live integration or by a stub/mock placeholder, and a project health view surfaces what still needs to be configured before the agent is ready to go live.

More reliable agent generation from SOPs

Generating agents from Standard Operating Procedures (SOPs) now produces more complete agent topologies. Arch AI avoids incomplete builds and validates the generated structure for quality, so SOP-derived projects start from a fuller, more accurate design.

Authoring evidence in Arch AI reviews

Arch AI authoring reviews now display evidence badges and incorporate evaluation results. Authoring guidance for constructs such as `MEMORY`, `HANDOFF`, `TOOLS`, `GATHER`, `AGENT`, `SUPERVISOR`, and guardrails is rendered from the Platform's own construct definitions, giving you clearer, more consistent recommendations as you build.

A smoother Arch AI chat experience

Several reliability improvements make working in the Arch AI chat smoother: errors that have already been resolved (such as model or provider configuration issues) no longer linger in the interface, you can start a new chat even when a previous session ended in an error, and sending a routine message no longer triggers a full refresh of the interface.

DELETE method for HTTP tools

HTTP tools now support the `DELETE` method, alongside `GET`, `POST`, `PUT`, and `PATCH`. Your agents can now call REST endpoints that remove resources directly from an HTTP tool.

Agents and Arch AI

Evidence-grounded authoring in Arch AI

Arch AI now grounds its authoring review in real parser and evaluation evidence, surfaces authoring-automation health and improvement opportunities, and routes proven best practices into generated agents — so the guidance you see while building reflects what the compiler and evals actually report.

Stronger evaluation gating

Invalid eval imports are now marked non-runnable, runs with zero coverage are blocked before they start, cancelled runs are preserved, and the winning evaluation status is reported clearly — making evaluation results more trustworthy.

More reliable agent generation

Improved the reliability of the Arch AI build experience: blueprint generation recovers from blank turns, BUILD topology failures are now recoverable instead of terminal, and the chat/generation experience handles error states more gracefully.

Canvas delegate edges render correctly

Fixed a regression on the agent canvas where delegate relationship edges were drawn as handoff edges. Delegation and handoff relationships are now visually distinct.

Behavior Profile honors all CONVERSATION fields

The Behavior Profile CONVERSATION block now applies all configured fields, resolving a case where only style and tone were applied and other fields were silently dropped.

Tools and workflows

Tools page "New Tool" menu fix

The Studio Tools page "+ New Tool" continues to be visible when the current tab has no tools. Now, you can create any type of tool regardless of which tab is active.

ABL workflow triggering from the AI4W channel

Fixed an issue where ABL workflows didn't trigger when invoked from the AI for Work (AI4W) channel.

Insights and dashboards

Agent Transfer dashboard

Added an Agent Transfer insights dashboard. You can now analyze how and when conversations are transferred between agents.

Voice

Reliable voice channel provisioning

Fixed an issue to improve the reliability of voice channel setup, during Jambonz WSS application provisioning.

Studio and administration

Consistent Audit Log date formatting

Audit Logs now use a consistent date format between the From–To filter and the results table, along with display fixes for smaller screen resolutions.

Git integration fixes

Git-sync failures are now surfaced clearly in Studio, and import-error messages are normalized for easier troubleshooting.

Security and privacy

SDK origin allowlist enforcement

The Web SDK public-key embed now respects the selected channel's origin allowlist.

Hardened SearchAI downloads and outbound requests

Strengthened security across SearchAI file downloads and outbound webhooks (including redirect handling) and hardened rich-content sanitization in the Web SDK.

Credential scrubbing in debug traces

Tool credentials and other secrets are now scrubbed from debug traces and compile-time trace events, preventing sensitive values from appearing in diagnostics.

Cleaner euser error handling

A failed LLM response is no longer misclassified as an empty response, and internal developer diagnostic strings aren't displayed to the users.

Agents and Arch AI

Reusable agent modules

Package agent logic as modules and reuse them across multiple projects instead of rebuilding per project. Imported tools now route correctly into the agent's `TOOLS:` section. Learn more about [Catalog Settings →](/agent-platform/administration/catalog-settings) and [Create and Manage Projects →](/agent-platform/projects).

Arch AI — one-shot generation and contract-driven authoring

Arch AI adds a one-shot generate endpoint (describe an agent and get a working blueprint), and its generated constructs are now derived from the compiler contracts with enforced typed topology and validated tool contracts — producing more reliable, deploy-ready agents.
[Learn more →](/agent-platform/arch-ai)

Runtime guardrail enforcement

Guardrails configured on an agent are now reliably applied at run time, closing a gap where policies could be skipped during live conversations.
[Learn more →](/agent-platform/safety-and-guardrails)

Knowledge and Search AI

Clearer web-crawl ingestion

The knowledge web-crawler setup now gates the wizard so you can only advance when the source is ready to crawl, and a crawl indexing lifecycle with a live status strip shows ingestion progress from submission to searchable content.

Knowledge fast-path search

A faster knowledge-retrieval path for agent chat, with the schema-preview endpoint now accepting JSON Schema for type extraction — lower-latency answers in conversation.
[Learn more →](/agent-platform/knowledge)

Custom pipeline authoring redesign (contract-first)

A redesigned authoring experience for custom indexing/processing pipelines with contract-first validation, making pipeline configuration clearer and less error-prone.
[Learn more →](/ai-for-service/searchai/index-configuration)

Crawler reliability improvements

Fixes to web-crawl ingestion (including docling filename/extension handling) improve the reliability of content ingested into knowledge.
[Learn more →](/ai-for-service/searchai/content-sources)

Voice

Unified voice provider registry and capability matrix

A single registry of voice (STT/TTS) providers and their capabilities gives consistent provider selection and configuration across voice agents.
[Learn more →](/agent-platform/administration/ai-configuration#voice-services)

Configurable Gemini Live VAD sensitivity

Gemini Live voice-activity-detection (VAD) sensitivity is now exposed in the speech-to-speech channel configuration, letting teams tune turn-taking and interruption behavior.
[Learn more →](/agent-platform/administration/ai-configuration#voice-services)

Channels, contact center, and connectors

Contact-center "After Call Work" (ACW) handling

The runtime now receives and processes ACW/disposition events after a human-agent call closes (emitting `acw_completed` and `agent_disconnected` with SmartAssist event data, keeping sessions alive to capture disposition) — enabling accurate post-call workflows after an agent transfer.
[Learn more →](/agent-platform/escalate)

AI-for-Work channel type

A dedicated channel lets AI for Work invoke ABL agents directly.
[Learn more →](/agent-platform/deployment)

Service Management connector category

A new connector category groups Zendesk and ServiceNow under Service Management.
[Learn more →](/ai-for-service/searchai/connectors/connector-directory)

LLM and models

AWS Bedrock provider integration

Adds AWS Bedrock as an LLM provider with IAM role support and additional connection and configuration options. Learn more about [AI Configuration →](/agent-platform/administration/ai-configuration) · [Supported AI Models →](/agent-platform/supported-models)

Studio, insights, and analytics

Insights Auditing — automated metric analysis and recommendations

Automated analysis of agent and quality metrics with improvement recommendations, wired across the agent-performance, quality-monitor, and customer-voice dashboards.
[Learn more →](/agent-platform/analytics-insights/performance-quality)

Studio feedback viewer

A recent-feedback table with filters in Studio to review captured user feedback.
[Learn more →](/agent-platform/analytics-insights/customer-voice)

Studio navigation redesign and design-system standardization

Refreshed Studio navigation and a standardized design system, including sidebar localization labels.

Security, administration, and runtime

Platform domain allowlist and DB-managed Platform admins

Adds a configurable Platform domain allowlist and database-managed Platform admin roles for tighter access governance.
[Learn more →](/agent-platform/administration/workspace-and-team)

JWE encryption for Hosted Exchange WebSDK auth

Adds JWE support to secure the Hosted Exchange WebSDK authentication flow, with enforced JWE policy scope and hardened browser-SDK CORS handling.
[Learn more →](/agent-platform/sdk)

Response metadata: LLM-generated versus scripted

Responses now carry metadata indicating whether a reply was LLM-generated or scripted, surfaced over the SDK/WebSocket structured content.

Tools and WebSDK

URL-encoded request body support

HTTP tools now support `application/x-www-form-urlencoded` request bodies, with the body type selection preserved.
[Learn more →](/agent-platform/tools/http)

2xx responses return the response body

Non-2xx HTTP responses now return the response body instead of being treated as outright execution failures, so agents can act on error payloads.
[Learn more →](/agent-platform/tools/http)

Rich-text rendering

The WebSDK now renders hyperlinks, paragraph formatting, and lists instead of showing them as plain text.
[Learn more →](/agent-platform/sdk)
Kore.ai Agent Platform \{ Artemis } is now available, with the following capabilities: * Agent Blueprint Language (ABLandtrade;) — create and manage agents. * Archandtrade; AI — generate agents from SOPs. * SearchAI — manage and retrieve knowledge. * Channels — deploy agents on web and voice. * Tool integrations — connect agents to external tools. * Insights dashboards — monitor agent performance. For details, see [Platform Overview](/agent-platform/index). # Agent runtime Source: https://koreai.mintlify.app/agent-platform/runtime The Runtime is the execution engine of Agent Platform. It receives messages from users and systems, executes agent logic, invokes tools, manages conversation state, and returns responses. Every agent interaction — regardless of channel or deployment environment — passes through the Runtime. ## How a message executes When a user sends a message, the Runtime processes it through a structured pipeline before returning a response. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% sequenceDiagram participant User participant Channel participant Runtime participant ReasoningLoop participant Tool participant LLM User->>Channel: Send message Channel->>Runtime: Inbound message (HTTP or WebSocket) Runtime->>Runtime: Authenticate & resolve session Runtime->>ReasoningLoop: Execute message loop Until final response or max 10 iterations ReasoningLoop->>LLM: Build prompt + call LLM LLM-->>ReasoningLoop: Response (text or tool calls) alt Tool call requested ReasoningLoop->>Tool: Execute tool Tool-->>ReasoningLoop: Tool result end end ReasoningLoop->>Runtime: Final response text Runtime->>Runtime: Persist session & emit traces Runtime->>Channel: Stream or return response Channel-->>User: Response ``` The cycle repeats until the agent produces a final text response or reaches the iteration limit. The default is **10 tool call iterations per turn**, configurable in the agent's execution settings. *** ## Channels The Runtime accepts messages from the following channels: **Realtime** (WebSocket or persistent connection): | Channel | Description | | - | - | | Web Chat | Browser-based chat widget | | SDK | JavaScript/mobile SDK over WebSocket | | API | Direct REST API call | | Voice | Generic voice, Twilio, LiveKit, and real-time voice pipeline | | A2A | Agent-to-Agent protocol for cross-service agent calls | **Async / webhook** (inbound event, outbound REST): | Channel | Description | | - | - | | WhatsApp | Meta Cloud, Gupshup, Infobip, Netcore | | Slack | Slack app | | Microsoft Teams | MS Teams bot | | Messenger | Meta Messenger | | Instagram | Instagram Direct | | Telegram | Telegram bot | | SMS | Twilio SMS | | Email | Inbound email | | Zendesk | Zendesk ticket and chat | | Genesys | Genesys Cloud contact center | | HTTP Async | Generic webhook-based integration | Channel connections are configured per project in Studio under **Settings → Connections**. Each connection maps a channel to its authentication credentials, routing rules, and channel-specific options. *** ## Tool execution Tools extend agent capabilities. When the LLM requests a tool call, the Runtime dispatches it to the appropriate executor and returns the result to the reasoning loop. | Tool type | What it does | | - | - | | HTTP | Calls an external REST or GraphQL API with optional auth injection | | MCP | Connects to a Model Context Protocol server; tools are discovered from the server's capability manifest | | Code | Executes JavaScript or Python in an isolated sandbox | | Connector | Uses a named integration (Salesforce, Jira, and others) with credential injection | | Workflow | Invokes a registered workflow; supports both synchronous and long-running async execution | | Knowledge Base | Queries a SearchAI knowledge base and returns ranked results | | Async Webhook | Sends a request to an external system and suspends the session until a callback is received | ### Execution pipeline When a tool executes, the Runtime processes it in sequence: 1. **Resolves the tool binding** from the deployment configuration. 2. **Validates inputs** against declared parameter types before the call. 3. **Makes the external call** with the appropriate authentication. 4. **Processes the result** — available in conversation context for reasoning agents, or as session variables for agents with steps. 5. **Handles errors** using `ON_ERROR` handlers: retry logic, fallback responses, or escalation triggers. Tool execution also enforces timeouts, categorizes errors, and retries automatically on transient failures. For setup and configuration, see [Tools Overview](/agent-platform/tools-overview). ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart TD LLM[LLM decides to call a tool] --> Dispatch{Tool type?} Dispatch -->|HTTP| HTTP[Calls external API\nwith auth injection] Dispatch -->|MCP| MCP[Calls MCP server] Dispatch -->|Code| SB[Runs code in\nisolated sandbox] Dispatch -->|Workflow| WF[Invokes workflow engine] Dispatch -->|Knowledge Base| SAI[Queries knowledge base] Dispatch -->|Connector| CN[Uses named integration] Dispatch -->|Async Webhook| AW[Sends request,\nsuspends session] HTTP --> Result[Result returned to LLM] MCP --> Result SB --> Result WF --> Result SAI --> Result CN --> Result AW --> Resume[Session resumes\non callback] Resume --> Result ``` *** ## Session management Each conversation is represented as a session. A session stores the conversation history, variables, agent state, and execution metadata. ### Session lifecycle | State | Description | | - | - | | Active | Processing a request | | Idle | Waiting for the next user message | | Completed | Conversation ended normally | | Failed | Terminated due to a runtime error | | Abandoned | Ended due to user inactivity | | Escalated | Transferred to a human agent | | Archived | Retained for historical reference | Sessions time out after **24 hours** of inactivity. Conversation history is retained for **90 days**. ### Conversation window and compaction The Runtime maintains a sliding window over the conversation history to control how much context is sent to the LLM on each turn. The default window is **40 messages**. When the window fills, the Runtime can compact older turns into a summary rather than discarding them. This preserves context from earlier in long conversations without increasing token usage. Compaction is disabled by default and can be enabled in **Runtime Config** in Studio. | Setting | Default | Description | | - | - | - | | Conversation window | 40 messages | Maximum messages sent to the LLM per turn | | Compaction | Disabled | When enabled, summarizes older turns as the window fills | | Compaction threshold | 80% | How full the window must be before compaction triggers | ### Concurrency The Runtime uses a configurable strategy to handle multiple messages arriving within the same session: | Strategy | Behavior | When to use | | - | - | - | | **Serial** (default) | Messages are queued and processed one at a time, in order. | Most conversational agents — ensures each message has full context from the previous one. | | **Preemptive** | A new message cancels in-progress execution and starts fresh. | Real-time interfaces where the user may correct themselves mid-response. | | **Parallel** | Multiple messages process simultaneously. | Batch operations where messages are independent. | *** ## Multi-agent orchestration The Runtime executes multi-agent topologies defined in ABL. When routing rules match, the Runtime transitions the active thread to the target agent, forwards context, and manages the return path. | Pattern | What happens at runtime | | - | - | | Supervisor | Receives every message; evaluates HANDOFF rules top-to-bottom; routes to first match | | Handoff | Transfers conversation to the target agent; optionally returns control when `RETURN: true` | | Delegate | Sends a task to a sub-agent; blocks the parent until the sub-agent completes or times out | | Fan-out | Dispatches multiple agents in parallel; merges results when all complete | | Escalation | Transfers the conversation to a human agent via a connected agent desktop | ### Thread hierarchy When a supervisor hands off to a specialist, the Runtime creates a **thread** within the existing session — not a new session. Threads form a stack: handoffs push new threads, completions pop back to the parent. The user experiences one continuous conversation regardless of how many agents participate. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% graph TB subgraph Session["Session (single ID)"] T0["Thread 0: Supervisor"] T1["Thread 1: Billing_Agent"] T2["Thread 2: Refund_Processor"] end T0 -->|handoff| T1 T1 -->|delegate| T2 T2 -->|return| T1 T1 -->|return| T0 ``` Each thread maintains its own conversation history and gathered variables, but can read data from parent threads. For configuration, syntax reference, and examples, see [Multi-Agent Orchestration](/agent-platform/orchestrate). *** ## Observability Every execution path emits structured trace events. Traces are accessible from the Sessions page in Studio. | Event type | What it captures | | - | - | | `llm_call` | LLM invocation — model used, token count, latency | | `tool_call` | Tool invocation request with input parameters | | `tool_result` | Tool execution result or error | | `decision` | Routing or flow decision outcome | | `handoff` | Agent-to-agent transfer | | `state_change` | Session variable update | | `guardrail_eval` | Guardrail policy evaluation result | | `error` | Runtime or execution error | Each session also captures summary metrics: total messages, runtime cost, token consumption, response latency, tool latency, and model usage breakdown. For details on navigating sessions and traces in Studio, see [Sessions and Runtime Diagnostics](/agent-platform/analytics-insights/analytics-explorer#session-explorer). *** ## Limits reference | Limit | Value | | - | - | | Request body size | 1 MB | | WebSocket message size | 512 KB | | Tool iterations per turn | 10 (default) | | Conversation window | 40 messages | | Session TTL (inactivity) | 24 hours | | Conversation history retention | 90 days | *** ## Troubleshooting **Agent stops responding mid-conversation** Open the session in Studio and check the Traces tab. Look for an `error` event or a `tool_result` with a non-200 status. Tool failures that aren't handled by an `ON_FAILURE` block can cause the reasoning loop to stall. **Earlier context is missing in long conversations** Once the conversation window fills (40 messages by default), older messages are no longer sent to the LLM. Enable compaction in **Runtime Config** to preserve earlier context as a summary rather than dropping it. **Tool calls return unexpected results** Check the `tool_call` and `tool_result` trace events in Studio for the exact request sent and response received. Verify that the tool's auth profile and endpoint are correct in **Settings → Connections**. **HTTP 429 — Too Many Requests** The `Retry-After` header tells you how many seconds to wait before retrying. If you hit limits consistently during evaluation, check whether multiple concurrent test sessions are sharing the same tenant quota. **Agents route to the wrong specialist** HANDOFF rules evaluate top-to-bottom and the first match wins. Check rule ordering in the Supervisor — overly broad conditions placed above specific ones will capture requests before the intended rule is reached. See [Orchestration Troubleshooting](/agent-platform/orchestrate#troubleshooting-guide). *** **Related articles:** * [Multi-Agent Orchestration](/agent-platform/orchestrate) * [Tools Overview](/agent-platform/tools-overview) * [Memory and State](/agent-platform/memory-and-state) * [Safety and Guardrails](/agent-platform/safety-and-guardrails) # Safety and guardrails Source: https://koreai.mintlify.app/agent-platform/safety-and-guardrails Configure enforcement layers, guardrail policies, providers, and input/output safety rules to protect agents from misuse and ensure compliance. The platform uses three enforcement layers to control agent behavior and protect against misuse, sensitive data exposure, and policy violations. | Enforcement layer | Description | Example | | - | - | - | | [Guardrails](#input-guardrails) | Safety and quality checks applied to agent input and output content. | Blocking abusive language or redacting sensitive information from responses. | | [Limitations](/agent-platform/create-agents#define-identity-and-persona) | Reasoning-based guidance enforced through the model. Defines how the agent should behave, what it should avoid, and the scope it should operate within. | Instructing an agent to avoid financial advice or remain within a support domain. | | [Constraints](/agent-platform/concepts#constraints-and-guardrails) | Runtime business-rule validations that prevent execution when required conditions aren't met. | Requiring a customer ID before allowing an order lookup or payment action. | ## Constraints vs. guardrails Constraints and guardrails operate at different layers of agent execution: | Feature | Constraints | Guardrails | | - | - | - | | Purpose | Enforce business rules. | Enforce safety and content policies. | | Scope | Agent logic and tool execution. | LLM input and output content. | | Evaluation | Runtime data and conditions. | Message and response content. | | Typical usage | Eligibility checks, required inputs. | Content moderation, PII detection. | *** ## Guardrails Guardrails evaluate agent inputs and outputs against configurable safety categories. Depending on configuration, they can block content, warn users, redact sensitive information, escalate interactions, request rephrasing, or automatically sanitize responses. The following shows a typical runtime flow: ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart TD A[User Input] --> B[Input Guardrails] B --> |PII detection\nPrompt injection\nTopic checks| C[Agent / Model Processing] C --> D[Output Guardrails] D --> |Toxicity checks\nPII redaction\nContent filtering| E[Final Response Returned to User] ``` *** ### Guardrail configuration levels Guardrails can be configured at two levels: | Scope | Purpose | Where managed | Typical usage | | - | - | - | - | | Project guardrails | Centralized governance and reusable safety policies across agents. | **Govern > Guardrails** | Enterprise-wide safety enforcement. | | Agent guardrails | Agent-specific runtime safety checks. | **Agent > Guardrails** | Localized rules for individual agents. | Project-level policies apply in addition to agent-specific guardrails. **Use project guardrails when you want:** * Consistent governance across multiple agents. * Shared moderation providers. * Organization-wide safety controls. * Centralized runtime management. **Use agent guardrails when:** * Safety rules are specific to one agent. * Runtime behavior must be customized locally. * Shared project-level governance isn't required. *** ## Guardrail policies Policies are reusable governance containers that define runtime safety behavior across agents and projects. Each policy contains one or more rules. Each rule defines what to evaluate, where to evaluate it, which provider to use, and what action to take when triggered. Rules can support input and output evaluation, streaming responses, pattern matching, model-based moderation, and LLM-based classification. Go to **Govern > Guardrails > Policies**. Only one policy can be active per project at a time. Activating a new policy automatically deactivates the previously active one. ### Policy scopes **Project-level scope** — Apply the policy to all agents in the project: ```yaml theme={null} { "scopeType": "project" } ``` **Agent-level scope** — Apply the policy only to a specific agent: ```yaml theme={null} { "scopeType": "agent", "agentDefId": "agent-definition-id" } ``` ### Create a guardrail policy 1. Go to **Govern > Guardrails**. 2. On the **Policies** tab, click **Create policy**. 3. Enter a policy name and description. 4. Select whether the policy applies to all agents in the project or only to a specific agent. 5. Configure the required rules and runtime settings. 6. Click **Save**. #### Rules | Field | Description | | - | - | | Applies To | Where the rule is evaluated: Input, Output, or Both. | | Action | What happens when the rule is triggered: Block, Warn, Redact, Escalate, Fix, Reask, or Filter. | | Provider | The provider used for guardrail evaluation. | | Category | The safety or content category evaluated by the rule. | | Severity Threshold | The numeric threshold that triggers the configured action. Enter a value from 0.01 to 1.0, or use Low, Medium, or High as a shortcut. | | Action Message | The message shown or logged when the rule is triggered. | #### Runtime settings | Setting | Description | | - | - | | Fail Mode | Controls whether execution continues or is blocked if guardrail evaluation fails. **Fail-open** allows execution to continue if evaluation fails or times out. **Fail-closed** blocks execution when evaluation can't complete. Use fail-closed for high-security or compliance-sensitive applications. | | Local Timeout | How long the platform waits for local guardrail evaluation. | | Model Timeout | How long the platform waits for model-based provider evaluation. | | LLM Timeout | How long the platform waits for LLM-based evaluation. | | Streaming Evaluation | Enables guardrail evaluation while responses are streamed. | | Chunk Interval | Whether streamed responses are evaluated by sentence, token, or chunk size. | | Early Termination | Stops evaluation on the first guardrail trigger. | *** ## Guardrail providers Providers are the evaluation engines used to classify or inspect content during runtime. They can detect unsafe content, identify PII, classify toxicity, evaluate prompt injection attempts, and perform model-based moderation. ### Select guardrail providers Project authors can select which workspace-configured guardrail providers the project can use. Providers that are not selected are not used by the project, even if they are configured at the workspace level. Ask your workspace administrator to add and configure a guardrail provider in the Admin Console before you select it for the project. For more information on configuring guardrail providers, see [Guardrail providers](/agent-platform/administration/ai-configuration#guardrail-providers). *** ## Input guardrails Input guardrails evaluate user messages before they reach the LLM. Use them to detect unsafe content, identify prompt injection attempts, protect sensitive information, and enforce topic or policy restrictions. Use `kind: input` to evaluate user messages before they reach the LLM: ```yaml theme={null} GUARDRAILS: profanity_filter: kind: input action: block ``` Input guardrails support: * Pattern-based detection. * Provider-based moderation. * LLM-based classification. * Severity-based actions. * Runtime priority ordering. *** ## Output guardrails Output guardrails evaluate generated responses before they're returned to the user. Use them to prevent unsafe responses, redact sensitive information, apply moderation checks, and inspect streaming output during generation. Use `kind: output` to evaluate generated responses: ```yaml theme={null} GUARDRAILS: pii_output_prevention: kind: output action: block ``` Use `kind: both` to apply the same rule to both input and output: ```yaml theme={null} GUARDRAILS: phone_number_check: kind: both action: warn ``` Enable streaming evaluation for responses while content is still being generated: ```yaml theme={null} GUARDRAILS: streaming_safety: kind: output streaming: true ``` Output guardrails support: * PII detection and redaction. * Toxicity scoring. * Streaming response evaluation. * Bidirectional guardrails. * Automatic response cleanup and fix strategies. *** ## DSL and UI mapping The platform maintains a one-to-one mapping between the UI configuration and the DSL/ABL definition. This lets you: * Configure guardrails visually. * Manage guardrails as code. * Version and compare configuration changes. * Switch between UI and DSL-based editing workflows. When you add a guardrail rule in the UI, the platform generates the corresponding `GUARDRAILS:` block in the DSL/ABL. Updating the `GUARDRAILS:` block directly in the DSL/ABL updates the same rule in the UI. *** ## Best practices * Use project guardrails for centralized governance; use agent guardrails for localized runtime behavior. * Start with `warn` before enabling `block` to understand impact before enforcement. * Test regex patterns carefully to reduce false positives. * Enable streaming guardrails for high-risk applications. * Use fail-closed behavior for compliance-sensitive workloads. * Separate business constraints from safety guardrails. * Use providers with caching and budget controls for large-scale deployments. *** # Artemis mobile SDK overview Source: https://koreai.mintlify.app/agent-platform/sdk/mobile-sdk Artemis provides mobile SDKs to integrate AI agent capabilities into Android and iOS apps that are built using Flutter or React Native. Two types of SDKs are available: * **Socket SDKs** for the underlying connection and messaging capabilities. Use it to build and control your own chat interface. * **UI SDKs** to quickly create a ready-to-use UI experience. Use it to embed the platform's chat experience in a UI. ## React native SDKs * **[React Native Socket SDK](https://www.npmjs.com/package/@koreai/artemis-react-native-socket-sdk)** — A headless SDK that provides WebSocket connectivity, authentication, session management, messaging, streaming responses, message history, interactive actions, feedback, and automatic reconnection. It doesn't include a user interface. * **[React Native UI SDK](https://www.npmjs.com/package/@koreai/artemis-react-native-ui-sdk)** — A UI layer built on the React Native Socket SDK. It provides chat screens and modals, message bubbles, rich-content rendering, themes, and configuration helpers. ## Flutter SDKs * **[Flutter Socket SDK](https://pub.dev/packages/artemis_socket_plugin)** — Provides the core SDK capabilities for connecting to Artemis, sending and receiving messages, managing conversation history, and listening to SDK and chat events. The application builds its own UI using the SDK. * **[Flutter UI Plugin](https://pub.dev/packages/artemis_ui_plugin)** — Provides a complete configurable AI agent chat experience, including chat UI, message history, connection status, rich-content rendering, themes, and UI customization. The plugin manages the chat connection internally. # Artemis Software Developer Kits Source: https://koreai.mintlify.app/agent-platform/sdk/overview Artemis provides SDKs to integrate AI agent capabilities into web and mobile apps. You can either build your own custom interface or use a prebuilt chat experience. ## Web SDK It's a JavaScript/TypeScript SDK that provides programmatic access to chat and voice interactions, including streaming responses, message history, file uploads, and voice interaction. It also provides React integration through `AgentProvider`, `useAgent`, `useChat`, and `useVoice` hooks. SDK also includes ready-to-use interface for apps. The Web SDK communicates with the Artemis platform over WebSocket and supports public API keys and verified-user authentication flows. ## Mobile SDKs Artemis provides separate SDKs for **React Native** and **Flutter**, with each platform offering a Socket SDK and a UI SDK. The **Socket SDKs** provide the underlying connection and messaging layer without a user interface. These handle authentication, WebSocket sessions, messaging, streaming responses, conversation history, events, and reconnection, while you build the chat interface. The **UI SDKs** build on the socket functionality and provide a ready-to-use chat experience. The React Native UI SDK provides chat screens and modals, message rendering, rich content, themes, and configuration helpers. The Flutter UI plugin provides a full-screen chat experience with rich-content rendering, themes, customization, and internally managed connectivity. ## How to choose an SDK Use the **Web SDK** for browser-based applications. For mobile applications, choose **React Native** or **Flutter** based on your application's framework. Within either mobile platform, choose a **Socket SDK** when you need to build the UI yourself, or a **UI SDK** when you want to integrate the provided chat experience. # Artemis Web SDK overview Source: https://koreai.mintlify.app/agent-platform/sdk/web-sdk The platform provides Web SDK to embed agent chat and voice interactions in web applications. The Web SDK (`@koreai/artemis-web-sdk`) provides a TypeScript/JavaScript library for embedding agent chat and voice interactions in web applications. The SDK supports vanilla JavaScript, React hooks, and a web component for drop-in integration. The browser-facing SDK does not send the public `pk_*` key directly to `/ws/sdk`. It first exchanges the key for a short-lived SDK session token on `POST /api/v1/sdk/init`, then authenticates SDK HTTP routes with `X-SDK-Token` and the SDK WebSocket with `Sec-WebSocket-Protocol: sdk-auth,`. ## Install Web SDK ```bash theme={null} npm i @koreai/artemis-web-sdk ``` The package includes the core `AgentSDK` constructor, chat and voice clients, React provider/hooks/components, custom elements, rich content renderers, attachment upload, feedback, auth challenge UI, and template utilities. ## Quick start ### Vanilla JavaScript ```typescript theme={null} import { AgentSDK } from '@koreai/artemis-web-sdk'; const sdk = new AgentSDK({ projectId: 'your-project-id', apiKey: 'pk_your-public-key', endpoint: 'https://agents.kore.ai', }); await sdk.connect(); // Send a chat message const chat = sdk.chat(); chat.on('message', (msg) => console.log(msg.content)); await chat.send('Hello, I need help!'); ``` ### React ```tsx theme={null} import { AgentProvider, useChat, useVoice } from '@koreai/artemis-web-sdk/react'; function App() { return ( ); } function ChatWidget() { const { messages, isTyping, sendMessage, isConnected } = useChat(); return (
{messages.map((msg) => (
{msg.role}: {msg.content}
))} {isTyping &&
Agent is typing...
}
); } ``` ### Web Component ```html theme={null} ``` *** ## AgentSDK The main SDK class. Creates and manages connections, chat clients, and voice clients. ### Constructor ```typescript theme={null} new AgentSDK(config: SDKConfig) ``` **SDKConfig** | Property | Type | Required | Default | Description | | - | - | - | - | - | | `projectId` | string | Yes | -- | Project ID to connect to | | `apiKey` | string | Yes | -- | Public API key (starts with `pk_`) | | `endpoint` | string | No | Same origin | Platform base URL | | `debug` | boolean | No | `false` | Enable debug logging to console | ### Methods | Method | Returns | Description | | - | - | - | | `connect()` | `Promise` | Establish WebSocket connection to the platform | | `disconnect()` | `void` | Close the connection and clean up resources | | `chat()` | `ChatClient` | Get the chat client instance (created on first call) | | `voice()` | `VoiceClient` | Get the voice client instance (created on first call) | | `isConnected()` | `boolean` | Check if the SDK is connected | | `getSessionId()` | `string` or `null` | Get the current session ID | ### Static methods | Method | Returns | Description | | - | - | - | | `AgentSDK.init(config)` | `AgentSDK` | Create and store an SDK instance globally (for web components) | ### Events | Event | Payload | Description | | - | - | - | | `connected` | `void` | WebSocket connection established | | `disconnected` | `void` | WebSocket connection closed | | `error` | `{ error: Error }` | Connection or runtime error | | `sessionStart` | `{ sessionId: string }` | New session started | | `sessionEnd` | `void` | Session ended | ```typescript theme={null} sdk.on('connected', () => { console.log('Connected to platform'); }); sdk.on('error', ({ error }) => { console.error('SDK error:', error.message); }); sdk.on('sessionStart', ({ sessionId }) => { console.log('Session started:', sessionId); }); ``` *** ## ChatClient Handles text messaging with streaming support. Obtained via `sdk.chat()`. ### Methods | Method | Returns | Description | | - | - | - | | `send(text, options?)` | `Promise` | Send a message. Returns the message ID | | `uploadAttachment(file)` | `Promise` | Upload a file attachment. Returns the attachment ID | | `getMessages()` | `Message[]` | Get all messages in the conversation | | `getIsTyping()` | `boolean` | Check if the agent is currently responding | | `clearMessages()` | `void` | Clear the local message history | ### send() options ```typescript theme={null} interface SendMessageOptions { /** Pre-uploaded attachment IDs to include with the message */ attachmentIds?: string[]; /** Optional per-message metadata for the current turn only */ metadata?: Record; } ``` **Example: send with attachments** ```typescript theme={null} const chat = sdk.chat(); // Upload a file first const attachmentId = await chat.uploadAttachment(fileInput.files[0]); // Send message with attachment await chat.send('Please analyze this document', { attachmentIds: [attachmentId], }); ``` **Example: send with per-message metadata** ```typescript theme={null} await chat.send('Look up this account', { metadata: { accountId: 'acct_123', context: { tier: 'gold' }, }, }); ``` Per-message metadata is validated server-side and is available only for that turn. Use `session.messageMetadata` as the canonical prompt/template path. `message_metadata` remains available as the tool-context alias for `context_access.read`. ### Events | Event | Payload | Description | | - | - | - | | `message` | `Message` | New message received (user or assistant) | | `messageChunk` | `{ messageId: string, chunk: string }` | Streaming text chunk from assistant | | `typing` | `{ isTyping: boolean }` | Agent typing indicator changed | | `messageSent` | `{ messageId: string }` | User message was sent | | `attachmentUploaded` | `{ attachmentId: string, filename: string }` | File upload completed | | `attachmentError` | `{ filename: string, error: string }` | File upload failed | | `error` | `{ error: Error }` | Chat error | ```typescript theme={null} const chat = sdk.chat(); chat.on('message', (msg) => { if (msg.role === 'assistant') { console.log('Agent:', msg.content); if (msg.richContent?.markdown) { renderMarkdown(msg.richContent.markdown); } if (msg.actions) { renderActions(msg.actions); } } }); chat.on('messageChunk', ({ messageId, chunk }) => { appendToMessage(messageId, chunk); }); chat.on('typing', ({ isTyping }) => { showTypingIndicator(isTyping); }); ``` #### Message type ```typescript theme={null} interface Message { id: string; role: 'user' | 'assistant' | 'system'; content: string; timestamp: Date; metadata?: Record; richContent?: RichContent; actions?: ActionSet; attachments?: AttachmentRef[]; } ``` ### RichContent type Multi-format content variants delivered alongside the plain text response: ```typescript theme={null} interface RichContent { markdown?: string; adaptive_card?: string; html?: string; slack?: string; ag_ui?: string; whatsapp?: string; } ``` ### ActionSet type Interactive action elements the agent sends for user input: ```typescript theme={null} interface ActionSet { elements: ActionElement[]; submit_label?: string; submit_id?: string; } interface ActionElement { id: string; type: 'button' | 'select' | 'input'; label: string; value?: string; description?: string; options?: Array<{ id: string; label: string; description?: string }>; input_type?: 'text' | 'number' | 'date' | 'time' | 'email'; placeholder?: string; required?: boolean; } ``` ### AttachmentRef type ```typescript theme={null} interface AttachmentRef { id: string; filename: string; mimeType: string; sizeBytes: number; category: 'image' | 'document' | 'audio' | 'video'; } ``` *** ## VoiceClient Handles voice interactions via WebSocket audio pipeline with optional WebRTC. Obtained via `sdk.voice()`. The voice client supports two modes: * **Pipeline mode**: Client-side VAD (Voice Activity Detection) captures PCM16 audio, sends it via WebSocket for server-side STT/LLM/TTS processing, and plays back MP3 audio responses. * **Realtime mode**: Native audio I/O via realtime LLM providers with PCM16 streaming. ### Methods | Method | Returns | Description | | - | - | - | | `start()` | `Promise` | Start voice interaction (requests microphone permission) | | `stop()` | `void` | Stop voice interaction and release audio resources | | `toggleMute()` | `boolean` | Toggle microphone mute. Returns the new mute state | | `getState()` | `VoiceState` | Get the current voice state | | `getInfo()` | `VoiceInfo` | Get full voice status information | ### Static methods | Method | Returns | Description | | - | - | - | | `VoiceClient.isSupported()` | `boolean` | Check if the browser supports voice features | ### Voice states ```typescript theme={null} type VoiceState = | 'idle' // Not active | 'connecting' // Establishing connection | 'ready' // Connected, waiting for speech | 'listening' // Detecting speech | 'processing' // Processing user speech | 'speaking' // Playing agent response | 'error'; // Error state ``` ### VoiceInfo type ```typescript theme={null} interface VoiceInfo { state: VoiceState; isMuted: boolean; currentTranscript: string; hasMicPermission?: boolean; } ``` ### VoiceClientOptions ```typescript theme={null} interface VoiceClientOptions { enableBargeIn?: boolean; // Default: true sampleRate?: number; // Default: 16000 deviceId?: string; // Specific audio input device vadConfig?: { positiveSpeechThreshold?: number; negativeSpeechThreshold?: number; redemptionMs?: number; minSpeechMs?: number; preSpeechPadMs?: number; }; } ``` ### Events | Event | Payload | Description | | - | - | - | | `stateChange` | `{ state: VoiceState, previousState: VoiceState }` | Voice state changed | | `transcription` | `{ text: string, isFinal: boolean, confidence?: number }` | Speech-to-text result | | `transcriptionFinal` | `{ text: string, confidence: number }` | Final transcription | | `responseStart` | `{ messageId: string }` | Agent started speaking | | `responseChunk` | `{ messageId: string, text: string }` | Agent speech text chunk | | `responseEnd` | `{ messageId: string, text: string }` | Agent finished speaking | | `speaking` | `{ isSpeaking: boolean }` | Audio playback state changed | | `volumeChange` | `{ level: number }` | Microphone volume level (0-1) | | `ready` | `void` | Voice client is ready | | `error` | `{ error: Error }` | Voice error | | `micPermissionDenied` | `void` | Microphone permission denied | | `bargeIn` | `void` | User interrupted agent speech | | `vadAvailable` | `{ available: boolean }` | VAD availability changed | **Voice interaction example** ```typescript theme={null} const voice = sdk.voice(); voice.on('stateChange', ({ state }) => { updateUI(state); }); voice.on('transcription', ({ text, isFinal }) => { updateTranscript(text, isFinal); }); voice.on('responseEnd', ({ text }) => { console.log('Agent said:', text); }); voice.on('error', ({ error }) => { console.error('Voice error:', error.message); }); // Start listening await voice.start(); // Mute/unmute const isMuted = voice.toggleMute(); ``` *** ## React hooks The React integration provides a context provider and hooks for state management. ### AgentProvider Wrap your application with `AgentProvider` to initialize the SDK: ```tsx theme={null} import { AgentProvider } from '@koreai/artemis-web-sdk/react'; {children} ; ``` ### useAgent() Access the full SDK context: ```typescript theme={null} const { sdk, // AgentSDK | null isConnected, // boolean sessionId, // string | null error, // Error | null chat, // ChatClient | null messages, // Message[] isTyping, // boolean sendMessage, // (text: string) => Promise voice, // VoiceClient | null voiceState, // VoiceState startVoice, // () => Promise stopVoice, // () => void toggleMute, // () => boolean isMuted, // boolean } = useAgent(); ``` ### useChat() Access chat-specific state: ```typescript theme={null} const { messages, // Message[] isTyping, // boolean sendMessage, // (text: string) => Promise isConnected, // boolean } = useChat(); ``` **Example** ```tsx theme={null} import { useChat } from '@koreai/artemis-web-sdk/react'; function ChatView() { const { messages, isTyping, sendMessage, isConnected } = useChat(); const handleSend = async (text: string) => { await sendMessage(text); }; return (
{messages.map((msg) => (
{msg.content}
))} {isTyping &&
Agent is typing...
}
); } ``` ### useVoice() Access voice-specific state: ```typescript theme={null} const { voiceState, // VoiceState startVoice, // () => Promise stopVoice, // () => void toggleMute, // () => boolean isMuted, // boolean isConnected, // boolean } = useVoice(); ``` **Example** ```tsx theme={null} import { useVoice } from '@koreai/artemis-web-sdk/react'; function VoiceControls() { const { voiceState, startVoice, stopVoice, toggleMute, isMuted } = useVoice(); return (

Status: {voiceState}

); } ``` *** ## File uploads Upload files for agent processing: ```javascript theme={null} const chat = sdk.chat(); // Upload a file const file = new File(['content'], 'document.pdf', { type: 'application/pdf', }); const attachmentId = await chat.uploadAttachment(file); // Send message with attachment await chat.send('Please analyze this document', { attachmentIds: [attachmentId], }); ``` ### Supported file types * **Images:** jpeg, png, gif, webp, svg * **Documents:** pdf, docx, xlsx, pptx, txt, csv * **Audio:** mp3, wav, ogg, m4a * **Video:** mp4, webm ### Upload limits * Maximum file size: 25 MB per file * Maximum files per message: 10 *** ## Styling and theming ### Widget theming Customize the web component appearance: ```typescript theme={null} interface WidgetTheme { primaryColor?: string; textColor?: string; backgroundColor?: string; borderRadius?: number; fontFamily?: string; darkMode?: boolean; } ``` Configure via attributes: ```html theme={null} ``` ### Widget positions * `bottom-right` (default) * `bottom-left` * `top-right` * `top-left` ### Widget modes * `chat` -- Text-only chat interface * `voice` -- Voice-only interface * `unified` -- Combined chat and voice interface *** ## TypedEventEmitter All SDK classes extend `TypedEventEmitter` for type-safe event handling: ```typescript theme={null} // Subscribe to events sdk.on('connected', callback); // Subscribe once sdk.once('connected', callback); // Unsubscribe sdk.off('connected', callback); // Remove all listeners sdk.removeAllListeners(); ``` *** ## WebSocket message types The SDK communicates with the platform over WebSocket. These types are used internally but documented for advanced use cases. ### Server message types | Type | Description | | - | - | | `response_start` | Agent started generating a response | | `response_chunk` | Incremental text from the agent | | `response_end` | Agent finished responding (includes `fullText`, `richContent`, `actions`) | | `error` | Error occurred during processing | | `session_start` | New session established | | `session_end` | Session ended | *** ## API key management Public API keys (`pk_` prefix) are scoped to a project and provide limited permissions for SDK usage. They are safe to expose in client-side code. ### Creating an API key 1. Go to **Project > Settings > API Keys**. 2. Click **Create API key**. 3. Set the allowed origins (for CORS protection). 4. Copy the key -- it is displayed only once. ### Origin restrictions Configure allowed origins to prevent unauthorized use of your API key: ```json theme={null} { "allowedOrigins": ["https://your-app.example.com", "https://staging.your-app.example.com"] } ``` The runtime validates the `Origin` header on every SDK request and rejects requests from unlisted origins. *** ## Authentication and verified identity Public API keys cover anonymous and low-assurance chat. For verified user identity, sensitive-data flows, and customer-issued tokens, configure SDK authentication end to end across Studio, the browser SDK, the customer backend, and the runtime. [Learn more: SDK end-to-end auth setup guide](/agent-platform/sdk/web-sdk-end-to-end-auth-setup). *** # Web SDK end-to-end auth setup guide Source: https://koreai.mintlify.app/agent-platform/sdk/web-sdk-end-to-end-auth-setup [Back to SDK Overview](/agent-platform/sdk#authentication-and-verified-identity) The Agent Platform SDK is a TypeScript/JavaScript library for embedding agent chat and voice interactions in web applications. It supports vanilla JavaScript, React hooks, and a web component for drop-in integration. For the full SDK reference, see the [SDK Overview](/agent-platform/sdk). This guide explains how to configure Artemis SDK authentication end to end across Studio, the browser SDK, your backend, and deployment and runtime settings. Before you set up E2E auth, [install the Agent Platform SDK](/agent-platform/sdk#install-web-sdk). ## Scenario selection | Scenario | Browser credential | Customer backend responsibility | Runtime responsibility | When to use | | - | - | - | - | - | | [Public anonymous SDK](#scenario-1-public-anonymous-sdk) | Public SDK API key | None for auth. The app may pass non-sensitive public `userContext`. | Verifies public key, channel, origin, and issues SDK session tokens. | Anonymous or low-assurance website chat where no sensitive data or verified customer identity is required. | | [Runtime-signed Hosted Exchange](#scenario-2-runtime-signed-hosted-exchange) | Short-lived `bootstrapToken` returned by customer backend | Authenticates the customer, gathers secure attributes, calls Runtime `POST /api/v1/sdk/customer-sessions` with the channel server secret. | Mints the hosted bootstrap artifact and SDK session token. Applies `sdkTokenEnvelopePolicy`. | Customers accept a server-to-server Runtime call and want Artemis to own signing/encryption. | | [Customer-issued shared-secret JWE](#scenario-3-customer-issued-shared-secret-jwe) | Short-lived compact JWE returned by customer backend | Authenticates the customer, builds claims, encrypts with the channel JWE shared secret, and returns JWE to the browser. | Decrypts and validates the customer-issued JWE, enforces replay, scope, age, and channel config, then issues SDK session token. | Flow where the customer backend must keep sensitive-data encryption local and avoid the `/customer-sessions` API call. | | [Customer-issued public-key JWE](#scenario-4-customer-issued-public-key-jwe) | Short-lived compact JWE returned by customer backend | Authenticates the customer, signs the payload with the customer private key, encrypts the signed payload with the Runtime channel public key, and returns JWE to the browser. | Decrypts with Runtime private key, verifies customer signature with the configured customer public key, enforces replay, scope, age, and channel config, then issues SDK session token. | Preferred no-API-call flow when security review requires no shared encryption secret on the customer side and explicit issuer authentication. | Public-key customer JWE has two layers: * The outer JWE gives confidentiality to Runtime using `RSA-OAEP-256` and `A256GCM`. * The inner JWS gives issuer authentication using the customer signing private key and Runtime-configured customer signing public key. Don't treat public-key encryption alone as issuer authentication. ## Shared runtime flow after bootstrap All four scenarios converge after browser initialization: ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4', 'actorBkg': '#EAF8E4', 'actorBorder': '#5CC83A', 'actorTextColor': '#1a1a1a', 'actorLineColor': '#5CC83A', 'signalColor': '#5CC83A', 'signalTextColor': '#2F9E1E', 'labelBoxBkgColor': '#EAF8E4', 'labelBoxBorderColor': '#5CC83A', 'labelTextColor': '#1a1a1a', 'loopTextColor': '#2F9E1E', 'noteBkgColor': '#EAF8E4', 'noteBorderColor': '#5CC83A', 'noteTextColor': '#1a1a1a', 'activationBorderColor': '#5CC83A', 'activationBkgColor': '#EAF8E4', 'sequenceNumberColor': '#2F9E1E'}}}%% sequenceDiagram participant Browser as Browser SDK participant Customer as Customer Backend participant Runtime as Artemis Runtime participant Bot as Agent Runtime Browser->>Runtime: POST /api/v1/sdk/init Runtime-->>Browser: sdk_session token Browser->>Runtime: SDK HTTP routes with X-SDK-Token Browser->>Runtime: POST /api/v1/sdk/ws-ticket with X-SDK-Token Runtime-->>Browser: one-time WebSocket ticket Browser->>Runtime: WebSocket using sdk-ticket subprotocol Runtime->>Bot: verified session identity + custom attributes ``` ## Permission and origin resolution Public API key permissions are stored as coarse `chat` and `voice` booleans. Runtime expands these to SDK permissions. | Public key permission | Runtime SDK permissions | | - | - | | `chat=true` | `session:send_message`, `session:read`, `attachment:read`, `attachment:write`, `attachment:delete` | | `voice=true` | `session:voice`, `session:read` | Runtime-signed Hosted Exchange tokens derive permissions from the channel's active public API key. The `/api/v1/sdk/customer-sessions` request body doesn't accept `permissions`. Customer-issued JWE payloads may include a narrower `permissions` array. Runtime normalizes the requested values, adds `session:read` for interactive or attachment grants, intersects the requested set with the channel's active public API key permissions, and rejects the token if the effective set is empty. Allowed customer-issued permission values: * `session:send_message` * `session:voice` * `session:read` * `attachment:read` * `attachment:write` * `attachment:delete` Origin enforcement happens after Runtime resolves the channel: * Public-key SDK init checks the public API key origins and, if present, legacy channel-config origins. * Hosted Exchange bootstrap init checks both the channel-config origins and the bound public API key origins after the bootstrap token is verified. * Empty or unset allowed-origin lists behave as unrestricted in code. Production channels should always configure explicit HTTPS origins. ## Shared Studio prerequisites 1. Create or select the project that owns the SDK channel. 2. Create or select the deployment the SDK channel should route to. 3. Create or select a public SDK API key for the project. 4. Set allowed origins on the public API key or managed channel key. * Include the exact browser origins that will host the SDK. * Don't rely on wildcard origins for production flows. 5. Create an SDK channel and bind it to the deployment or environment. 6. Keep the channel active only after customer backend and browser changes are deployed. The Studio channel auth mode decides the setup path: * `anonymous` for public anonymous SDK. * `hosted_exchange` for Runtime-signed, customer-issued shared-secret JWE, and customer-issued public-key JWE. ## Shared deployment prerequisites Runtime must have: * `JWT_SECRET`, `AUTH_SDK_BOOTSTRAP_SIGNING_SECRET`, and `AUTH_SDK_SESSION_SIGNING_SECRET` configured consistently across Runtime pods. * `ENCRYPTION_MASTER_KEY` configured when Hosted Exchange token envelope JWE is required or preferred. * `AUTH_SDK_JWE_ENABLED` unset or `true` when JWE issuance/verification should be available. * `REDIS_URL` configured for distributed SDK session state, WebSocket tickets, replay protection, and rotation locks. * MongoDB available for SDK channel, public key, and customer JWE key metadata. * HTTPS between browser, customer backend, and Runtime. Before enabling strict Hosted Exchange JWE, verify capability: ```bash theme={null} curl \ -H "Authorization: Bearer " \ "https:///api/projects//sdk-jwe-capability" ``` Expected ready response: ```json theme={null} { "success": true, "supported": true, "canIssueBootstrap": true, "canIssueSession": true, "canVerify": true, "maxEncryptedBootstrapBytes": 4096, "maxEncryptedSessionBytes": 4096 } ``` If `jwe_required` is configured and capability is not ready, Runtime must fail closed instead of returning signed Hosted Exchange tokens. *** ## Scenario 1: Public anonymous SDK ### Studio setup 1. Open the project deployment channel settings in Studio. 2. Create or edit an SDK channel. 3. Set SDK auth mode to `anonymous`. 4. Bind the channel to the target deployment or environment. 5. Configure allowed origins for the browser site. 6. Save the channel. 7. Copy the public SDK API key and channel identifier or channel name. Do not configure `sdkTokenEnvelopePolicy` or `customerIssuedJwe` on anonymous channels. Runtime rejects those settings unless `auth.mode=hosted_exchange`. ### Customer server changes No customer server auth endpoint is required. The server that renders the website, may provide public configuration values such as: * Runtime endpoint * Project ID * Public SDK API key * Channel ID or channel name * Deployment slug, when the channel uses deployment-slug selection Do not put channel server secrets, customer JWE secrets, Runtime private keys, or customer signing private keys in browser-rendered HTML or JavaScript. ### Browser SDK changes ```ts title="Code SDK" theme={null} import { AgentSDK } from '@koreai/artemis-web-sdk'; const sdk = new AgentSDK({ endpoint: 'https://runtime.example.com', projectId: 'project_123', apiKey: 'pk_public_sdk_key', channelId: 'channel_123', clientSessionIdentifier: true, userContext: { userId: 'anonymous-browser-id', customAttributes: { page: 'support', }, }, }); await sdk.connect(); ``` ```ts title="Public-key variants" theme={null} // Channel name instead of channel ID. new AgentSDK({ endpoint: 'https://runtime.example.com', projectId: 'project_123', apiKey: 'pk_public_sdk_key', channelName: 'web', }); // Deployment slug selection for public-key embeds. new AgentSDK({ endpoint: 'https://runtime.example.com', projectId: 'project_123', apiKey: 'pk_public_sdk_key', channelId: 'channel_123', deploymentSlug: 'support-production', }); // Unified URL mode. Public-key configs can omit projectId when the URL path // carries project/environment/agent context; bootstrap-token configs cannot. new AgentSDK({ endpoint: 'https://runtime.example.com', apiKey: 'pk_public_sdk_key', wsPath: '/api/v1/project/support/production/agent/support-agent/ws', }); ``` ```tsx title="React" theme={null} import { AgentProvider, ChatWidget } from '@koreai/artemis-web-sdk/react'; export function SupportChat() { return ( ); } ``` ```html title="Custom element" theme={null} ``` ### Validation 1. Load the browser site from an allowed origin. 2. Confirm `/api/v1/sdk/init` uses `X-Public-Key`. 3. Confirm the request body does not include `bootstrapToken`. 4. Send a chat message. 5. Confirm `/api/v1/sdk/ws-ticket` succeeds before WebSocket connect. 6. Disable the channel or remove the origin and confirm new sessions fail. *** ## Scenario 2: Runtime-signed hosted exchange This path adds a customer backend endpoint, but lets Runtime mint the bootstrap artifact. The browser never sees the Hosted Exchange channel server secret. ### Studio setup 1. Open the project deployment channel settings in Studio. 2. Create or edit an SDK channel. 3. Set SDK auth mode to `hosted_exchange`. 4. Configure allowed origins. 5. Select `sdkTokenEnvelopePolicy`: * `signed` for signed-token compatibility. * `jwe_preferred` for rollout where JWE is used when Runtime capability is ready. * `jwe_required` after the capability endpoint shows Runtime can issue and verify JWE. 6. Save the channel. 7. Copy the Hosted Exchange server secret when Studio reveals it. 8. Store the server secret only in the customer backend secret manager. ### Customer server changes Add a backend endpoint that authenticates the customer in the customer's system, then calls Runtime. ```ts expandable=true theme={null} import express from 'express'; const router = express.Router(); const runtimeBaseUrl = process.env.ARTEMIS_RUNTIME_BASE_URL!; const tenantId = process.env.ARTEMIS_TENANT_ID!; const projectId = process.env.ARTEMIS_PROJECT_ID!; const channelId = process.env.ARTEMIS_SDK_CHANNEL_ID!; const channelSecret = process.env.ARTEMIS_SDK_CHANNEL_SECRET!; router.post('/api/artemis/bootstrap', requireCustomerSession, async (req, res, next) => { try { const runtimeResponse = await fetch(`${runtimeBaseUrl}/api/v1/sdk/customer-sessions`, { method: 'POST', headers: { 'content-type': 'application/json', 'x-sdk-channel-secret': channelSecret, }, body: JSON.stringify({ tenantId, projectId, channelId, verifiedUserId: req.customer.id, customAttributes: { plan: req.customer.plan, region: req.customer.region, }, }), }); const body = (await runtimeResponse.json()) as { bootstrapToken?: string; expiresIn?: number; error?: { code?: string; message?: string }; }; if (!runtimeResponse.ok || !body.bootstrapToken) { res.status(502).json({ error: 'Unable to start Artemis SDK session' }); return; } res.json({ bootstrapToken: body.bootstrapToken, expiresIn: body.expiresIn, }); } catch (error) { next(error); } }); ``` Request body rules: * Send exactly one of `channelId` or `channelName`. * Do not send `permissions`; Runtime derives the effective SDK permissions from the channel's active public API key. * Keep `customAttributes` within the SDK user context size limits. Runtime returns `SDK_TOKEN_TOO_LARGE` when normalized custom attributes exceed the token budget. `channelName` variant: ```ts theme={null} body: JSON.stringify({ tenantId, projectId, channelName: 'web', verifiedUserId: req.customer.id, customAttributes: { plan: req.customer.plan, }, }); ``` Successful Runtime response: ```json theme={null} { "bootstrapToken": "", "tokenEnvelope": "signed", "expiresIn": 300, "tenantId": "tenant_123", "projectId": "project_123", "channelId": "channel_123" } ``` When `sdkTokenEnvelopePolicy` resolves to JWE, `tokenEnvelope` is `"jwe"` and `bootstrapToken` is a 5-segment compact JWE. Recommended customer backend controls: * Authenticate the website user before minting a bootstrap token. * Collect sensitive data and secure custom attributes server-side only. * Keep `verifiedUserId` stable for the customer identity. * Keep the endpoint same-origin or protected by customer auth cookies. * Do not return the channel server secret to the browser. * Do not log `bootstrapToken`, sensitive data, or secure custom attributes. ### Browser SDK changes Use a `bootstrapTokenProvider` so each new init gets a new short-lived token: ```ts expandable=true theme={null} import { AgentSDK } from '@koreai/artemis-web-sdk'; const sdk = new AgentSDK({ endpoint: 'https://runtime.example.com', projectId: 'project_123', bootstrapTokenProvider: async () => { const response = await fetch('/api/artemis/bootstrap', { method: 'POST', credentials: 'include', }); if (!response.ok) { throw new Error('Unable to start chat'); } const body = (await response.json()) as { bootstrapToken: string }; return body.bootstrapToken; }, }); await sdk.connect(); ``` **React** ```tsx expandable=true theme={null} import { AgentProvider, ChatWidget } from '@koreai/artemis-web-sdk/react'; async function getBootstrapToken() { const response = await fetch('/api/artemis/bootstrap', { method: 'POST', credentials: 'include', }); const body = (await response.json()) as { bootstrapToken: string }; return body.bootstrapToken; } export function SupportChat() { return ( ); } ``` **Custom element** ```html expandable=true theme={null} ``` Set the `bootstrapTokenProvider` property before appending the custom element. The widget reads SDK config during its connection/bootstrap lifecycle. ### Validation 1. Customer backend calls `POST /api/v1/sdk/customer-sessions`. 2. Browser calls `/api/v1/sdk/init` with `bootstrapToken` and without `X-Public-Key`. 3. Runtime returns an SDK session token. 4. If `sdkTokenEnvelopePolicy=jwe_required`, confirm returned Hosted Exchange bootstrap/session material is JWE or the flow fails closed. 5. Replay the same bootstrap token and confirm Runtime rejects it. 6. Confirm SDK refresh and WebSocket ticket flows succeed. *** ## Scenario 3: Customer-issued shared-secret JWE This flow requires no new Artemis API call: the customer backend pulls secure data, encrypts the customer bootstrap payload, and the browser passes the compact JWE to the SDK. Shared-secret mode authenticates the issuer by possession of the scoped channel secret and authenticates ciphertext integrity through `A256GCM`. It does not use a separate asymmetric customer signature. ### Studio setup 1. Open the SDK channel in Studio. 2. Set SDK auth mode to `hosted_exchange`. 3. Enable customer-issued JWE. 4. Set key mode to `shared_secret`. 5. Set `maxAgeSeconds` between `60` and `900`. Prefer `300`. 6. Choose whether to keep Runtime-issued Hosted Exchange bootstrap enabled: * `acceptRuntimeIssued=true` allows the Runtime-signed `/customer-sessions` flow to continue during migration. * `acceptRuntimeIssued=false` limits this channel to customer-issued JWE. 7. Save the channel. 8. Rotate the customer-issued JWE secret. 9. Copy the one-time shared secret and `keyId`. 10. Store the shared secret only in the customer backend secret manager. Customer-issued JWE config shape: ```json theme={null} { "customerIssuedJwe": { "enabled": true, "maxAgeSeconds": 300, "acceptRuntimeIssued": true, "keyMode": "shared_secret" } } ``` ### Customer server changes Install a JOSE implementation using the command `npm install jose`. Mint the compact JWE server-side: ```ts expandable=true theme={null} import { CompactEncrypt } from 'jose'; import { randomUUID } from 'node:crypto'; const tenantId = process.env.ARTEMIS_TENANT_ID!; const projectId = process.env.ARTEMIS_PROJECT_ID!; const channelId = process.env.ARTEMIS_SDK_CHANNEL_ID!; const customerJweKeyId = process.env.ARTEMIS_CUSTOMER_JWE_KEY_ID!; const customerJweSharedSecret = process.env.ARTEMIS_CUSTOMER_JWE_SHARED_SECRET!; function decodeSharedSecret(secret: string): Uint8Array { const bytes = Buffer.from(secret, 'base64url'); if (bytes.byteLength !== 32) { throw new Error('Invalid Artemis customer JWE shared secret'); } return new Uint8Array(bytes); } export async function mintSharedSecretBootstrapToken(customer: { id: string; plan: string; region: string; }): Promise { const now = Math.floor(Date.now() / 1000); const payload = { type: 'customer', tenantId, projectId, channelId, verifiedUserId: customer.id, permissions: ['session:send_message', 'session:read'], iat: now, exp: now + 300, jti: randomUUID(), customAttributes: { plan: customer.plan, region: customer.region, }, }; return new CompactEncrypt(new TextEncoder().encode(JSON.stringify(payload))) .setProtectedHeader({ alg: 'dir', enc: 'A256GCM', kid: customerJweKeyId, typ: 'abl-sdk-customer-bootstrap+jwe', cty: 'application/json', epv: 1, tid: tenantId, pid: projectId, cid: channelId, }) .encrypt(decodeSharedSecret(customerJweSharedSecret)); } ``` Expose it through a customer backend endpoint: ```ts theme={null} router.post('/api/artemis/bootstrap', requireCustomerSession, async (req, res, next) => { try { const bootstrapToken = await mintSharedSecretBootstrapToken({ id: req.customer.id, plan: req.customer.plan, region: req.customer.region, }); res.json({ bootstrapToken, expiresIn: 300 }); } catch (error) { next(error); } }); ``` Required JWE header fields: ```json theme={null} { "alg": "dir", "enc": "A256GCM", "kid": "", "typ": "abl-sdk-customer-bootstrap+jwe", "cty": "application/json", "epv": 1, "tid": "", "pid": "", "cid": "" } ``` Required payload fields: ```json theme={null} { "type": "customer", "tenantId": "tenant_123", "projectId": "project_123", "channelId": "channel_123", "verifiedUserId": "customer-user-123", "permissions": ["session:send_message", "session:read"], "iat": 1782380000, "exp": 1782380300, "jti": "customer-generated-uuid", "customAttributes": { "plan": "gold" } } ``` Runtime rejects unsupported top-level claims. Put secure custom data in `customAttributes`, not `secureCustomData`. Customer-issued permission templates: ```ts theme={null} const CHAT_ONLY = ['session:send_message', 'session:read'] as const; const CHAT_WITH_ATTACHMENTS = [ 'session:send_message', 'session:read', 'attachment:read', 'attachment:write', ] as const; const VOICE_ONLY = ['session:voice', 'session:read'] as const; const READ_ONLY = ['session:read'] as const; ``` Runtime intersects these requested permissions with the channel's bound public API key permissions. For example, requesting `session:voice` on a channel whose public key has `voice=false` produces no voice grant. If the intersection is empty, `/api/v1/sdk/init` returns a Hosted Exchange permissions error. ### Browser SDK changes Use the same Hosted Exchange browser configuration as Runtime-signed bootstrap: ```ts theme={null} const sdk = new AgentSDK({ endpoint: 'https://runtime.example.com', projectId: 'project_123', bootstrapTokenProvider: async () => { const response = await fetch('/api/artemis/bootstrap', { method: 'POST', credentials: 'include', }); const body = (await response.json()) as { bootstrapToken: string }; return body.bootstrapToken; }, }); ``` Do not pass `apiKey`, `channelId`, `channelName`, `deploymentSlug`, `userContext`, or `clientSessionIdentifier` in this SDK config. ### Validation 1. Browser calls the customer backend bootstrap endpoint. 2. Customer backend does not call `/api/v1/sdk/customer-sessions`. 3. Browser passes the compact JWE to `/api/v1/sdk/init` as `bootstrapToken`. 4. Runtime accepts the first use. 5. Runtime rejects replay of the same `jti`. 6. Runtime rejects expired tokens and tokens where `exp - iat` exceeds channel `maxAgeSeconds`. 7. Runtime rejects tokens with mismatched `tenantId`, `projectId`, `channelId`, `tid`, `pid`, or `cid`. 8. Rotate the shared secret and confirm: * In-flight tokens minted before rotation still work until expiry. * Newly minted tokens using the old secret fail. *** ## Scenario 4: Customer-issued public-key JWE This is the preferred no-new-Artemis-API-call option for higher-assurance flows. The customer backend signs the payload, encrypts it to Runtime, and the browser passes only the resulting compact JWE. ### Key ownership | Key material | Owner | Stored where | Purpose | | - | - | - | - | | Customer signing private key | Customer | Customer backend secret manager or HSM | Signs the inner JWS. Never leaves the customer environment. | | Customer signing public key | Customer and Studio/Runtime | Studio channel `customerIssuedJwe.customerSigningPublicKey` config | Runtime verifies issuer authenticity. | | Runtime channel private decrypt key | Runtime | Runtime key store / database protected by platform encryption | Decrypts outer JWE. Never leaves Runtime. | | Runtime channel public encrypt key | Runtime and customer | Studio shows safe key metadata after customer JWE key rotation | Customer encrypts outer JWE for Runtime. | ### Studio setup 1. Generate a customer signing key pair in the customer environment. 2. Copy only the customer signing public key. 3. Open the SDK channel in Studio. 4. Set SDK auth mode to `hosted_exchange`. 5. Enable customer-issued JWE. 6. Set key mode to `public_key`. 7. Paste the PEM-encoded customer signing public key. 8. Set `maxAgeSeconds` between `60` and `900`. Prefer `300`. 9. Choose whether `acceptRuntimeIssued` should remain enabled for migration. 10. Save the channel. 11. Rotate the customer-issued JWE key. 12. Copy the Runtime channel public encryption key and `keyId`. 13. Store the Runtime public encryption key and `keyId` in customer backend configuration. Example customer signing key generation: ```bash theme={null} openssl genpkey \ -algorithm RSA \ -pkeyopt rsa_keygen_bits:3072 \ -out customer-signing-private.pem openssl rsa \ -in customer-signing-private.pem \ -pubout \ -out customer-signing-public.pem ``` Upload `customer-signing-public.pem` to Studio. Store `customer-signing-private.pem` only in the customer backend secret manager or HSM. The TypeScript example below expects the private key in PKCS#8 PEM format and the public key in SPKI PEM format. Customer-issued public-key JWE config shape: ```json theme={null} { "customerIssuedJwe": { "enabled": true, "maxAgeSeconds": 300, "acceptRuntimeIssued": true, "keyMode": "public_key", "customerSigningPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" } } ``` ### Customer server changes Install a JOSE implementation using the command `npm install jose`. Mint the signed-then-encrypted bootstrap token: ```ts expandable=true theme={null} import { CompactEncrypt, CompactSign, importPKCS8, importSPKI } from 'jose'; import { randomUUID } from 'node:crypto'; const tenantId = process.env.ARTEMIS_TENANT_ID!; const projectId = process.env.ARTEMIS_PROJECT_ID!; const channelId = process.env.ARTEMIS_SDK_CHANNEL_ID!; const customerJweKeyId = process.env.ARTEMIS_CUSTOMER_JWE_KEY_ID!; const runtimeEncryptionPublicKey = process.env.ARTEMIS_CUSTOMER_JWE_RUNTIME_PUBLIC_KEY!; const customerSigningPrivateKey = process.env.ARTEMIS_CUSTOMER_SIGNING_PRIVATE_KEY!; export async function mintPublicKeyBootstrapToken(customer: { id: string; plan: string; region: string; }): Promise { const now = Math.floor(Date.now() / 1000); const payload = { type: 'customer', tenantId, projectId, channelId, verifiedUserId: customer.id, permissions: ['session:send_message', 'session:read'], iat: now, exp: now + 300, jti: randomUUID(), customAttributes: { plan: customer.plan, region: customer.region, }, }; const signingKey = await importPKCS8(customerSigningPrivateKey, 'RS256'); const innerJws = await new CompactSign(new TextEncoder().encode(JSON.stringify(payload))) .setProtectedHeader({ alg: 'RS256', typ: 'abl-sdk-customer-bootstrap+jws', }) .sign(signingKey); const encryptionKey = await importSPKI(runtimeEncryptionPublicKey, 'RSA-OAEP-256'); return new CompactEncrypt(new TextEncoder().encode(innerJws)) .setProtectedHeader({ alg: 'RSA-OAEP-256', enc: 'A256GCM', kid: customerJweKeyId, typ: 'abl-sdk-customer-bootstrap+jwe', cty: 'application/jose', epv: 1, tid: tenantId, pid: projectId, cid: channelId, }) .encrypt(encryptionKey); } ``` Expose it through a customer backend endpoint: ```ts theme={null} router.post('/api/artemis/bootstrap', requireCustomerSession, async (req, res, next) => { try { const bootstrapToken = await mintPublicKeyBootstrapToken({ id: req.customer.id, plan: req.customer.plan, region: req.customer.region, }); res.json({ bootstrapToken, expiresIn: 300 }); } catch (error) { next(error); } }); ``` Required outer JWE header fields: ```json theme={null} { "alg": "RSA-OAEP-256", "enc": "A256GCM", "kid": "", "typ": "abl-sdk-customer-bootstrap+jwe", "cty": "application/jose", "epv": 1, "tid": "", "pid": "", "cid": "" } ``` Required inner JWS header fields: ```json theme={null} { "alg": "RS256", "typ": "abl-sdk-customer-bootstrap+jws" } ``` The inner JWS payload uses the same claim shape as shared-secret mode. ### Browser SDK changes Use the same Hosted Exchange browser configuration: ```ts theme={null} const sdk = new AgentSDK({ endpoint: 'https://runtime.example.com', projectId: 'project_123', bootstrapTokenProvider: async () => { const response = await fetch('/api/artemis/bootstrap', { method: 'POST', credentials: 'include', }); const body = (await response.json()) as { bootstrapToken: string }; return body.bootstrapToken; }, }); ``` ### Validation 1. Customer backend does not call `/api/v1/sdk/customer-sessions`. 2. Browser passes compact JWE to `/api/v1/sdk/init` as `bootstrapToken`. 3. Runtime decrypts the JWE and verifies the inner JWS. 4. Runtime rejects an unsigned plaintext payload in public-key mode. 5. Runtime rejects wrong `cty` values such as `application/json` in public-key mode. 6. Runtime rejects tokens signed by an unconfigured customer private key. 7. Runtime rejects replay, scope drift, expired tokens, overlong tokens, and disabled-channel tokens. 8. Rotate the Runtime channel public encryption key and confirm: * In-flight tokens minted before rotation still work until expiry. * Newly minted tokens using the retired key fail. *** ## Studio operations checklist For public anonymous SDK: 1. SDK channel auth mode is `anonymous`. 2. Allowed origins include the browser site. 3. Public API key is active and scoped to the project. 4. Channel is active and bound to the expected deployment/environment. For Runtime-signed Hosted Exchange: 1. SDK channel auth mode is `hosted_exchange`. 2. Server secret exists and is stored by the customer backend. 3. `sdkTokenEnvelopePolicy` is selected intentionally. 4. Runtime capability is ready before selecting `jwe_required`. 5. Customer-issued JWE may remain disabled. For customer-issued shared-secret JWE: 1. SDK channel auth mode is `hosted_exchange`. 2. `customerIssuedJwe.enabled=true`. 3. `customerIssuedJwe.keyMode=shared_secret`. 4. `customerIssuedJwe.maxAgeSeconds` matches the customer backend token TTL. 5. Secret has been rotated and copied once to the customer backend. 6. `acceptRuntimeIssued` matches the migration plan. For customer-issued public-key JWE: 1. SDK channel auth mode is `hosted_exchange`. 2. `customerIssuedJwe.enabled=true`. 3. `customerIssuedJwe.keyMode=public_key`. 4. Customer signing public key is configured. 5. Runtime public encryption key has been rotated and copied to the customer backend. 6. `customerIssuedJwe.maxAgeSeconds` matches the customer backend token TTL. 7. `acceptRuntimeIssued` matches the migration plan. ## Studio API automation payloads Use these shapes when automating setup through Studio instead of clicking through the channel configuration UI. Create a Hosted Exchange channel: ```http theme={null} POST /api/runtime/sdk-channels?projectId=project_123 Content-Type: application/json ``` ```json theme={null} { "name": "web", "channelType": "web", "publicApiKeyId": "public_key_record_id", "allowedOrigins": ["https://app.example.com"], "environment": "production", "auth": { "mode": "hosted_exchange", "rotateServerSecret": true }, "config": { "sdkTokenEnvelopePolicy": "jwe_preferred" } } ``` Enable customer-issued public-key JWE on an existing channel: ```http theme={null} PATCH /api/runtime/sdk-channels/channel_123 Content-Type: application/json ``` ```json theme={null} { "auth": { "mode": "hosted_exchange", "rotateCustomerIssuedJweSecret": true }, "config": { "sdkTokenEnvelopePolicy": "jwe_preferred", "customerIssuedJwe": { "enabled": true, "maxAgeSeconds": 300, "acceptRuntimeIssued": true, "keyMode": "public_key", "customerSigningPublicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" } } } ``` Enable customer-issued shared-secret JWE: ```json theme={null} { "auth": { "mode": "hosted_exchange", "rotateCustomerIssuedJweSecret": true }, "config": { "sdkTokenEnvelopePolicy": "jwe_preferred", "customerIssuedJwe": { "enabled": true, "maxAgeSeconds": 300, "acceptRuntimeIssued": true, "keyMode": "shared_secret" } } } ``` Rotation response material: ```json theme={null} { "success": true, "channel": { "...": "..." }, "customerIssuedJweSecret": { "keyId": "customer_jwe_key_id", "keyMode": "public_key", "alg": "RSA-OAEP-256", "enc": "A256GCM", "secretPrefix": "public-key-fingerprint", "publicKey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----", "publicKeyFingerprint": "fingerprint", "status": "active", "rotatedAt": "2026-06-26T00:00:00.000Z" } } ``` For shared-secret mode, `customerIssuedJweSecret.secret` is returned once in the same response. Store it immediately in the customer backend secret manager. It will not be available from later safe metadata reads. ## Customer backend checklist All Hosted Exchange customer backend endpoints should: 1. Require the customer application's own authenticated session. 2. Derive `verifiedUserId` server-side. 3. Fetch sensitive or secure attributes server-side. 4. Put secure attributes under `customAttributes`. 5. Generate unique `jti` values. 6. Use short TTLs. Prefer 5 minutes or less. 7. Avoid logging bootstrap tokens, secure attributes, sensitive data, server secrets, or private keys. 8. Return only `{ "bootstrapToken": "..." }` and optional non-sensitive expiry metadata to the browser. 9. Rate limit token minting per customer session. 10. Alert on repeated invalid token minting, Runtime 401s, and replay failures. Required customer backend material by scenario: | Scenario | Required customer backend material | | - | - | | Public anonymous SDK | None for SDK auth. | | Runtime-signed Hosted Exchange | Hosted Exchange channel server secret. | | Customer-issued shared-secret JWE | Customer JWE `keyId` and shared secret. | | Customer-issued public-key JWE | Customer signing private key, Runtime channel public encryption key, and customer JWE `keyId`. | ## Browser SDK checklist For public anonymous SDK: * Use `apiKey`. * Optional: `channelId`, `channelName`, `deploymentSlug`, `userContext`, `clientSessionIdentifier`. For all Hosted Exchange scenarios: * Use `bootstrapTokenProvider` when possible. * Do not combine Hosted Exchange bootstrap config with `apiKey`. * Do not pass browser `userContext`; put verified data in the backend token. * Do not pass `channelId`, `channelName`, or `deploymentSlug`; Runtime resolves those from the bootstrap token. * `projectId` is still required for bootstrap-token configs. * `sessionMetadata` is allowed, but it is not identity. Use it for safe browser context such as locale, timezone, or UI context. * Make the bootstrap provider call same-origin or protected by customer auth. * Handle bootstrap failure as an auth/session startup failure, not as a bot error. ## Manual runtime probes Use these probes after Studio and customer backend setup. Replace placeholders with environment-specific values and do not paste real secrets into tickets, logs, or shared terminals. Public anonymous SDK init: ```bash theme={null} curl -sS -X POST "$RUNTIME_BASE_URL/api/v1/sdk/init" \ -H "Content-Type: application/json" \ -H "Origin: https://app.example.com" \ -H "X-Public-Key: $PUBLIC_SDK_KEY" \ -d '{ "channelId": "channel_123", "userContext": { "userId": "anonymous-browser-id", "customAttributes": { "page": "support" } } }' ``` Runtime-signed Hosted Exchange bootstrap mint: ```bash theme={null} curl -sS -X POST "$RUNTIME_BASE_URL/api/v1/sdk/customer-sessions" \ -H "Content-Type: application/json" \ -H "X-SDK-Channel-Secret: $SDK_CHANNEL_SECRET" \ -d '{ "tenantId": "tenant_123", "projectId": "project_123", "channelId": "channel_123", "verifiedUserId": "customer-user-123", "customAttributes": { "plan": "gold" } }' ``` Hosted Exchange bootstrap exchange, for Runtime-signed and customer-issued JWE: ```bash theme={null} curl -sS -X POST "$RUNTIME_BASE_URL/api/v1/sdk/init" \ -H "Content-Type: application/json" \ -H "Origin: https://app.example.com" \ -d '{ "bootstrapToken": "" }' ``` WebSocket ticket mint from the SDK session token returned by init: ```bash theme={null} curl -sS -X POST "$RUNTIME_BASE_URL/api/v1/sdk/ws-ticket" \ -H "Content-Type: application/json" \ -H "X-SDK-Token: " \ -d '{}' ``` Negative probes worth running: | Probe | Expected result | | - | - | | Send both `X-Public-Key` and `bootstrapToken` to `/api/v1/sdk/init`. | `400 INVALID_BOOTSTRAP_REQUEST` | | Send a Hosted Exchange bootstrap token with `channelId` in the init body. | `400 INVALID_BOOTSTRAP_REQUEST` | | Send customer bootstrap token with browser `userContext`. | `400 INVALID_BOOTSTRAP_REQUEST` | | Replay the same customer bootstrap token. | `401 Bootstrap token already used` or invalid/expired token response | | Use an origin outside the channel/public key allowlist. | `403 Origin not allowed` | | Use public-key customer JWE with `cty=application/json`. | `401 Invalid or expired bootstrap token` | | Use shared-secret customer JWE with `cty=application/jose`. | `401 Invalid or expired bootstrap token` | ## Deployment checklist Runtime: 1. Configure `JWT_SECRET`. 2. Configure `AUTH_SDK_BOOTSTRAP_SIGNING_SECRET`. 3. Configure `AUTH_SDK_SESSION_SIGNING_SECRET`. 4. Configure `ENCRYPTION_MASTER_KEY` for JWE envelope support. 5. Keep `AUTH_SDK_JWE_ENABLED` unset or `true` for JWE rollout. 6. Configure `REDIS_URL` for tickets, replay protection, and distributed state. 7. Expose Runtime over HTTPS. 8. Verify `/api/projects/:projectId/sdk-jwe-capability` before enforcing JWE. 9. Deploy all Runtime pods with the same auth and encryption settings. Studio: 1. Studio must reach Runtime for channel mutation APIs and JWE capability preflight. 2. Studio users need channel permissions to create/update SDK channel auth settings. 3. Treat one-time revealed secrets as unrecoverable; rotate if they are missed or exposed. Customer backend: 1. Store Runtime endpoint, tenant ID, project ID, and channel ID as deploy config. 2. Store scenario-specific secrets in a secret manager or HSM. 3. Rotate secrets through Studio first, then update customer backend config. 4. Deploy customer backend before switching the browser SDK to Hosted Exchange bootstrap. 5. Use HTTPS and secure cookies for the browser-to-customer bootstrap endpoint. Browser app: 1. Serve an SDK bundle version that supports `bootstrapTokenProvider` and WebSocket `sdk-ticket`. 2. Set Runtime endpoint and project ID per environment. 3. Keep Hosted Exchange bootstrap calls credentialed when the customer session uses cookies. 4. Do not persist bootstrap tokens in local storage. ## End-to-end cutover plan 1. Create or update the SDK channel in Studio. 2. Set allowed origins and deployment binding. 3. For Hosted Exchange, configure channel auth mode and token/JWE settings. 4. Deploy Runtime with required auth, Redis, and encryption configuration. 5. Verify Runtime JWE capability if `jwe_preferred` or `jwe_required` is used. 6. Deploy the customer backend bootstrap endpoint. 7. Validate the backend endpoint with a real customer session. 8. Deploy browser SDK changes. 9. Start with an internal or allowlisted origin. 10. Exercise init, refresh, chat send, attachment if used, WebSocket ticket, and WebSocket connect. 11. Check Runtime logs and audit events for invalid token, replay, origin, or scope failures. 12. For migration channels, keep `acceptRuntimeIssued=true` until all browser and customer backend clients are using customer-issued JWE. 13. When ready, set `acceptRuntimeIssued=false` for customer-issued-only channels, or set `sdkTokenEnvelopePolicy=jwe_required` for Runtime-issued Hosted Exchange channels. ## Expanded scenario coverage matrix Use this matrix for implementation review, QA planning, and customer security approval. A complete rollout should cover the positive path and at least the listed negative path for each scenario in the target environment. | Scenario | Positive path | Negative/security path | Layers covered | | - | - | - | - | | Public anonymous chat | Browser initializes with `apiKey`, `channelId` or `channelName`, sends chat, mints WebSocket ticket, connects with `sdk-ticket`. | Remove origin from allowed origins or disable public API key and confirm new init fails. | Studio, public API key, Runtime init, SDK, WebSocket ticket | | Public anonymous voice | Public API key has `voice=true`; browser initializes and starts voice flow. | Set `voice=false` or disable voice widget capability and confirm voice permission is absent or voice flow is denied. | Studio key permissions, Runtime permissions, SDK voice | | Public anonymous attachment | Public API key has `chat=true`; chat flow uploads/uses attachment where product flow allows it. | Request attachment behavior when chat permission is disabled and confirm attachment permissions are not granted. | Public key permissions, SDK HTTP routes, Runtime auth | | Runtime-signed Hosted Exchange signed | Customer backend calls `/customer-sessions`; browser exchanges returned token through `/sdk/init`; refresh and ws-ticket succeed. | Send wrong `X-SDK-Channel-Secret` or omit both `channelId` and `channelName`; Runtime rejects. | Customer backend, Runtime `/customer-sessions`, Runtime init, SDK | | Runtime-signed Hosted Exchange JWE preferred | Channel uses `sdkTokenEnvelopePolicy=jwe_preferred`; capability ready returns `tokenEnvelope=jwe`. | Test capability-unready lower environment and confirm policy degrades only when configured as preferred. | Studio policy, Runtime JWE capability, token envelope | | Runtime-signed Hosted Exchange JWE required | Channel uses `sdkTokenEnvelopePolicy=jwe_required`; capability ready returns JWE bootstrap/session tokens. | Capability unavailable fails closed with `SDK_JWE_UNAVAILABLE`; signed token should not be accepted. | Deployment config, Runtime capability, token envelope policy | | Customer-issued shared-secret JWE | Customer backend encrypts JSON payload with `alg=dir`, `enc=A256GCM`, `cty=application/json`; Runtime accepts first use. | Wrong shared secret, wrong `cty`, replayed `jti`, expired token, or scope mismatch all fail. | Studio customer JWE config, customer backend JOSE, Runtime verifier, SDK | | Customer-issued public-key JWE | Customer backend signs inner JWS with customer private key, encrypts outer JWE to Runtime public key, and Runtime verifies signature. | Unsigned payload, wrong customer signing key, wrong `cty`, wrong `kid`, or stale Runtime public key fails. | Customer signing keys, Runtime encryption key, Studio config, Runtime verifier | | Customer-issued migration | `acceptRuntimeIssued=true` allows both Runtime-signed and customer-issued bootstrap during rollout. | Set `acceptRuntimeIssued=false` and confirm Runtime-issued `/customer-sessions` tokens no longer bootstrap that channel. | Studio config, migration controls, Runtime bootstrap resolver | | Key rotation | Rotate Hosted Exchange server secret or customer JWE material and update customer backend configuration. | Tokens minted before rotation work only until normal expiry; newly minted tokens with retired material fail. | Studio rotation, secret management, Runtime key store | | Permissions narrowing | Customer-issued token requests a subset such as read-only or voice-only. Runtime intersects with public key permissions. | Request permissions not allowed by the public key and confirm denied or narrowed effective permissions. | Customer backend claims, public key permissions, Runtime permissions | | Session metadata | Browser sends safe `sessionMetadata` such as locale/timezone and widget text resolves as expected. | Oversized `sessionMetadata` fails validation; secrets in metadata are not persisted durably by design. | SDK, Runtime schemas, widget config/localization | Minimum evidence for each selected production scenario: 1. Studio screenshot or API response showing channel auth mode, token envelope policy, customer JWE mode, and active key metadata. 2. Customer backend request/response trace with secrets and sensitive data redacted. 3. Runtime init response showing `channelId`, `permissions`, `tokenEnvelope` when present, and `expiresIn`. 4. SDK browser trace showing no channel secret, customer JWE secret, Runtime private key, or customer signing private key is present client-side. 5. WebSocket ticket response and successful WebSocket connection using `sdk-ticket`. 6. Negative-path evidence for replay, origin, expiry, and key-mode mismatch for customer-issued JWE scenarios. ## Troubleshoot issues and errors | Symptom | Likely cause | Fix | | - | - | - | | `SDK config must provide exactly one bootstrap credential` | Browser config includes both `apiKey` and `bootstrapToken` or neither. | Use `apiKey` for public anonymous, or `bootstrapTokenProvider` for Hosted Exchange. | | Runtime rejects bootstrap with public-key-only fields | Hosted Exchange SDK config includes `channelId`, `channelName`, `deploymentSlug`, `userContext`, or `clientSessionIdentifier`. | Remove those fields from Hosted Exchange browser config. | | `config.customerIssuedJwe requires auth.mode=hosted_exchange` | Customer-issued JWE was configured on an anonymous channel. | Switch channel auth mode to Hosted Exchange first. | | `customerSigningPublicKey is required when keyMode=public_key` | Public-key mode enabled without the customer signing public key. | Generate customer signing keys and paste the PEM public key in Studio. | | `customer_issued_jwe_content_type_mismatch` | Shared-secret/public-key mode used the wrong outer `cty`. | Use `application/json` for shared secret and `application/jose` for public key. | | `customer_issued_jwe_key_mode_mismatch` | Channel key mode changed but customer backend still uses old key material. | Rotate key material for the active mode and update customer backend config. | | Replay failures | Customer backend reused a `jti` or browser retried a static bootstrap token. | Generate a fresh token for every init and prefer `bootstrapTokenProvider`. | | Expired or overlong token failures | `exp`, `iat`, or TTL exceeds channel `maxAgeSeconds`. | Keep clocks in sync and use TTL at or below the channel setting. | | CORS failure | Browser origin not allowed for the channel/public API key. | Add exact HTTPS origin in Studio. | | WebSocket fails after init succeeds | Browser bundle is old or Redis/ticket store is unavailable. | Use SDK with `sdk-ticket` support and verify Redis runtime config. | | `jwe_required` fails closed | Runtime JWE capability is unavailable. | Verify `ENCRYPTION_MASTER_KEY`, `AUTH_SDK_JWE_ENABLED`, and capability route readiness. | # Send custom SIP headers on voice transfers Source: https://koreai.mintlify.app/agent-platform/sending-custom-sip-headers When a voice agent transfers a live call to an external SIP destination, you can include custom SIP headers with the transfer. The receiving system can use these headers for call routing or to carry business context, such as a queue or skill hint, campaign ID, transfer reason, or any other custom payload. * This capability applies to the **Kore Voice Gateway** voice channel. - This capability is currently in **Alpha**. The platform builds and sends the transfer request with the configured custom headers, but header delivery depends on the gateway and receiving SIP peer. Validate the behavior in your SIP environment before using it in production. This guide covers transfers that use the **SIP REFER** method. ## How custom SIP headers are applied Custom SIP headers are controlled at three levels: ```text theme={null} Platform-supported headers ↓ Project-level Allowed Transfer Headers ↓ Agent-level header names and values ↓ SIP REFER transfer ``` A header is forwarded only when: * Its name is supported by the platform. * It is added to the project's Allowed Transfer Headers list. * A header value is configured for the transfer in the agent. ## Platform-supported headers The platform supports a default fixed set of header names for voice transfers. Supported header names are: ```text theme={null} x-custom-data x-session-id x-tenant-id x-agent-id x-contact-id x-project-id x-correlation-id x-request-id x-queue x-skill x-campaign user-to-user x-transfer-reason ``` * Header names are case-insensitive and normalized to lowercase. * To add a custom header to an agent transfer, ensure it is added to the project's\*\* Allowed Transfer Headers\*\* list. You cannot add new header name directly during agent routing configuration. ## Configure allowed transfer headers for the project At the project level, select which platform-supported headers to forward during voice transfers. * Go to **Project Settings** > **Agent Transfer** > **Voice Gateway**. * Enable **Header Passthrough**. * Under **Allowed Transfer Headers**, review the predefined SIP headers and delete any headers that are not required for agent transfers. * For each header, choose how its value is populated: * Caller-supplied (inbound): Uses the value received in the incoming SIP header. * Fixed value: Sends the value you specify. * AI chooses: Lets the AI determine the value at transfer time (supported headers only). * In addition to the predefined SIP headers, you can add custom SIP headers that are sent during agent transfers. To add a custom header, under **Add header** field, enter the SIP header name (for example, x-my-header) and click **Add**. Configure the new header by selecting its value source and, if applicable, specifying a fixed value. * Save the configuration. For example, if the project allows the following, agents can use these headers in transfer configurations. Other platform-supported headers are not forwarded unless they are also added to this list. ```text theme={null} x-queue x-skill x-transfer-reason ``` Use custom headers only when the receiving SIP system expects them. Header names must start with `x-` and also follow your SIP provider's or receiving application's requirements. ## Configure SIP headers in the agent After a header is allowed at the project level, configure its value for the agent's transfer. 1. Open the agent in **Studio** > **Agent Editor**. 2. Open the **Escalation** configuration. 3. Under **Routing**, expand **Voice Settings**. 4. Set **Transfer Method** to **SIP REFER**. 5. Under SIP Headers, add the required header name and value: * Header name — Select or specify a header permitted by the project's Allowed Transfer Headers configuration, for example, `x-queue`. * Header value — Specify the value to send, for example, `tier2`. 6. Select **+ Add Header** to configure additional headers. 7. Save the agent. Example: ```yaml theme={null} ESCALATE: routing: connection: voice: transfer_method: refer sip_headers: x-queue: "tier2" x-transfer-reason: "billing" ``` In this example: * `x-queue` and `x-transfer-reason` must be supported by the platform. * Both headers must be added to the project's Allowed Transfer Headers list. * The agent supplies the values tier2 and billing for the transfer. ## Troubleshooting | Symptom | Likely cause / fix | | - | - | | Header never arrives | Name isn't on the platform allowlist → use an allowed name. | | No custom headers at all | **Header Passthrough** is off → turn it on. | | Only some headers arrive | They were excluded by your project **Allowed Transfer Headers** list, or you exceeded the 10-header limit. | | Value looks cut off | Value exceeded 256 characters and was truncated. | | Need a `BYE`-based release with headers | Not supported yet (the `bye` method is blocked in settings). | | Want a re-INVITE instead of REFER | Choose **SIP INVITE** as the transfer method (requires a valid `sip:`/`sips:` target URI). | # Sessions Source: https://koreai.mintlify.app/agent-platform/sessions A session represents a conversation between an agent in an Artemis project and a user. The platform creates a session at the beginning of a conversation and uses it for each message exchange between the user and the agent. The session persists throughout the entire conversation, even as the user is transferred between agents. It stores the conversation context, maintains state across interactions, and remains active until it is closed. ## Session Lifecycle A session moves through the following set of states from creation to eventual archival. | Status | Meaning | | :- | :- | | `active` | Session in progress; agent is currently processing. | | `idle` | Session in progress; waiting for user input. | | `ended` | Session completed by user action, or an idle timeout fired after COMPLETE in the DSL (timeout is configurable per project per channel). | | `completed` | Agent finished by calling COMPLETE. | | `escalated` | Session transferred to a human. | | `abandoned` | Session abandoned by the user after an idle period. | | `failed` | Fatal execution error or a guardrail violation. | | `archived` | Moved to cold storage after the retention period ends. | ## Session identification and metadata Every session has a unique Session ID. Sessions are always scoped to a tenant and a project. To view sessions, go to **Projects** > **Sessions**. | Field | Description | | - | - | | Session ID | Unique identifier in the format `s-\` (for example, s-193fdef8-xxxx-xxxx-xxxx-96020ced84d8). | | Caller phone number | Filter by the phone number captured on the session. Enter a number to find every session for a specific caller. | | Agent | The agent that the session is bound to. | | Environment | The project environment where the session ran (for example, Development, Staging, or Production). | | Channel | The communication channel used for the session, such as Web, Voice, WhatsApp, or Slack. | | Created At | Date and time of session creation. | | Trace events | Total number of trace events generated during the session. | | Duration | Length of time for which the session was active. | | Messages | Count of messages exchanged in the session | | Cost | Estimated LLM cost in USD | ## Session details Click a session row to open the detail panel. It lists the following details. ### Engagement | **Field** | **Description** | | - | - | | Status | The current lifecycle state of the session (for example, Active). | | Disposition | The outcome/resolution state assigned to the session (for example, Pending). | | Engaged time | Total duration the user was actively engaged with the agent during the session. | | Segments | Number of distinct conversational segments within the session. | | Escalated to human | Whether the session was transferred to a human agent (Yes/No). | | Workflow suspension | Whether the session's workflow was suspended mid-execution (Yes/No). | | First interaction | Timestamp of the first user interaction in the session. | | Last interaction | Timestamp of the most recent interaction (user or agent) in the session. | | Last user activity | Timestamp of the most recent user-originated activity in the session. | ### Overview It summarizes session metadata, timing, and connection-timeout behavior. | **Section** | **Field** | **Description** | | - | - | - | | **Session Overview** | Agent | The agent that handled the session. | | | Session | Unique identifier for the session, in the format `s-`. | | | Messages | Total number of messages exchanged between the user and the agent during the session. | | | Trace Events | Total number of trace events generated during the session. This value indicates the amount of execution activity captured for debugging and analysis. | | | Connection | Current connection status of the session (for example, `Connected` or `Disconnected`). | | **Timestamps** | Started | Date and time when the session started. | | | Finished | Date and time when the session ended. | | **Models Used** | Model | The AI model used during the session. If multiple models are used, each model is listed. | | **Token Breakdown** | Tokens In | Total number of input tokens sent to the model during the session. | | | Tokens Out | Total number of output tokens generated by the model during the session. | | | Total Tokens | Combined number of input and output tokens consumed during the session. | | | Cache Read | Number of tokens served from the model cache instead of being processed again. | | | LLM Calls | Total number of Large Language Model (LLM) invocations made during the session. | | | Cost | Estimated LLM usage cost for the session, expressed in USD. | | **Timeout Diagnostics** | Browser idle | Maximum period the Studio client (browser) can remain inactive before the user is automatically logged out. | | | Access token TTL | Maximum lifetime of the Studio access token before it expires and must be refreshed or reissued. | | | Runtime idle | Time before **conversation/runtime session** terminates due to **no user messages** | | | Runtime max age | Maximum lifetime of a runtime session, regardless of activity. After this period, the runtime is restarted or replaced. | | **Model Resolution** | Model resolution | Displays model-resolution information for the session, when available. | ### Traces The **Traces** tab shows a chronological, step-by-step execution log of what the agent did during the session - decisions made, tools called, memory changes, and the final response. **Trace summary header** | **Field** | **Description** | | - | - | | Trace ID | Short identifier for this trace (for example,`#b2f1e372`). | | Status | Overall trace outcome (for example, `Completed`). | | Interactions | Number of interactions in this trace. | | Agents | Number of agents involved in this trace. | | LLM calls | Number of LLM calls made during this trace. | | Tool calls | Number of tool calls made during this trace. | | Duration | Total execution time for the trace (for example, `32ms`). | | Guardrails | Guardrail policy status for this trace (for example, `no active policy`). | The rest of the Traces tab renders as a single collapsible timeline for the trace, with one row per agent step and a chronological sequence of colored event cards within each step. ### Errors Lists errors or warnings recorded in the session. ### Data This tab shows the field collection status from the agent's gather flow and data in the session context. ### Voice The **Voice** tab only appears for voice-channel sessions. Use it to investigate call quality, latency, speech performance, network conditions, and call termination details.   | **Card** | **Field** | **Description** | | - | - | - | | **Call Overview** | Duration | Total call duration (for example, 39s). | | | Turns | Number of conversational turns in the call. | | | Caller | Unique identifier for the caller. | | | Disconnect | Who/what ended the call and the disconnect code. | | | Avg E2E | Average end-to-end response latency.  | | | Barge-ins | Count and rate of barge-in events (user interrupting the agent) — for example, 2 (100% of turns). | | | DTMF Input | Count of DTMF (keypad) inputs received. | | | Containment | Whether the call was resolved without escalation (for example, Contained / "AI resolved"). | | | Call Phase | The call phase when this data was captured (for example, Greeting), with the responsible agent noted. | | **Gateway ⇄ Runtime Verbs** | Event count | | | **Network Quality (MOS)** | Inbound (Caller → Platform) | | | | Outbound (Platform → Caller) | | | **SIP Ladder** | Reveal control | | | **TTS Quality** | Combined MOS | Combined TTS quality score, blending application and network metrics. | | | Proxy MOS | Application-level quality score ("App-level quality"). | | | Network MOS | Network-level quality score, from RTCP data. | | | Avg TTFB | Average time-to-first-byte for TTS responses. | | **Barge-in Events** | Total | Total number of barge-in events. | | | Rate | Barge-in rate as a percentage of turns. | | | Turns | Number of turns with a barge-in. | | | Event Details | Per-turn breakdown (for example, "Turn 1 — SPEECH — Agent spoke 0.7s before interrupt"). | | **Call Activity** | Activity bar | Visual breakdown of call time by Agent / User / Processing / Dead Air percentages. | | | Agent Speaking | Time and percentage the agent was speaking. | | | User Speaking | Time and percentage the user was speaking. | | | Silence | Total silence duration and percentage of call. | | | Call Duration | Total call duration (repeated from Call Overview). | | | Silence Breakdown | Splits silence into categories (for example, "Processing (E2E — LLM think time)" vs. "Dead Air — waiting gaps"). | | **Speech (realtime)** | Provider | The real-time speech-to-speech model provider (for example, Google). Note: shown only because this agent uses a realtime speech-to-speech model, which has no separate STT/TTS stages — these are turn-level metrics from the provider. | | | Turns | Number of turns measured. | | | Avg turn latency | Average latency per turn. Shows -- when unavailable. | | | Barge-ins | Barge-in count (repeated from Call Overview/Barge-in Events). | | **Call Termination** | Disconnect By | Which party ended the call (for example, platform). | | | SIP Method | SIP method used to terminate (for example, BYE). | | | SIP Status | SIP status code (for example, 200). | | | Reason | Termination reason string (for example, BYE → 200). | | | Homer Data | Availability of Homer (SIP capture tool) data for this call (for example, Available). | ### IR The **IR** tab shows the compiled definition of the agent that produced this session. This information represents the deployed agent definition, not the runtime state captured in the session.  | **Sub-tab** | **Content** | | - | - | | ABL Source | The raw ABL source code defining the agent, as authored. | | IR JSON | Compiled version of the source. | | Test Context | This sub-tab lets you construct a **test scenario** to launch a new session against this agent's current definition, pre-loaded with specific starting conditions. Use it to reproduce or probe agent behavior. | ### Performance The **Performance** tab shows model- and tool-invocation-level performance data for the session. **LLM & Tools:** Summarizes every LLM call and tool call made during the session. Expand each event to view the payload. **Logs**: Shows a raw, timestamped execution log for the session - a flat, chronological stream of system-level events. ### Call recording Use the **Call Recording** to review completed voice conversations. It helps you verify what was said during a call, correlate the audio with the conversation and execution traces, investigate issues, and download recordings for offline review when available.  To view or play a recording, the following conditions must be met: * Call recording must be enabled for the project. Learn how to enable recordings for a project. * You must have the **View Recordings** permission to view, play, and download the recordings. By default, this permission is granted to all system roles. Recordings in Sessions **Recording Details** | **Field** | **Description** | | - | - | | Recording ID | Unique identifier for the call recording. | | Duration | Total length of the recording. | | File size | Size of the recording file. | | Status | Current recording state. | | Retention | Date and time until which the recording is retained. | | Refresh | Refreshes the recording status if processing is still in progress. | | Download | Downloads the recording when it is available. You can download only after the Playback and delivery status is \*\*completed. | **Recording States** | State | Description | | - | - | | **Pending** | The call has ended, and the recording is queued for processing. | | **Fetching** | The recording is currently being collected and processed. | | **Completed** | The recording is available for playback and download. | | **Failed** | The recording could not be collected or processed. | | **Deleted** | The recording has been permanently removed. Only the recording entry remains for historical reference. | Recording processing typically completes within a few minutes, but can take up to 15 minutes for longer recordings. Use Refresh to check the latest status. * The waveform visually represents the conversation. * **Green** bars represent the caller audio. * **Purple** bars represent the agent audio. * The **Synced to** indicator shows the conversation timestamp that corresponds to the current playback position. * Use **Open conversation** to navigate directly to the matching message in the **Conversation** tab. * **Use Open traces** to jump to the corresponding point in the **Traces** tab. #### Play a recording Click **Play** to start playback. The recording loads on the first play request, so initial playback may take a few moments. #### Download a recording Click **Download** to save the recording locally. The **Download** button is available only when the recording status is **Completed**. #### Recording retention period Completed recordings are retained until the **Retention** date shown in the Call Recording panel. When the retention period expires: * The recording is permanently deleted. * The recording entry remains in the session history, but its status changes to **Deleted**. * Deletion runs as a scheduled process, so recordings may remain available for up to **one hour** after the displayed retention time before they are removed. A project admin or owner can update the Retention Period in [Project Settings](/agent-platform/administration/agent-behavior-settings#recording). #### Use call recording Use call recordings to investigate issues with a voice call. * Listen to the recording. * Jump to the corresponding conversation. * Open the trace at the same timestamp. * Review Performance for slow model or tool calls. * Inspect Voice telemetry for latency or network issues. #### Compliance considerations for recordings * **Tell the caller yourself.** The platform doesn't play a "this call is recorded" announcement or show the caller a recording indicator. If your region requires consent or a notice, add it to your agent's opening prompt. * **Storage location.** Recordings are stored in the storage configured for your workspace. Use your own storage account if the audio must stay in a specific region. #### Troubleshoot if recording doesn't start Check these in order - the first "no" is usually the cause. 1. Does **Workspace Settings** show the **Call Recording** section? If not, contact support team to add the feature to your workspace. 2. Is the workspace-level switch turned on and saved? If not, ask your account admin to enable it. 3. Does the project **Settings** menu show **Recording**? If not, the workspace switch is off. 4. Is **Enable call recording** on for the project, and did you click **Save changes**? 5. Was the call started after you saved the setting? Recording only applies to new calls. 6. Did you wait about 15 minutes and click **Refresh**? If you've verified all six checks and the recording is still unavailable, contact the support team and provide the environment details, project name, session ID, and the approximate call date and time. #### Turn recording off | Goal | Action | Effect | | - | - | - | | Stop recording new calls in one project | Turn off **Enable call recording** in project **Settings** → **Recording** | New calls aren't recorded. Existing recordings stay and remain playable. | | Hide recording across the whole workspace | Turn off the workspace-level **Call Recording** switch | Recording settings and audio are hidden in every project. Nothing is deleted. | | Remove the feature completely | Contact support team | Every recording surface disappears. | ## API access Session details are also available via APIs. See [session APIs](/agent-platform/api-reference/analytics-list-session-summaries). ## Session Closure Closing a session finalizes the conversation and is essential for platform operations. ### What happens when a session closes When a session closes, regardless of how it is closed, the platform performs the following lifecycle operations: * Persists any pending conversation data. * Records the session disposition and final status. * Publishes a session completion event for downstream services. * Finalizes billing metrics. A session is the billable unit. Session duration and engaged time are calculated when the session closes. * Finalizes reporting metrics. The session `disposition` and `status` fields are used to derive reporting metrics such as containment, resolution, and abandonment rates. * Releases session resources associated with the conversation. ### Closing a session A session can be closed by the agent, automatically by the platform, or when a conversation is transferred to another system. Whenever a session is closed, the platform records the following fields for the session: * `disposition` — why the conversation ended. * `status` — current state of the session. The `disposition` field can have the following values. The `status` is automatically derived from the `disposition`. | Disposition | Meaning | Status | | - | - | - | | `completed` | The conversation reached its natural end | `completed` | | `abandoned` | The user walked away | `abandoned` | | `agent_hangup` | The agent side dropped the conversation | `abandoned` | | `transferred` | Handed to a human or another system | `escalated` | | `failed` | Ended because of a system failure | `abandoned` | | `timeout` | Closed automatically after inactivity | `ended` | | `unengaged` | Opened but the user never really engaged | `ended` | ### Closing from agent DSL The most common way to end a session is for the agent to determine that the conversation is complete. You define the completion conditions in the agent using the `COMPLETE:` block. When one of the conditions is met, the agent sends the configured final response and closes the session with the disposition and status as `completed`. ```JSON theme={null} COMPLETE: WHEN: issue_resolved == true RESPOND: "Glad I could help. Have a great day!" WHEN: intent contains "bye" RESPOND: "Thanks for contacting us. Goodbye!" ``` * In a multi-agent workflow, place the COMPLETE block in the agent that owns the conversation. If a COMPLETE executes in a child agent that is designed to return control to its parent, it completes only that child agent's work and returns to the parent. It does not end the overall conversation. To end the conversation, define the COMPLETE block in the supervisor agent or in a dedicated closing agent that does not return. * Conditions are evaluated in the order they are defined. The first matching condition is applied. * The final response is delivered to the user before the session closes. * The session is closed with the disposition and status as `completed`. Note that When `TERMINAL: false` is specified in a `COMPLETE` block, the current interaction ends, but the session remains active. The response is sent to the user, and their next message starts a new interaction within the same session. [Learn More.](/agent-platform/abl/reference/multi-agent-and-supervisor#complete) ### Automatic session closure The platform automatically closes sessions when they exceed configured inactivity or configured session lifetime limits, or when channel-specific events indicate that the conversation has ended. The platform can automatically close a session when it exceeds either of the following limits: * **Idle timeout**: The maximum period of user inactivity before the session is closed. * **Maximum session age**: The maximum lifetime of a session, regardless of activity. When a session is closed because of either limit, it is recorded with the `timeout` disposition and the `ended` status. For asynchronous messaging channels such as WhatsApp, SMS, and email, a session may remain active across long periods of inactivity. The platform does not split these conversations into multiple sessions. Instead, it internally tracks active engagement periods or engagement segments for billing and reporting while maintaining a single session. * The recorded end time reflects when the session actually became inactive, not when the timeout was detected. * The default values of the idle timeout and max age vary by plan. For SDK channels, sessions closed because of inactivity are recorded with `terminal_source: sdk_idle_timeout` and the `timeout` disposition. Sessions explicitly ended by the application or the user are recorded with `terminal_source: sdk_end_session` and the `completed` disposition. Use these fields in the [Query builder in Analytics explorer](/agent-platform/analytics-insights/analytics-explorer#query) to build queries that : * Identify sessions that timed out because users became inactive. * Distinguish application-initiated session closures from inactivity timeouts. * Measure SDK session abandonment and user completion trends. ## Sessions and agent transfers When a conversation is transferred to a human agent or another external system, the session disposition is set to 'transferred` and status is set to 'escalated`. The session lifecycle after the external system completes the conversation on their end depends on the routing configuration. * When Post-Agent Action is set to **End Conversation**, the session is terminated and the disposition depends on the **end reason** the contact centre sends: | End reason sent | Disposition | Status | | - | - | - | | `transferred` | `transferred` | `escalated` | | `completed` / `agent_completed` / `conversation_complete` | `completed` | `completed` | | `abandoned` / `user_left` / `user_exit` | `abandoned` | `abandoned` | | `agent_hangup` | `agent_hangup` | `abandoned` | | `failed` / `error` | `failed` | `abandoned` | | Missing or unrecognized value | `completed` | `completed` | Post-agent action is **Return to bot**. The conversation session is **not** closed. It remains active and no final `disposition` is recorded. * Transfer metadata is written onto the session. * The status changes from `escalated` back to `active`. * The bot resumes handling messages as usual. The session is closed later when the conversation ends through the agent, automatic timeout, or another transfer that terminates the conversation. # Supported AI models Source: https://koreai.mintlify.app/agent-platform/supported-models Agent Platform supports a wide range of AI models across multiple providers, allowing you to switch models without changing your agent architecture. Supported models include LLMs, embedding models, audio models, and realtime models. The supported models list includes the provider name, model name, and model identifier used for configuration. ## OpenAI | Display Name | Model ID | | - | - | | GPT-4o Transcribe | gpt-4o-transcribe | | GPT Transcribe | gpt-transcribe | | GPT Live Transcribe | gpt-live-transcribe | | GPT Realtime Whisper | gpt-realtime-whisper | | Whisper 1 | whisper-1 | | Text Embedding Ada 002 | text-embedding-ada-002 | | Text Embedding 3 Small | text-embedding-3-small | | Text Embedding 3 Large | text-embedding-3-large | | o4 Mini (2025-04-16) | o4-mini-2025-04-16 | | o4 Mini | o4-mini | | o3 Mini (2025-01-31) | o3-mini-2025-01-31 | | o3 Mini | o3-mini | | o3 (2025-04-16) | o3-2025-04-16 | | o3 | o3 | | o1 (2024-12-17) | o1-2024-12-17 | | o1 | o1 | | GPT-5.4 Pro | gpt-5.4-pro | | GPT-5.4 Nano | gpt-5.4-nano | | GPT-5.4 (2026-03-05) | gpt-5.4-2026-03-05 | | GPT-5.4 | gpt-5.4 | | GPT-5.3 Codex | gpt-5.3-codex | | GPT-5.2 Chat (Latest) | gpt-5.2-chat-latest | | GPT-5.2 (2025-12-11) | gpt-5.2-2025-12-11 | | GPT-5.2 | gpt-5.2 | | GPT-5.1 Chat (Latest) | gpt-5.1-chat-latest | | GPT-5.1 (2025-11-13) | gpt-5.1-2025-11-13 | | GPT-5.1 | gpt-5.1 | | GPT-5 Nano (2025-08-07) | gpt-5-nano-2025-08-07 | | GPT-5 Nano | gpt-5-nano | | GPT-5 Mini (2025-08-07) | gpt-5-mini-2025-08-07 | | GPT-5 Mini | gpt-5-mini | | GPT-5 Chat (Latest) | gpt-5-chat-latest | | GPT-5 (2025-08-07) | gpt-5-2025-08-07 | | GPT-5 | gpt-5 | | GPT-4o Search Preview | gpt-4o-search-preview-2025-03-11 | | GPT-4o Search Preview | gpt-4o-search-preview | | GPT-4o Realtime Preview (2025-06-03) | gpt-4o-realtime-preview-2025-06-03 | | GPT-4o Realtime Preview (2024-12-17) | gpt-4o-realtime-preview-2024-12-17 | | GPT-4o Realtime | gpt-4o-realtime-preview | | GPT-4o Mini Transcribe | gpt-4o-mini-transcribe | | GPT-4o Mini Realtime | gpt-4o-mini-realtime-preview | | GPT-4o Mini Audio Preview (2024-12-17) | gpt-4o-mini-audio-preview-2024-12-17 | | GPT-4o Mini Audio Preview | gpt-4o-mini-audio-preview | | GPT-4o Mini (2024-07-18)Vision | gpt-4o-mini-2024-07-18 | | GPT-4o MiniVision | gpt-4o-mini | | GPT-4o Audio Preview (2025-06-03) | gpt-4o-audio-preview-2025-06-03 | | GPT-4o Audio Preview (2024-12-17) | gpt-4o-audio-preview-2024-12-17 | | GPT-4o Audio Preview | gpt-4o-audio-preview | | GPT-4o (2024-11-20) | gpt-4o-2024-11-20 | | GPT-4o (2024-08-06) | gpt-4o-2024-08-06 | | GPT-4o | gpt-4o | | GPT-4.1 Nano (2025-04-14) | gpt-4.1-nano-2025-04-14 | | GPT-4.1 Nano | gpt-4.1-nano | | GPT-4.1 Mini (2025-04-14) | gpt-4.1-mini-2025-04-14 | | GPT-4.1 Mini | gpt-4.1-mini | | GPT-4.1 (2025-04-14) | gpt-4.1-2025-04-14 | | GPT-4.1 | gpt-4.1 | | GPT Realtime Mini (2025-10-06) | gpt-realtime-mini-2025-10-06 | | GPT Realtime Mini | gpt-realtime-mini | | GPT Realtime 1.5 | gpt-realtime-1.5 | | GPT Realtime (2025-08-28) | gpt-realtime-2025-08-28 | | GPT Realtime | gpt-realtime | | GPT Image 1 Mini | gpt-image-1-mini | | GPT Image 1 | gpt-image-1 | | GPT Audio Mini (2025-10-06) | gpt-audio-mini-2025-10-06 | | GPT Audio Mini | gpt-audio-mini | | GPT Audio (2025-08-28) | gpt-audio-2025-08-28 | | GPT | gpt-audio | | DALL-E 3 | dall-e-3 | | DALL-E 2 | dall-e-2 | ## Anthropic | Display Name | Model ID | | - | - | | Claude Sonnet 4.6 | claude-sonnet-4-6-20260217 | | Claude Sonnet 4.5 | claude-sonnet-4-5-20250929 | | Claude Sonnet 4 | claude-sonnet-4-20250514 | | Claude Opus 4.8 | claude-opus-4-8 | | Claude Opus 4.7 | claude-opus-4-7 | | Claude Opus 4.6 | claude-opus-4-6-20260204 | | Claude Opus 4.5 | claude-opus-4-5-20251101 | | Claude Opus 4.1 | claude-opus-4-1-20250805 | | Claude Opus 4 | claude-opus-4-20250514 | | Claude Haiku 4.5 | claude-haiku-4-5-20251001 | | Claude 3.5 Haiku | claude-3-5-haiku-20241022 | ## Azure OpenAI | Display Name | Model ID | | - | - | | Azure: Text Embedding 3 Large | azure/text-embedding-3-large | | Azure: Text Embedding 3 Small | azure/text-embedding-3-small | | Azure: Text Embedding Ada 002 | azure/text-embedding-ada-002 | | GPT Audio (Azure) | GPT-Audio | | GPT Audio Mini (Azure) | GPT-Audio-Mini | | GPT Image 1 (Azure) | GPT-Image-1 | | GPT Image 1 Mini (Azure) | GPT-Image-1-Mini | | GPT Realtime (Azure) | GPT-Realtime | | GPT Realtime Mini (Azure) | GPT-Realtime-Mini | | GPT-4.1 (Azure) | GPT-4.1 | | GPT-4.1 Mini (Azure) | GPT-4.1-Mini | | GPT-4.1 Nano (Azure) | GPT-4.1-Nano | | GPT-4o (Azure) | GPT-4o | | GPT-4o Audio Preview (Azure) | GPT-4o-Audio-Preview | | GPT-4o Mini (Azure) | GPT-4o-Mini | | GPT-4o Mini Audio Preview (Azure) | GPT-4o-Mini-Audio-Preview | | GPT-4o Mini Transcribe (Azure) | GPT-4o-Mini-Transcribe | | GPT-5 (Azure) | GPT-5 | | GPT-5 Chat (Azure) | GPT-5-Chat | | GPT-5 Mini (Azure) | GPT-5-Mini | | GPT-5 Nano (Azure) | GPT-5-Nano | | GPT-5.1 (Azure) | GPT-5.1 | | GPT-5.1 Chat (Azure) | GPT-5.1-Chat | | GPT-5.2 (Azure) | GPT-5.2 | | GPT-5.2 Chat (Azure) | GPT-5.2-Chat | | GPT-5.4 (Azure) | GPT-5.4 | | GPT-5.4 Mini (Azure) | GPT-5.4-Mini | | GPT-5.4 Nano (Azure) | GPT-5.4-Nano | | GPT-5.4 Pro (Azure) | GPT-5.4-Pro | | GPT-5.5 (Azure) | GPT-5.5 | | o1 (Azure) | O1 | | o3 (Azure) | O3 | | o4 Mini (Azure) | O4-Mini | ## Microsoft Foundry ### Anthropic | Display Name | Model ID | | - | - | | Claude Haiku 4.5 (Microsoft Foundry) | microsoft\_foundry/claude-haiku-4-5-20251001 | | Claude Opus 4.6 (Microsoft Foundry) | microsoft\_foundry/claude-opus-4-6 | | Claude Opus 4.7 (Microsoft Foundry) | microsoft\_foundry/claude-opus-4-7 | | Claude Sonnet 4.5 (Microsoft Foundry) | microsoft\_foundry/claude-sonnet-4-5-20250929 | | Claude Sonnet 4.6 (Microsoft Foundry) | microsoft\_foundry/claude-sonnet-4-6 | ### OpenAI | Display Name | Model ID | | - | - | | GPT-4o (Microsoft Foundry) | microsoft\_foundry/gpt-4o | | GPT-5 (Microsoft Foundry) | microsoft\_foundry/gpt-5 | ### Meta | Display Name | Model ID | | - | - | | Llama 3.3 70B (Microsoft Foundry) | microsoft\_foundry/llama-3.3-70b-versatile | ### Mistral | Display Name | Model ID | | - | - | | Mistral Large (Microsoft Foundry) | microsoft\_foundry/mistral-large-latest | ### Cohere | Display Name | Model ID | | - | - | | Command R+ (Microsoft Foundry) | microsoft\_foundry/command-r-plus-08-2024 | ## Google AI Models | Display Name | Model ID | | - | - | | Gemini 2.0 Flash | gemini-2.0-flash | | Gemini 2.0 Flash Lite | gemini-2.0-flash-lite | | Gemini 2.5 Flash | gemini-2.5-flash | | Gemini 2.5 Flash Image | gemini-2.5-flash-image | | Gemini 2.5 Flash Lite Preview (09-2025) | gemini-2.5-flash-lite-preview-09-2025 | | Gemini 2.5 Flash Native Audio (09-2025) | gemini-2.5-flash-native-audio-preview-09-2025 | | Gemini 2.5 Flash Native Audio (12-2025) | gemini-2.5-flash-native-audio-preview-12-2025 | | Gemini 2.5 Flash Preview (05-20) | gemini-2.5-flash-preview-05-20 | | Gemini 2.5 Flash Preview (09-2025) | gemini-2.5-flash-preview-09-2025 | | Gemini 2.5 Pro | gemini-2.5-pro | | Gemini 3 Flash Preview | gemini-3-flash-preview | | Gemini 3 Pro Image Preview | gemini-3-pro-image-preview | | Gemini 3 Pro Preview | gemini-3-pro-preview | | Gemini 3.1 Flash Lite | gemini-3.1-flash-lite | | Gemini 3.1 Flash Live | gemini-3.1-flash-live-preview | | Gemini 3.1 Pro Preview | gemini-3.1-pro-preview | | Gemini 3.5 Flash | gemini-3.5-flash | | Gemini 3.8 Live | gemini-3.8-live | | Gemini Live 2.5 Flash | gemini-live-2.5-flash | | Gemini Live 2.5 Flash Native Audio (Preview) | gemini-live-2.5-flash-preview-native-audio-09-2025 | | Gemini Multilingual Embedding 002 | text-multilingual-embedding-002 | | Gemini Text Embedding 004 | text-embedding-004 | ## Groq | Display Name | Model ID | | - | - | | Whisper Large V3 Turbo | whisper-large-v3-turbo | | Whisper Large V3 | whisper-large-v3 | | Qwen QwQ 32B (Reasoning) | qwen-qwq-32b | | Mixtral 8x7B | mixtral-8x7b-32768 | | Llama 3.3 70B Versatile | llama-3.3-70b-versatile | | Llama 3.1 8B Instant | llama-3.1-8b-instant | | Llama 3.1 70B Versatile | llama-3.1-70b-versatile | | Llama 3 8B | llama3-8b-8192 | | Llama 3 70B | llama3-70b-8192 | | Gemma 2 9B IT | gemma2-9b-it | | DeepSeek R1 Distill Qwen 32B | deepseek-r1-distill-qwen-32b | | DeepSeek R1 Distill Llama 70B | deepseek-r1-distill-llama-70b | ## Cohere | Display Name | Model ID | | - | - | | Command R7B Arabic | command-r7b-arabic-02-2025 | | Command R7B | command-r7b-12-2024 | | Command R+ | command-r-plus-08-2024 | | Command R (08-2024) | command-r-08-2024 | | Command R | command-r | | Command A | command-a-vision-07-2025 | | Command A | command-a-03-2025 | | Cohere Embed Multilingual v3 | embed-multilingual-v3.0 | | Cohere Embed English v3 | embed-english-v3.0 | | Cohere Embed English Light v3 | embed-english-light-v3.0 | | Aya 32B | c4ai-aya-vision-32b | | Aya Expanse 32B | c4ai-aya-expanse-32b | ## Mistral AI | Display Name | Model ID | | - | - | | Pixtral Large (Latest) | pixtral-large-latest | | Pixtral 12B | pixtral-12b-2409 | | Open Mixtral 8x7B | open-mixtral-8x7b | | Open Mixtral 8x22B | open-mixtral-8x22b | | Open Mistral 7B | open-mistral-7b | | Mistral Small 4 | mistral-small-4 | | Mistral Small (Latest) | mistral-small-latest | | Mistral Medium (Latest) | mistral-medium-latest | | Mistral Large (Latest) | mistral-large-latest | | Mistral Embed | mistral-embed | | Ministral 8B (Latest) | ministral-8b-latest | | Ministral 3B (Latest) | ministral-3b-latest | | Codestral (Latest) | codestral-latest | ## Fireworks | Display Name | Model ID | | - | - | | Qwen QwQ 32B (Reasoning) | accounts/fireworks/models/qwen-qwq-32b-preview | | Qwen 2.5 Coder 32B | accounts/fireworks/models/qwen2p5-coder-32b-instruct | | Mixtral 8x7B Instruct | accounts/fireworks/models/mixtral-8x7b-instruct | | Mixtral 8x22B Instruct | accounts/fireworks/models/mixtral-8x22b-instruct | | Llama 3.3 70B Instruct | accounts/fireworks/models/llama-v3p3-70b-instruct | | Llama 3.2 11B Vision Instruct | accounts/fireworks/models/llama-v3p2-11b-vision-instruct | | Llama 3.1 405B Instruct | accounts/fireworks/models/llama-v3p1-405b-instruct | | DeepSeek R1 (Reasoning) | accounts/fireworks/models/deepseek-r1 | ## Together AI | Display Name | Model ID | | - | - | | Qwen 2.5 72B Instruct Turbo | Qwen/Qwen2.5-72B-Instruct-Turbo | | Mixtral 8x22B Instruct | mistralai/Mixtral-8x22B-Instruct-v0.1 | | Llama 3.3 70B Instruct Turbo | meta-llama/Llama-3.3-70B-Instruct-Turbo | | Llama 3.1 8B Instruct Turbo | meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo | | DeepSeek V3 | deepseek-ai/DeepSeek-V3 | | DeepSeek R1 (Reasoning) | deepseek-ai/DeepSeek-R1 | ## Perplexity | Display Name | Model ID | | - | - | | Sonar Reasoning Pro | sonar-reasoning-pro | | Sonar Reasoning | sonar-reasoning | | Sonar Pro (Online) | sonar-pro | | Sonar (Online) | sonar | ## DeepSeek | Display Name | Model ID | | - | - | | DeepSeek V3.2 | deepseek-v3.2 | | DeepSeek Reasoner | deepseek-reasoner | | DeepSeek Coder | deepseek-coder | | DeepSeek Chat | deepseek-chat | ## xAI (Grok) | Display Name | Model ID | | - | - | | Grok Beta | grok-beta | | Grok 4.20 | grok-4.20 | | Grok 4.1 Fast (Reasoning) | grok-4-1-fast-reasoning | | Grok 4.1 Fast (Non-Reasoning) | grok-4-1-fast-non-reasoning | | Grok 4.1 | grok-4-1 | | Grok 4 | grok-4 | | Grok 2 Vision | grok-2-vision-1212 | ## AWS Bedrock | Display Name | Model ID | | - | - | | Claude Sonnet 4.6 (Bedrock) | anthropic.claude-sonnet-4-6-v1:0 | | Claude Sonnet 4 (Bedrock) | anthropic.claude-sonnet-4-20250514-v1:0 | | Claude Opus 4.6 (Bedrock) | anthropic.claude-opus-4-6-v1:0 | | Claude Haiku 4.5 (Bedrock) | anthropic.claude-haiku-4-5-20251001-v1:0 | ## Vertex AI | Display Name | Model ID | | - | - | | Gemini 3 Pro Preview (Vertex) | gemini-3-pro-preview-vertex | | Gemini 3 Pro Image Preview (Vertex) | gemini-3-pro-image-preview-vertex | | Gemini 3 Flash Preview (Vertex) | gemini-3-flash-preview-vertex | | Gemini 2.5 Pro (Vertex) | gemini-2.5-pro-vertex | | Gemini 2.5 Flash Preview (09-2025, Vertex) | gemini-2.5-flash-preview-09-2025-vertex | | Gemini 2.5 Flash Lite Preview (09-2025, Vertex) | gemini-2.5-flash-lite-preview-09-2025-vertex | | Gemini 2.5 Flash Lite (Vertex) | gemini-2.5-flash-lite-vertex | | Gemini 2.5 Flash Image (Vertex) | gemini-2.5-flash-image-vertex | | Gemini 2.5 Flash (Vertex) | gemini-2.5-flash-vertex | | Gemini 2.0 Flash Lite (Vertex) | gemini-2.0-flash-lite-vertex | | Gemini 2.0 Flash (Vertex) | gemini-2.0-flash-vertex | ## Ultravox | Display Name | Model ID | | - | - | | UltravoxVoice | fixie-ai/ultravox | # Test agents Source: https://koreai.mintlify.app/agent-platform/testing Test and debug Agentic projects before deploying them to production. The platform provides interactive chat and voice test sessions, execution traces, session inspection, and automated evaluation workflows to help validate agent behavior, tool execution, routing, and response quality. The platform supports the following testing capabilities: | Capability | Purpose | | - | - | | Studio Test Chat & Voice | Interactively test agents and workflows using chat or voice | | Execution Traces | Inspect decisions, tool calls, handoffs, and state changes | | Session Inspection | Analyze runtime behavior for a session | | Trace Analysis | Detect slow spans, failures, and anomalies | | Test Personas & Scenarios | Create repeatable conversation flows | | Evaluations (Evals) | Measure quality and performance using automated evaluators | ## Test in Studio Use Studio to validate agent behavior interactively before deploying to production. Depending on your project configuration, you can test agents using chat or voice. Both use the same execution pipeline as production, including: * Tool calls and their results. * Flow step transitions. * Handoffs between agents. * Guardrail checks on inputs and outputs. ### Start a test session 1. Open your project in Studio. 2. To test the full project, go to the **Overview** page and click **Test your agents**. For multi-agent projects, Studio defaults to the project's entry agent (usually the supervisor). 3. To test an individual agent in isolation, go to the **Agent** page and click **Chat with Agent**. Test Agent 4. Or use the **Chat** option next to any agent on the **Overview** page. Test Agent 5. Studio creates a test session. * **Chat:** Type a message in the chat input and press **Enter**. * **Voice:** Select the **Voice** tab, choose a configured test target, and click **Start Call**. Speak to the agent using the built-in softphone. A live transcript is displayed during the call. 6. During testing, Studio displays runtime information such as: * Executed flow steps * Tool invocations * Response time * Live conversation transcript (voice sessions) ## Debug view The Session Debug view provides real-time visibility into agent execution during a conversation. Use it to inspect responses, trace execution flow, monitor token usage, and diagnose runtime issues. The left panel displays active and historical chat sessions. The center panel shows the live conversation between the user and the agent, including routed responses and workflow progression. The right-side diagnostics panel provides detailed runtime insights such as trace events, model usage, token consumption, session identifiers, and timeout diagnostics in real-time. | Signal | What it means | What to do | | - | - | - | | High response latency on a turn | A tool call is slow or the LLM loop ran multiple iterations | Check the trace for slow `tool_call` spans; consider adding `ON_ERROR` retry limits | | Unexpected handoff | Supervisor routed to wrong agent | Review the `handoff_match` trace event and refine `WHEN` conditions | | Tool called with wrong parameters | `GATHER` field mismatch or type error | Check `gather_extraction` trace events and validate field types in ABL | | Guardrail triggered unexpectedly | Output hit a content or format boundary | Review `guardrail_eval` trace events and adjust guardrail thresholds | | Agent asks for data already provided | Session state not passed during handoff | Check `PASS` declarations in `HANDOFF` blocks | For voice test sessions, the debug view also displays the live call transcript. After the call is complete, you can inspect the session and its execution traces in the same way as chat sessions. ## Using traces to debug Use traces to inspect every decision, tool call, state update, and handoff during execution. 1. Open your project in Studio. 2. Navigate to Sessions and select a session. 3. Click the Traces tab to view the full execution trace. 4. Use filters to narrow results by event type, agent, or time range. ### Trace structure Each session produces a hierarchical execution trace organized into spans. ``` Session +-- Turn 1 | +-- LLM Call | +-- Tool Call | | +-- Tool Result | +-- Response +-- Turn 2 | +-- Decision | +-- Handoff | +-- Response ``` For repeatable, automated testing beyond manual chat, the platform supports Personas, Scenarios, and Evals. [Learn More](/agent-platform/evals). # Tools overview Source: https://koreai.mintlify.app/agent-platform/tools-overview Tools extend agents' capabilities by enabling them to retrieve data, execute logic, interact with external systems, and invoke workflows at runtime. The platform supports the following tool types: | Tool type | Description | | - | - | | [Workflow Tools](#workflow-tools) | Build reusable workflow-based tools using the visual workflow builder. | | [HTTP Tools](#http-tools) | Connect to external APIs and services through HTTP requests. | | [Code Tools](#code-tools) | Execute custom JavaScript or Python logic. | | [Knowledge Base Tools](#knowledge-base-tools) | Retrieve information from connected enterprise knowledge sources. | | [MCP Tools](#mcp-tools) | Connect to Model Context Protocol (MCP) servers and use their exposed tools. | | [WSDL Repository](#wsdl-repository) | Upload and manage WSDL assets for SOAP services and create reusable SOAP-based HTTP tools from discovered operations. | | [Inbuilt Tools](/agent-platform/inbuilt-tools/index) | Use tools that the Artemis runtime provides for voice transfers, IVR and DTMF, human-agent handoff, and attachment handling. | | [Actions](#actions) | Integrate with supported third-party applications and use their predefined actions as tools within agents and workflows. | | [Agent Tables](#tables) | Read, query, and manage structured data stored in Tables using built-in database operations. | Tools Use the Tools page to: * Create and manage tools * Register workflows as tools * Import and export tool definitions For detailed configuration steps, see the documentation for each tool type. *** ## Workflow tools Workflow tools allow agents to invoke reusable workflows during runtime. A workflow can be registered as a tool and attached to one or more agents. Registered workflow tools expose workflow inputs as tool parameters that agents can invoke during interactions. Workflow tools support: * Multi-step execution logic * Human approval and data-entry flows * Conditional branching * Integration actions * Reusable business processes For details about registering workflows as tools and attaching them to agents, see: * [Register workflows as tools](/agent-platform/workflows#register-workflow-as-a-tool) * [Attach workflow tools to agents](/agent-platform/workflows#attach-workflow-tools-to-agents) *** ## HTTP tools HTTP tools let agents call external APIs to fetch data or trigger actions. The agent provides the required parameters, and the platform handles authentication, request construction, and response mapping. For details, see [HTTP Tools](/agent-platform/tools/http). *** ## Code tools Code Tools let you define custom logic using JavaScript or Python that agents can invoke as part of a workflow. They bridge the gap between LLM reasoning and deterministic, programmatic operations, enabling your agents to reliably transform data, call APIs, or run business logic. For details, see [Code Tools](/agent-platform/tools/code). *** ## Knowledge base tools The Knowledge Base tool enables agents to retrieve relevant information from enterprise knowledge sources during conversations. It connects the agent to one or more configured knowledge bases and allows it to search indexed content such as documents, web pages, and data from external systems. For detail, see [Knowledge Base and Knowledge Tools](/agent-platform/knowledge). *** ## MCP tools MCP (Model Context Protocol) tools connect an agent to an external MCP-compatible server that exposes tools and capabilities, enabling agents to dynamically discover, access, and orchestrate tools across systems. For details, see [MCP Tools](/agent-platform/tools/mcp). *** ## WSDL repository The WSDL Repository allows you to upload and manage WSDL assets for SOAP-based services. Uploaded WSDLs are parsed to discover available operations, which can then be used to create reusable SOAP-based HTTP tools. For details, see [WSDL Repository](/agent-platform/tools/wsdl-repo). *** ## Actions Actions allow agents to interact with supported third-party applications by invoking predefined operations, such as creating records, sending notifications, or updating data, without requiring custom API development. For details, see [Actions](/agent-platform/tools/actions). *** ## Tables Table tools enable agents to read, query, and manage structured data stored in Agent Tables. You can create reusable tools that perform operations such as retrieving, inserting, updating, deleting, or counting records, allowing agents to work with persistent business data without connecting to an external database. For details, see [Agent Tables](/agent-platform/agent-tables). # Actions Source: https://koreai.mintlify.app/agent-platform/tools/actions Actions enable agents and workflows to interact with external applications such as Gmail, Jira, Slack, GitHub, Salesforce, and many others. Instead of creating custom HTTP integrations, you can create reusable action tools using the platform's prebuilt integrations. An action tool combines an integration, one of its supported actions, an authentication profile, and optional default parameter values. Once created, the tool can be used across multiple agents and workflows. ## How actions work * Configure an integration for the external application you want to connect to. * Create an action tool using the configured integration. * Select the action to expose and associate an authentication profile. * Add the configured action as a tool to an agent or workflow. * At runtime, the agent invokes the action to interact with the external application and uses the returned response to continue execution. ## Prerequisite Before you create an action: * Integate the external application (for example, Gmail, Jira, Slack, or Salesforce) with Agent Platform. * Configure a valid authentication profile if the integration requires authentication to work. ## Create an action To create an action tool: 1. Log in to the platform and go to **Tools**. 2. Click **New Tool** and select **Action**. 3. Select an Integration. The integration catalog displays all supported providers grouped into categories such as:AI & Development, Communication, CRM & Sales, and others. Use the search box or browse the categories to locate the required integration. 4. Click **Continue**. T 5. Next, configure the **Action** by providing the required information. **Basic Information**: Provide a unique name to identify the tool and an explanatory description that helps an agent to determine when to invoke this tool. **Integration & Action** Select the connector action that the tool executes. The supported actions are available in the menu. | Field | Description | | - | - | | **Integration** | The selected provider. | | **Action** | The operation to execute, such as *Send Email*, *Create Issue*, or *Post Message*. | | **Auth Profile** | Credentials used when the tool executes. | Some integrations require authentication before the available actions can be retrieved. **Authentication Profile** * Authentication profiles contain the credentials required to access the selected integration. * Select an existing authentication profile or create one if necessary. * The selected credentials are automatically used whenever the tool runs. **Input Parameters** After an action is selected, the platform automatically loads its required input schema. Configure the parameters as required. Each parameter displays: * Parameter name * Data type * Required or optional status * Description * Default value You can optionally provide default values for any parameter. * If the agent supplies a value during execution, that value overrides the configured default. * Leave a parameter empty if you want the agent to determine its value at runtime. **Output Parameters** Choose how the connector response should be returned. You can: * Return the complete connector response. * Return only selected fields by defining a custom output. Use custom output when your agent needs only a subset of the connector response. **Variable Namespaces** Variable namespaces determine which runtime variables are available to the tool during execution. Select the namespaces that the tool should be able to access. **Scenario Fixtures** Scenario fixtures provide deterministic responses during testing and evaluations. Use fixtures when you want repeatable tool behavior without invoking the external service. 6. Save the Action. Click **Save** to add the action to the project. The action appears under the **Actions** tab in the Tools library and can now be assigned to agents or used within workflows. ## Edit an action To modify an existing action: 1. Log in to the platform and go to **Tools**. 2. Open the required action. 3. Select the **Configuration** tab and update as required. ## Test an action Use the **Testing** tab to verify that the configured action executes successfully before assigning it to an agent. Testing validates: * Authentication. * Required parameters. * Connector execution. * Response returned by the external application. Use testing to ensure the action behaves as expected before deploying it in production. ## Assign an action to an agent After creating an action: 1. Open the required agent. 2. Navigate to **Tools**. 3. Add the required action. The agent can invoke the action whenever it determines that the action is appropriate to answer a user's request. For example: | Integration | Example Actions | | - | - | | Gmail | Send email, Search emails, Create draft | | Jira | Create issue, Search issues, Add comment | | Slack | Send message, Create channel | | GitHub | Create issue, Create pull request | | Salesforce | Create lead, Update record | ## Use an action in a workflow Actions can also be used from workflow **Tool** steps. When used in a workflow: * The integration is inherited from the action tool. * The selected action is reused. * Authentication is reused. * Input parameters can be mapped to workflow variables. * Output can be consumed by subsequent workflow steps. # Code tools Source: https://koreai.mintlify.app/agent-platform/tools/code Code Tools let you define custom logic using JavaScript or Python that agents can invoke as part of a workflow. They bridge the gap between LLM reasoning and deterministic, programmatic operations, enabling your agents to reliably transform data, call APIs, or run business logic. ## Step 1: Basic info Provide the basic details of the tool. **Name**: Unique identifier for the tool. **Description**: Briefly describe the tasks of the tool. This description is used by the LLM while selecting the tools for task execution. ## Step 2: Code and config **Runtime**: Select the execution environment for your tool's code. Agent Platform supports **JavaScript** and **Python**. **Code**: Write the business logic for processing. * You must define a `main()` function. This is the entry point the platform calls when the tool is invoked. * The `main()` function must return a value. Example: `return main($param)`. * Parameters are injected into the code using a `$` prefix. For example, a parameter named `customer_name` is accessed as `$customer_name` in your code. **Parameters**: Define the inputs this tool accepts. These parameters are sent to the LLM as a JSON Schema. The parameters are injected into the code using the \$ prefix. Click + **Add** to add a new parameter manually, or click Parse to auto-detect parameters from your main() function signature. | Field | Description | | - | - | | **Name** | The parameter name, prefixed with $(for example.$data). This is how you reference it in code. | | **Type** | Data type - String, Number, Boolean, Object. | | **Required** | Toggle whether this parameter must always be provided. | | **Description** | A natural-language description sent to the LLM to explain what this parameter represents (for example. "The customer's full name"). This field is mandatory. | | **Default value** | An optional fallback value that is used if the parameter is not supplied. | **Generated JSON Schema** The platform automatically generates a JSON Schema from your defined parameters. You can inspect it by expanding **View Generated Input Schema**. This is sent to the LLM to describe the required parameters for the tool execution. **Advanced Settings** * **Return Type**: Specifies the data type of the value your tool returns. This is used in the DSL signature and the LLM schema. Common values: object, string, array, and boolean. Default: object * **Memory (MB)**: The memory allocated to the tool's isolated container during execution, in megabytes. Default: 256 * **Timeout (ms)**: The maximum time (in milliseconds) the tool is allowed to run before it is terminated. Set this based on the expected complexity and latency of your tool's logic. Default: 5000. * **Variable Namespace**: Namespace that the tool can access. By default, every tool can access ‘Default’ namespace. ## Step 3: Test and review Provide sample values for the input variables and **Run Test**. Verify the response from the tool. Check Logs and parameters for debugging purposes. # HTTP tools Source: https://koreai.mintlify.app/agent-platform/tools/http HTTP tools connect agents to external APIs and services to fetch data, submit forms, or trigger external services. The agent invokes the tool based on its reasoning, passing the required parameters — the platform handles authentication, request construction, and response mapping. ## Configuring an HTTP tool Navigate to **Tools** > **+ New Tool** > **HTTP**. ### Step 1: Provide basic information about the tool **Tool Name**: A unique name for the tool. **Description**: Brief description of the tool. This description is used by the agent to identify when to invoke the tool. ### Step 2: Configure your API endpoint, authentication, and request settings The Configuration step defines the API endpoint, authentication, and request structure. You can populate these fields manually or click Import from cURL to automatically parse a cURL command into the configuration fields. **Protocol**: Select REST for standard HTTP APIs or SOAP for XML-based web services. **Endpoint**: Enter the full URL of the API endpoint. **Method**: Select the HTTP method for the request. **Authentication**: Select the authentication method the API requires. When authentication is configured, use the **Auth Profile Reference** field to reference a stored auth profile. The platform resolves credentials from the auth profile at runtime. Configure auth profiles in Workspace settings under Auth Profiles. Alternatively, provide the OAuth fields directly, as per the selected authentication method. **Query Parameters**: Define key-value pairs to append to the endpoint URL. Click **+ Add** to add a parameter. **Headers**: Define custom headers to include with every request. Click **+ Add** to add a header. You can also use variables. \*\*Parameters \*\*define the inputs the LLM provides when invoking this tool. The platform uses these to construct the request payload and inform the LLM of the data it needs to supply. Click **+ Add** to define a parameter manually, or click Parse to auto-detect parameters from `{{input.X}}` references already used in the endpoint URL, headers, or query parameters. Each parameter requires a name, a type, a description, and a required status. The description is critical, as the LLM uses it to determine what value to provide. **Advanced Settings** Advanced Settings controls the tool's reliability and performance behavior. | Field | Default | Description | | - | - | - | | Return Type | object | The data type returned by the tool. Used in the DSL signature and LLM schema to define what the agent can expect from the tool response. | | mTLS | None | Selects an mTLS profile to present a client certificate during the TLS handshake. Configure this only when the destination service requires mutual TLS authentication. | | Timeout (ms) | 30000 | Maximum time, in milliseconds, the platform waits for the HTTP request to complete before timing out. Maximum supported value: 120000 ms. | | Retries | 0 | Number of times the platform retries the request if it fails. Set to 0 to disable retries. | | Retry Delay (ms) | 1000 | Time in milliseconds to wait between retry attempts. | | Retry Strategy | Legacy | Determines how retry attempts are performed when a request fails. | | Rate Limit/min | Unlimited | Maximum number of requests per minute. Leave blank for no limit. | | Circuit Breaker | Off | Opens the circuit after consecutive request failures to prevent repeated calls to an unhealthy service. When enabled, configure the failure threshold and reset time. | | Failure Threshold | 5 | Number of consecutive request failures required before the circuit breaker opens and temporarily blocks further requests. | | Reset Time (ms) | 60000 | Time, in milliseconds, the circuit breaker remains open before allowing another request to determine whether the service has recovered. | | Idempotency Header | None | Specifies the HTTP header used to send an idempotency key with requests. Use this when the target service supports idempotent operations to prevent duplicate processing of repeated requests. | ### Step 3: Testing the tool After the tool is created, go to the Testing tab. Click Run Test and provide sample values. Validate the request sent and the response received from the service. ## Attaching tool to the agent **Via UI** * Go to the \*\*Tools \*\*page under **Agent**. * Click **Attach Tool**. * Add the selected Tool. **Via DSL** * Open the agent DSL editor by clicking DSL at the top of the agent page. * In the editor, type `/http-tool` to trigger the tool insertion shortcut. * Select the tool from the list and click **Insert Tool**. The platform inserts the tool definition into the DSL. * Click **Compile** to validate and **Save** to apply. # MCP tools Source: https://koreai.mintlify.app/agent-platform/tools/mcp Connect agents to external tools and services using the Model Context Protocol. MCP (Model Context Protocol) is an open standard that lets AI agents interact with external tools and services through a single, consistent interface. Instead of building a custom integration for each tool, agents connect to MCP servers and access all the tools they expose through one standardized protocol. [Learn More](https://modelcontextprotocol.io/docs/getting-started/intro). **Without MCP:** Connecting to multiple external services requires a separate integration with custom logic for each one. **With MCP:** The agent communicates with all services through a single interface, dramatically reducing development complexity. ## When to use MCP is particularly suited for tools hosted on external servers, shared toolsets across teams, and cases where you want clear separation between tool logic and agent logic. | Use case | Why MCP works | | - | - | | CRM integration | Connect to the CRM provider's MCP server | | Enterprise tools | Share tools across multiple apps | | Third-party services | Use pre-built MCP tool providers | | Microservices | Each service exposes tools via MCP | ## How MCP works MCP uses a client-server architecture with three components: | Component | Role | | - | - | | **MCP Server** | Hosts and exposes tools to clients | | **MCP Client** | The Platform, discovers available tools and invokes them | | **MCP Protocol** | Standardized communication layer (HTTP or SSE) between client and server | ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% flowchart LR subgraph Project["Project"] MCPClient["MCP Client
(built into the Platform)"] end MCPClient -- "MCP Protocol (HTTP/SSE)" --> MCPServer subgraph ExternalSystem["External System"] MCPServer["MCP Server"] MCPServer --> ToolA["Tool A"] MCPServer --> ToolB["Tool B"] MCPServer --> ToolC["Tool C"] MCPServer --> ToolD["Tool D"] end ``` ### Interaction workflow 1. **Tool Discovery** - Agent Platform MCP client connects to the MCP server and retrieves the list of available tools. 2. **Intent Detection** - The LLM receives the user query and the tool list, then identifies the appropriate tool. 3. **Tool Invocation** - The client sends a structured request with the tool name and required parameters. 4. **Execution** - The server runs the tool and returns results to the client. 5. **Response Generation** - The agent uses the tool output to formulate a natural language response. ```mermaid actions={false} theme={null} %%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent', 'primaryColor': '#EAF8E4', 'primaryTextColor': '#1a1a1a', 'primaryBorderColor': '#5CC83A', 'lineColor': '#5CC83A', 'secondaryColor': 'transparent', 'tertiaryColor': 'transparent', 'clusterBkg': 'transparent', 'clusterBorder': '#5CC83A', 'titleColor': '#1a1a1a', 'clusterLabelBackground': 'transparent', 'edgeLabelBackground': '#EAF8E4'}}}%% sequenceDiagram actor User participant LLM participant MCP Client participant MCP Server MCP Client->>MCP Server: 1. Tool Discovery (pre query execution) MCP Server-->>MCP Client: 2. Tool metadata list User-->>MCP Client: 3. User Query MCP Client-->>LLM: 4. Tool list & user query LLM->>MCP Client: 5. Selected Tool MCP Client->>MCP Server: 6. Invoke selected tool MCP Server-->>MCP Client: 7. Response MCP Client-->>LLM: 8. Tool Response LLM-->>User: 9. User friendly answer ``` ### Example ```text theme={null} User: "What's the weather in Dubai today?" 1. MCP Client connects to the MCP server and discovers available tools during configuration. 2. Agent identifies need for weather data and selects: `getWeatherForecast`. 3. Invokes with: { "location": "Dubai" } 4. Server returns: "36°C, Partly cloudy, Wind: 15 km/h" 5. Agent responds: "The weather in Dubai today is partly cloudy with a temperature of 36°C and light winds." ``` ## Configure MCP server Navigate to **Tools** > **MCP Servers**, provide the following and **Register Server**. | Field | Description | | - | - | | Server name | A unique, meaningful name for the MCP server | | Transport | The transport protocol — SSE (Server-Sent Events) or Streamable HTTP.

**SSE (Server-Sent Events)**: Streaming responses for real-time updates using server sent events.

**Streamable HTTP**: Unified HTTP transport to handle bidirectional streaming interactions between clients and servers over a single endpoint. | | Server URL | The endpoint URL of the MCP server | | Authentication | Expand the Authentication section to configure secure access. Enable **Use Auth Profile** to use a predefined profile. Supported methods:

- API Key
- OAuth
- Bearer Token
- Basic Authentication | | Environment variables | Use the Environment Variables section to define key-value pairs required by the MCP server at runtime. Click **Add** to configure them. | Do the following after successfully registering an MCP server: * Configure connection settings. * Test server connectivity. * View discovered tools. * Import tools into the platform. ### Configure connection settings | Field | Description | | - | - | | **Transport** | Select the transport protocol used to communicate with the MCP server. | | **Server URL** | The endpoint URL of the MCP server that the platform connects to for tool discovery and execution. | | **Connection Timeout (ms)** | Specifies how long the platform waits to establish a connection to the MCP server before timing out. | | **Request Timeout (ms)** | Specifies the maximum time to wait for a response after a request is sent to the MCP server. Requests exceeding this duration fail with a timeout. | | **Rate limit** | Specifies the maximum number of requests per minute that can be sent to the MCP server. This limit is enforced at the MCP server level and is **shared across all tools** exposed by the server. Leave this field empty to allow **unlimited requests**. | | **Auto Reconnect** | Automatically attempts to re-establish the connection if communication with the MCP server is interrupted. | | **Max Reconnect Attempts** | Specifies the maximum number of reconnection attempts before the platform stops trying to reconnect to the MCP server. | | **Authentication** | Select the authentication profile used to securely connect to the MCP server. Authentication profiles store credentials separately from the server configuration. | | **Custom Headers** | Adds HTTP headers that are included with every request sent to the MCP server. Header values can be resolved dynamically at runtime. | | **Parameters** | Define input parameters that are shared across tools hosted by the MCP server. Parameters can be parsed from incoming requests or added manually to establish the tool's input contract. | | **UCP mode** | Enables Universal Commerce Protocol (UCP) mode for the MCP server. When enabled, the platform injects the UCP agent metadata into every request and returns the complete UCP response payload. | | **Agent profile URI** | Specifies the URI of the agent profile used for UCP discovery and interoperability. Applicable only when UCP mode is enabled. | | **Idempotent tools** | Select the tools that can safely be retried. The platform assigns a unique idempotency key to each invocation to help downstream services identify and safely process retry requests. | | **Environment Variables** | Configure encrypted environment variables that are securely injected into MCP requests at runtime. These variables are not exposed in the server configuration. | | **Priority** | Defines the execution priority of the MCP server relative to other configured servers. Higher-priority servers can be preferred during tool selection, depending on the runtime configuration. | | **Tags** | Add tags to categorize and organize MCP servers. Tags can help filter and identify servers within the project. | ## Test connection Click **Test** to verify server connectivity and validate the current configuration. This confirms server reachability, authentication validity, transport configuration, and request handling. ## Discover and import tools After registering a server, the platform automatically discovers the tools it exposes. You can then: * Click **Import** next to a specific tool to add it individually * Click **Import All** to add all discovered tools at once Once imported, tools can be attached to one or more agents. ## View and test tools Click on a tool to view its description, metadata, input and output parameters and to test the tool. Go to the Testing and Run Test. Provide sample values for input fields and verify the response. ## Refresh MCP server Use the **Refresh** icon to rediscover tools from the server. Do this when new tools are added, existing tools are updated, or tool metadata changes. Tool definitions are fetched at configuration time and aren't automatically synced. ## Attach MCP tools to an agent Go to the selected agent, then navigate to **Tools** > **Attach Tool**. Select tools from the list. MCP tool names are prefixed with the MCP server name when attached to an agent. You can also attach tools via the ABL file using the `/mcp-tool` insert command: ```yaml theme={null} TOOLS: get_weather(location: string) -> {temp: number, conditions: string} type: mcp server: "weather-service" tool: "get_current_weather" description: "Get current weather for a destination" ``` where, * server - MCP server name * tool - Tool name on the MCP server # WSDL repository Source: https://koreai.mintlify.app/agent-platform/tools/wsdl-repo The WSDL Repository lets you upload and manage Web Services Description Language (WSDL) files for SOAP-based services. After you upload a WSDL file, the platform parses the service definition and makes the SOAP operations available to create SOAP-based HTTP tools. The WSDL Repository centralizes WSDL assets so that you can reuse it across workflows and agents. ## Prerequisites Make sure that: * The WSDL file is a valid file. * The WSDL contains a valid SOAP service definition. * The service endpoints referenced in the WSDL are reachable from the platform deployment environment. ## Upload a WSDL 1. Navigate to your project and open **Tools**. 2. Select the **WSDL Repository** tab. 3. Enter the asset details - name and description. 4. Click **Upload WSDL**. Select the WSDL file from your local filesystem. After you upload the WSDL, the platform takes the following actions: * Validates the WSDL. * Parses the service definition. * Discovers available services and operations. * Extracts request and response schemas. * Stores the WSDL as a reusable asset. * Makes the operations available to create SOAP-based HTTP tools. **Dry Run** — Identifies and lists the operations from the uploaded WSDL, without creating any tools. Use this option to preview the HTTP tools that the platform will generate from the operations defined in the uploaded WSDL. **Create Tools** — Creates SOAP-based HTTP tools from the WSDL operations in bulk. The platform names each tool after its WSDL operation name, prepended with the value in the **Name prefix** field (for example, prefix `soap` + operation `FahrenheitToCelsius` → `soap_fahrenheit_to_celsius`). The prefix applies globally to every operation in the asset. ## Manage WSDL assets Uploaded WSDL files appear in the **Assets** list, searchable by name. Selecting an asset shows: | Field | Description | | :- | :- | | **Operations** | Number of operations discovered in the uploaded WSDL. | | **Dependents** | Number of tools or workflows that are using this WSDL asset. | | **Latest version** | Current version of the uploaded WSDL asset. | | **Name prefix** | Prefix applied to the names of tools generated from the WSDL operations. | | **Endpoint override** | Optional. Overrides the service endpoint defined in the WSDL. For example, you can redirect requests from a development or test endpoint defined in the WSDL to `https://billing.example.com/soap`. Leave this field blank to use the endpoint specified in the WSDL. | From the asset page, you can: **Add version** — Upload a new version of the WSDL under the same asset, preserving version history. **Download** — Download the stored WSDL file. **Archive** — Retire the asset without deleting it. ## Create an HTTP tool using a WSDL asset 1. In your project, go to **Tools**. 2. Click **New Tool** → **HTTP Tool**. 3. In the HTTP Config form, provide the following configurations. | Field | Description | | :- | :- | | **Name** | Specifies the name of the HTTP tool. For example, `get_customer`. | | **Description** | Provides a brief description of the tool. | | **Protocol** | Specifies the communication protocol. Set this value to `SOAP`. | | **SOAP Version** | Specifies the SOAP version to use. Select `SOAP 1.1` or `SOAP 1.2` based on your WSDL. | | **SOAPAction** | Specifies the `soapAction` value for the selected WSDL operation. If left blank, the platform automatically populates this field from the uploaded WSDL after the WSDL asset is selected. | | **Fault Handling** | Specifies how the platform handles SOAP faults. Leave the default option, `Treat fault as error`, unless the service returns business outcomes within `` elements. | | **WSDL Binding Operation** | Specifies the WSDL asset, binding version, and operation to invoke for this tool. | | **Method** | Specifies the HTTP method used to invoke the SOAP service. This field is automatically set to `POST`. | | **Auth Profile** | Specifies the WS-Security authentication profile used for the SOAP request. Leave field blank if authentication isn't required. Most enterprise SOAP services use WS-Security UsernameToken or X.509 certificate authentication. Create a WS-Security profile under `Project Settings > Auth Profiles > Custom Profile > WS-Security` and provide the required authentication details. When the tool invokes the SOAP service, the platform automatically generates and injects the required `` header into the request. | | **Parameters** | Define the inputs the LLM will provide when calling the tool. | | **Return type** | Define the Return type: what the tool returns to the agent. Example: `{name: string, tier: string, account_number: string}` | 4. Click **Next** to review the configuration and test the tool. ## Using the tool in an agent A WSDL-bound SOAP tool is still an HTTP tool. The difference is that it carries WSDL binding fields and omits the manual SOAP body template — the platform generates the request envelope from the tool's JSON input. In the ABL editor, reference the asset by name and (optionally) pin a version: ```yaml theme={null} TOOLS: get_customer(customer_id: string) -> {name: string, tier: string} type: http endpoint: "https://core.example.com/CoreApi" # WSDL service address or an override method: POST protocol: soap wsdl_asset: "CoreApi" # name from the WSDL Repository wsdl_version: latest # or a specific version number wsdl_operation: "GetCustomer" ``` On save, the platform resolves `wsdl_asset`/`wsdl_version` to stable, immutable IDs. Because versions are immutable, uploading a new WSDL version does not change existing tools until you repoint them. See the full field reference in [HTTP tools → WSDL operation binding](/agent-platform/abl-reference/tools#wsdl-operation-binding). A WSDL-bound SOAP-based HTTP tool is an HTTP tool. The important difference is that this tool carries WSDL binding fields and can omit a manual SOAP body template. Attach the tool to the agent like any other tool, through the designer or the ABL editor. # Transfer sessions Source: https://koreai.mintlify.app/agent-platform/transfer-sessions Transfer Sessions is a monitoring and control page for live handoffs from an AI agent to a human agent on an external contact center platform. Navigate to **Operate** > **Transfer Sessions** from the project sidebar. When an AI agent cannot resolve a conversation and escalates it to a human representative, the platform initiates a transfer session with the configured external provider. Each active or recently completed handoff appears as a row in the Transfer Sessions table. It allows administrators and operators to monitor the status of transferred sessions. The platform polls agent-transfer sessions every 30 seconds to keep transfer states up-to-date. Use the **Refresh** button to force an immediate update. ## Prerequisites Configure an integration for external agent transfer. Transfer sessions get created only when an external agent transfer integration is configured for the project. The supported providers are: * [Kore SmartAssist](/agent-platform/integrations/smartassist) * Salesforce * ServiceNow * Five9 * Genesys If no integration is configured, the Transfer Sessions table remains empty even when agents trigger escalation actions. To configure the integration, go to **Project Settings** > **Agent Transfer** and set up a connection. ## How is a transfer session created A transfer session is created automatically when an AI agent hands off to a human representative. This occurs when the agent's ABL contains a `HANDOFF` or `DELEGATE` block targeting a human escalation path. The platform creates the session record, notifies the configured external provider, and the session immediately appears in the Transfer Sessions table with a **Pending** status. When a transfer request is created, the corresponding escalation task is also visible in the [**Inbox**](/agent-platform/inbox). ## Transferred sessions For each transferred session, the following details are presented. | Field | Description | | - | - | | Contact | Unique identifier associated with the end user. | | Agent | The AI agent that initiated the transfer. | | Provider | The CCaaS provider handling the transferred session. | | Channel | Communication channel used for the interaction - chat / voice / email / messaging / campaign. | | Status | Current state of the transferred session. See Session Status Values below. | | Queue | Queue assigned by the destination provider, if available. | | Duration | Time elapsed since the transfer was initiated. | | Actions | Available actions that can be performed on the transferred session. | **Session Status Values** | Status | Description | | - | - | | pending | Transfer initiated. | | queued | Placed in a queue, waiting for an agent. | | active | Human agent connected. | | post\_agent | Human agent disconnected and the session is in an After Call Work (ACW) or CSAT flow. | | ended | Session closed. | ## Transfer session details Click any row in the Transfer Sessions table to open the **Transfer Session Details** modal. The modal shows the complete record for a single session and is the primary surface for diagnosing handoff issues. | Field | Description | | - | - | | Session ID | Full composite session identifier used internally by the platform. Format: `agent_transfer:::`. This ID uniquely identifies the session across the platform and the provider. | | Contact ID | Full UUID of the user contact associated with this transfer session. This is the untruncated version of the ID shown in the **Contact** column of the table. | | Agent | Name of the AI agent that initiated the transfer. | | Provider | The external provider handling this transfer session (for example, SmartAssist, Genesys, Five9). | | Status | Current lifecycle state of the session, shown as a colored badge. Same values as the table. | | Channel | Communication channel of the originating conversation (for example, chat, voice, email). | | Provider Session ID | The session identifier assigned by the external provider for this transfer. Use this ID when cross-referencing the session in the provider's platform, such as Salesforce case records, Five9 call logs, or SmartAssist transcripts. | | Timestamps | Timestamps indicating when the session was created and last updated. | | Metadata | A JSON block containing platform-level metadata for the session. | | Provider Data | A JSON block containing provider-specific data returned by the external platform when the transfer was initiated. Fields vary by provider. | ## Transfer session actions **End** - Marks the conversation as closed, ends the transfer session, and notifies the remote agent application. **Refresh** - Forces an immediate poll of transfer sessions, bypassing the 30-second automatic refresh interval. # Use the Artemis CLI Source: https://koreai.mintlify.app/agent-platform/use-cli The Artemis CLI lets you create, review, customize, test, and deploy agents, tools, and enterprise content connectors from your terminal or CI pipeline, without opening the browser. The CLI supports both interactive development and repeatable automation workflows. Before you begin, [install and configure the CLI](/agent-platform/cli). ## Create ### Create a project ```bash theme={null} artemis-platform-cli projects create "Support Automation" \ --description "Customer support agents and workflows" artemis-platform-cli projects list artemis-platform-cli projects select ``` ### Create an agent Create an agent from an ABL file: ```bash theme={null} artemis-platform-cli agents create support_agent \ --file ./agents/support.agent.abl ``` ### Create tools Create an HTTP tool and associate it with an ABL file: ```bash theme={null} artemis-platform-cli tools create lookup_order \ --description "Look up an order by ID" \ --type http \ --endpoint https://api.example.com/orders/lookup \ --method POST \ --file ./tools/lookup-order.tools.abl artemis-platform-cli tools get lookup_order ``` ### Connect enterprise content Connectors ingest enterprise content into a SearchAI index. The available options vary by connector type. Use the help command to see the current options: ```bash theme={null} artemis-platform-cli connector create --help ``` For example, create and authenticate a SharePoint connector: ```bash theme={null} artemis-platform-cli connector create sharepoint "Support Knowledge" \ --index-id \ --tenant-url https://.sharepoint.com \ --client-id artemis-platform-cli connector auth ``` After completing OAuth authentication, start a full synchronization and check its status: ```bash theme={null} artemis-platform-cli connector sync start artemis-platform-cli connector sync status ``` Use `connector filter --help` and `connector permission --help` to configure the content scope and permission mode before production ingestion. ### Generate a project with Arch AI Start an Arch AI onboarding session to generate a new project from natural language: ```bash theme={null} artemis-platform-cli arch session new artemis-platform-cli arch session list ``` Send the initial build request: ```bash theme={null} artemis-platform-cli arch send \ "Add order-status support and update the project" ``` To generate using Arch AI, you must configure a primary model. You can create a session even when the tenant isn't configured for generation. **Related content:** * Continue an Arch AI conversation in [Iterate with Arch AI](#iterate-with-arch-ai). * Inspect its output in [Inspect Arch AI-generated state](#inspect-arch-ai-generated-state). * Refresh ingested content in [Keep enterprise content current](#keep-enterprise-content-current). ## Review ### Inspect and compile an agent ```bash theme={null} artemis-platform-cli agents get support_agent artemis-platform-cli agents compile support_agent ``` ### Inspect Arch AI-generated state After Arch AI generates or updates a project, inspect the resulting state: ```bash theme={null} artemis-platform-cli arch summary --project artemis-platform-cli arch health --project ``` ### Export and verify a project package Export the supported project layers: ```bash theme={null} artemis-platform-cli export \ --project \ --output ./exports \ --all-layers \ --include-deployments ``` Verify the exported files locally: ```bash theme={null} artemis-platform-cli verify ./exports/ artemis-platform-cli lockfile recompute ./exports/ --check ``` **Related content:** Arch AI-generated projects are, * Created in [Generate a project with Arch AI](#generate-a-project-with-arch-ai). * Refined in [Iterate with Arch AI](#iterate-with-arch-ai). ## Customize ### Update an agent Update an agent from an ABL file and read it back: ```bash theme={null} artemis-platform-cli agents update support_agent \ --file ./agents/support-v2.agent.abl artemis-platform-cli agents get support_agent ``` ### Keep enterprise content current After the initial full synchronization, run an incremental synchronization to pick up content changes: ```bash theme={null} artemis-platform-cli connector sync start --delta artemis-platform-cli connector sync status ``` ### Iterate with Arch AI Open a session for an existing project and continue the conversation: ```bash theme={null} artemis-platform-cli arch session new --project ``` Send a follow-up request or start an interactive chat: ```bash theme={null} artemis-platform-cli arch send \ "Add order-status support and update the project" artemis-platform-cli arch chat ``` ### Track customizations with Git Git operations require a project-shared authentication profile created by a Platform administrator. Initialize Git synchronization: ```bash theme={null} artemis-platform-cli git init \ --project \ --provider bitbucket \ --repo \ --auth-profile \ --branch main \ --sync-path /artemis artemis-platform-cli git status --project ``` Push project state to Git: ```bash theme={null} artemis-platform-cli git push \ --project \ --branch main \ --message "Update Artemis project" ``` **Related content:** * Apply incoming Git changes in [Apply Git updates](#apply-git-updates). * Start the initial content sync in [Connect enterprise content](#connect-enterprise-content). ## Test ### Test models and agents Agent execution requires a working primary model connection. List and test available models before testing an agent: ```bash theme={null} artemis-platform-cli models list artemis-platform-cli models test artemis-platform-cli agents test support_agent \ --message "Check order CERT-1001" ``` If model testing fails, ask a tenant administrator to configure and select a primary model before retrying agent, evaluation, or Arch generation commands. ### Validate and evaluate a project The `lifecycle` command tree is designed for CI and release automation. Each invocation writes one versioned JSON envelope to stdout and returns a stable exit code. Validate a project package, then run an evaluation gate: ```bash theme={null} artemis-platform-cli lifecycle validate ./exports/ \ --project \ --preview artemis-platform-cli lifecycle eval run \ --project \ --set \ --wait \ --min-pass-rate 0.90 \ --min-score 0.80 ``` Continue with synchronization, import, release publication, deployment, and evidence only after the validation preview and the evaluation succeed. ## Deploy ### Apply Git updates Preview a pull before applying it: ```bash theme={null} artemis-platform-cli git pull --project --branch main ``` Apply the pull after reviewing the preview: ```bash theme={null} artemis-platform-cli git pull --project --branch main --apply ``` ### Import a project package Preview an import before applying it: ```bash theme={null} artemis-platform-cli import ./exports/ \ --project \ --dry-run ``` Review the preview before applying the import. Then apply the package and run post-import checks: ```bash theme={null} artemis-platform-cli import ./exports/ \ --project \ --acknowledge-warnings artemis-platform-cli doctor --project ``` Do not acknowledge warnings until you understand the proposed changes. Use a dedicated project when evaluating an unfamiliar package. ### Synchronize a validated package Once validation and evaluation succeed, synchronize the package: ```bash theme={null} artemis-platform-cli lifecycle sync ./exports/ \ --project ``` **Related content:** * Push local customizations before pulling in [Track customizations with Git](#track-customizations-with-git). ## Automate and integrate ### Use the CLI in CI CI jobs can provide a user access token without writing it to CLI storage: ```bash theme={null} export KORE_API_TOKEN='' ``` Store the token in your CI secret manager. Don't place access tokens in command arguments, committed scripts, build logs, or documentation. To isolate CLI state for a CI job, set a separate configuration directory: ```bash theme={null} export ARTEMIS_PLATFORM_CLI_CONFIG_DIR="$(mktemp -d)" ``` The directory must be an absolute path. Remove it when the job finishes. ### Use the CLI with MCP You can run the CLI as an MCP stdio server. To start the server, use the command `artemis-platform-cli mcp`. A sample config for your MCP client to launch the CLI is below. ```json theme={null} { "mcpServers": { "artemis-platform": { "command": "artemis-platform-cli", "args": ["mcp"], "env": { "KORE_API_URL": "https://example.com", "KORE_RUNTIME_API_URL": "https://example.com", "KORE_SEARCHAI_API_URL": "https://example.com" } } } } ``` The MCP process uses the same stored login, selected workspace, selected project, and service URLs as direct CLI commands. If your desktop MCP client doesn't inherit or can't use the `PATH` value, it can't launch the CLI directly. In such a case, use the absolute binary path returned by the command `command -v artemis-platform-cli`. Keep the stdout reserved for MCP JSON-RPC messages. ## Reference ### CLI commands The following commands provide the primary CLI capabilities: | Command | Purpose | | - | - | | `login`, `logout`, `whoami` | Authenticate and inspect identity | | `workspaces`, `projects` | Select and manage context | | `agents`, `tools`, `models` | Author, compile, and test agent resources | | `connector` | Configure and synchronize enterprise content | | `export`, `import`, `verify`, `doctor`, `lockfile` | Move and validate project packages | | `git` | Synchronize project state with a Git repository | | `lifecycle` | Run machine-readable evaluation, release, and deployment automation | | `arch` | Use Arch AI without the browser | | `mcp` | Expose Platform capabilities over MCP stdio | | `config` | Manage service URLs and inspect local state paths | Use the installed command help as the syntax source of truth: ```bash theme={null} artemis-platform-cli --help artemis-platform-cli --help artemis-platform-cli --help ``` ### Troubleshooting | Problem | Resolution | | - | - | | `Not authenticated` | Run `login`, then confirm with `whoami`. In CI, check `KORE_API_TOKEN`. | | `No active project` | Run `projects select ` or provide `--project `. | | Connector commands return 404 | Check `searchAiApiUrl` or `KORE_SEARCHAI_API_URL`. | | `No API key configured` | Ask an administrator to configure and select a tenant primary model. | | Connector is not authenticated | Run `connector auth ` and complete OAuth. | | `No git integration configured` | Create an authorized Git profile, then run `git init`. | | `IMPORT_PREVIEW_STALE` | Generate a new preview and use its new digest. | | `OUTCOME_UNKNOWN` | Read the affected resource with `get`, `list`, or `status` before deciding whether to retry. | | MCP client cannot start the command | Configure the absolute binary path returned by `command -v`. | For the configured service URLs and CLI state paths, run: ```bash theme={null} artemis-platform-cli config show ``` ### Safety considerations * Use explicit project IDs in automation. * Read previews before applying imports, synchronization operations, or Git pulls. * Do not bypass qualification gates without an approval record and reason. * Do not retry an `OUTCOME_UNKNOWN` mutation blindly. Read the authoritative resource state first. * Keep API tokens, OAuth credentials, provider keys, and Git credentials out of command arguments and logs. * Use non-production projects and repositories when evaluating a release candidate. # Build with Arch using MCP Source: https://koreai.mintlify.app/agent-platform/use-mcp-server Agent Platform exposes its features through a Model Context Protocol (MCP) server. Your AI assistant can connect to the platform and perform platform operations to let you build, evaluate, optimize, debug, and analyze agents. A few common tasks include: * Creating and editing projects, agents, and tools * Running evaluations * Inspecting live sessions and traces * Diagnosing failures You can perform all these operations from the platform UI as well. However, by using MCP server, you can perform these tasks from within your IDE, command-line, or terminal, without context-switching. If you prefer scripting or CI automation over an AI assistant, the [Artemis Platform CLI](/agent-platform/use-cli) exposes the same platform operations directly from your terminal or pipeline. The server's name as available in the clients is `arch-agent-platform` and its tools are prefixed with `platform_` and `debug_`. These tools are grouped by functional area. ## Build Create and change platform projects, workflows, agents, tools, auth profiles, integrations, MCP servers, configuration, versions, deployments, and imports. | Tool | Description | | :- | :- | | `platform_projects` | Manage projects (list, get, create, update, delete) | | `platform_workflows` | Create, update, publish, execute, and expose workflows as tools | | `platform_auth_profiles` | Manage profile metadata and start secure OAuth flows | | `platform_integrations` | Create, update, test, and remove connector connections | | `platform_mcp_servers` | Provision MCP servers; discover, import, and test tools | | `platform_agents` | Manage agents (list, get, save\_dsl) | | `platform_versions` | Manage immutable versions (list, get, publish, qualifications, audit, diff) | | `platform_deployments` | Manage typed deployments (list, get, create, promote, rollback, restore, retire) | | `platform_sdk_channels` | Create SDK keys and web, mobile, and API channels | | `platform_tools` | Manage tools (list, get, create, update, delete, test) | | `platform_import_export` | Import and export projects | | `platform_config` | Manage project and LLM configuration | | `platform_workspaces` | List, switch, and inspect active workspaces | | `platform_arch_sop` | Drive Studio Arch SOP-build sessions | | `platform_arch_auto_loop` | Drive project-scoped Arch Auto Loop repair workflows | | `platform_project_builder` | Discover provider contracts, inspect live dependencies and readiness, and plan | | `platform_project_builder_operations` | Continue durable operations and execute attempt-bound governed actions | | `agent_tables` | Manage Agent Tables definitions and project-scoped rows | ### Project-builder protocol `platform_project_builder` and `platform_project_builder_operations` expose a feature-neutral protocol rather than one tool per feature. Call `platform_project_builder(action: "describe")` to discover the core ontology and registered providers instead of reconstructing cross-feature dependencies by calling primitive tools and joining the responses yourself. ### Durable operations and grants Use `platform_project_builder_operations` for durable list, read, report, resume, cancel, grant, and execute flows. Side effects require the exact operation version and attempt-bound grant that Studio returns. Never retry a consumed attempt with an unknown outcome. ### Credential-bearing auth profiles `platform_auth_profiles(create)` only creates profiles with `authType: "none"`. Credential-bearing profiles return a `secureSetupRequired` handoff instead, because API keys, client secrets, certificates, and tokens must never enter MCP or model context. After you complete setup in Studio, MCP can list, inspect, update metadata for, validate, revoke, and initiate OAuth for a profile, then bind its opaque profile ID to integrations or MCP servers. ## Evaluate Generate eval assets, run eval workflows, and read CI evidence. | Tool | Description | | :- | :- | | `platform_eval_personas` | Manage and generate eval personas | | `platform_eval_scenarios` | Manage and generate eval scenarios | | `platform_eval_evaluators` | Manage eval evaluators and templates | | `platform_eval_sets` | Manage eval sets | | `platform_eval_runs` | Manage eval runs, preflight, cases, and heatmaps | | `debug_harness_logs` | Get CI execution logs | ## Optimize Validate packages, inspect compiler-visible models, and drive repair loops. | Tool | Description | | :- | :- | | `platform_validate_package` | Validate a local package and optional import preview | | `platform_package_model` | Show compiler-visible agents, tools, constraints, refs, and diagnostics | | `debug_lint_abl` | Run ABL repair and design lint checks | | `debug_why_transcript_failed` | Correlate transcript symptoms with ABL file/line causes | | `debug_diagnose_transcript` | Alias for transcript failure diagnosis | ## Debug Connect to live sessions, trace failures, and inspect execution state. | Tool | Description | | :- | :- | | `platform_connect` | Connect and authenticate to the platform | | `debug_list_agents` | List available agents by domain | | `debug_load_agent` | Load an agent and create a debug session | | `debug_send_message` | Send a message to an agent | | `debug_get_current_state` | Inspect agent context, gather progress, flow state | | `debug_traces` | Search trace events (type, text, agent, error) | | `debug_session_history` | Page durable Runtime sessions and persisted traces | | `debug_get_span_tree` | View hierarchical execution flow | | `debug_explain_decision` | Explain agent decisions with context | | `debug_get_flow_graph` | View state machine graph (JSON or Mermaid) | | `debug_get_errors` | Get errors, warnings, and escalations | | `debug_list_active_sessions` | List observable sessions | | `debug_session` | Subscribe/unsubscribe to session traces | ### Historical session analysis Use `debug_session_history` as the read-only alternative to `debug_traces` when you need to analyze a retained session after its live MCP buffer is no longer available. Connect to the intended Runtime with `platform_connect` first, so the existing bearer identity, workspace, and environment stay authoritative. List a first page (defaults: `limit: 50`, `offset: 0`, `sortBy: lastActivityAt`, `sortDir: desc`): ```json theme={null} { "action": "list", "projectId": "project-id", "status": ["completed"], "range": "7d" } ``` Read a middle or final trace page by advancing `offset`. A page beyond the final one returns an empty successful page, not an error: ```json theme={null} { "action": "get", "projectId": "project-id", "sessionId": "session-id", "limit": 200, "offset": 200, "types": ["llm_call", "tool_call"] } ``` Runtime controls retention, authorization, and ordering. Each call makes one bounded request (2 MiB, 10 seconds) and never falls back to the live MCP buffer. Don't place credentials or secrets in filter values. ## Analyze Explain documentation, diagnostics, and system health signals. | Tool | Description | | :- | :- | | `debug_docs` | Get or search ABL documentation | | `debug_diagnose` | Diagnose agent config and execution issues | | `debug_analyze_session` | Automated session diagnostics | # Workflows Source: https://koreai.mintlify.app/agent-platform/workflows Workflows enable you to build durable, multi-step automations that coordinate agents, APIs, integrations, human approvals, and business logic across systems. Use workflows to automate business processes that span multiple systems or require structured orchestration beyond a single agent conversation. You can build deterministic workflows using structured workflow steps or create semi-autonomous workflows by adding Agent nodes that support AI-driven reasoning and decision-making during workflow execution. Typical workflow use cases include: * Order processing * Leave approval workflows * Claims and underwriting processes * Human approval tasks ## Key features * Durable multi-step workflow orchestration across agents, integrations, APIs, and human tasks. * Visual workflow builder with configurable workflow nodes and execution paths. * Human-in-the-loop execution using Human task nodes with Inbox-based task handling. * Trigger-based execution using webhooks, schedules, and external application events. * Workflow execution monitoring with execution history, flow logs, runtime metrics, and execution details inspection. The platform maintains a one-to-one mapping between the visual configuration interface and the DSL/ABL definition. You can configure workflows and tools using either the visual interface or the DSL/ABL definition. Changes made in one view are automatically reflected in the other. *** ## Understand workflows vs. agent flows Use workflows and agent flows for different execution scenarios. | Use case | Recommended capability | | - | - | | Interaction-level deterministic steps during a conversation | Agent Flow | | Long-running orchestration outside the interaction boundary | Workflow | | Human approvals or asynchronous waits | Workflow | | Scheduled or event-driven execution | Workflow | | Multi-system backend automation | Workflow | | Agent-triggered orchestration | Workflow | | Conversational interactions with deterministic execution steps | Agent Flow | A workflow can also be exposed as a tool and invoked by an agent during runtime. *** ## Create a workflow 1. From the Workflows page, select **Create Workflow**. 2. In the creation dialog, enter **Name** and **Description** (optional). 3. Select **Create**. After you create a workflow, the workflow opens in the **Overview** tab. *** ### Workflow overview page The Overview page provides a summary of the workflow, including its structure, execution metrics, trigger configuration, version history, and dependencies. Use this page to quickly review the workflow status and access key information without opening the Flow editor. | Section | Description | | - | - | | **Workflow visualization** | Displays a visual representation of the workflow, allowing you to review the workflow structure at a glance. | | **Monitor** | Displays workflow execution metrics, including the total number of runs, workflows currently in progress, response time, and failure rate. | | **Triggers** | Displays the configured triggers for the workflow and their current status. | | **Versions** | Lists the available workflow versions, publication information, and deployment status. | | **Used by** | Lists the agents or tools that reference the workflow. | | **Workflow details** | Displays workflow metadata, including the creation date and last modified date. | Workflow *** ### Flow builder Use the **Flow** tab to build and configure workflow steps. The workflow builder provides a visual canvas where you can add workflow nodes, configure execution logic, and define step relationships. Every workflow starts with a **Start** step and ends with an **End** step. Add workflow steps between the Start and End steps to define workflow execution logic. The workflow builder contains the following areas. | Area | Description | | - | - | | Node panel | Displays available workflow nodes grouped by category. | | Flow canvas | Add workflow structure and execution path. | | Configuration panel | Displays configuration options for the selected workflow step. | Workflow ### Add workflow steps To add a workflow step: 1. Open the **Flow** tab. 2. Select a node from the node panel. The node appears on the flow canvas. 3. Configure the node settings. 4. Connect the node to the workflow path. ### Configure workflow steps Select a workflow step to open its configuration panel. Configuration options vary by node type. Typical configuration options include: * Step name * Input parameters * Output mappings * Timeout settings * Approval assignments * Integration settings * Conditional expressions ### Build workflow logic Connect nodes visually on the canvas to define execution order. Workflows support: * Sequential execution * Conditional branching * Parallel execution patterns ### Use workflow variables Each node exposes configurable inputs and outputs. Workflow data is passed through the workflow context object. Use the following syntax patterns to reference workflow data between steps. | Variable type | Syntax | | - | - | | Input variable | `context.steps.start.` | | Step output | `context.steps..output` | ### Use forms in workflows You can use reusable forms to collect structured information during workflow execution. Create and manage forms under **Resources > Forms**, then use them in [Human task nodes](#human-task-node) or as [Intake form triggers](/agent-platform/workflows#intake-form-trigger). For information about creating and configuring reusable forms, see [Forms](/agent-platform/forms). *** ## Node types | Node type | Group | Description | | | - | - | - | - | | [Agent](#agent-node) | Agent | Executes an agent within the workflow to handle part of the process. | | | [Tool](#tool-node) | Tools | Executes a configured tool within the workflow (HTTP call, code execution). | | | [Function](#function-node) | Actions | Executes custom JavaScript logic for data transformation or processing. | | | [Integration](#integration-node) | Actions | Executes actions using configured integrations and external services. | | | [Human task](#human-task-node) | Human-in-the-loop | Pauses workflow execution and assigns a task to users for human intervention before continuing execution. | | | [Condition](#condition-node) | Flow control | Routes workflow execution based on conditions. | | | [Loop](#loop-node) | Flow control | Repeats a set of workflow steps for each item in an input array using sequential or parallel execution. | | | [Delay](#delay-node) | Flow control | Pauses workflow execution for a configured duration. | | | [End](#end-node) | Flow control | Completes workflow execution and defines workflow outputs. | | *** ### Start node Use the Start node to define the workflow input parameters. These parameters define the data that is passed to the workflow when it starts. You can define input parameters using either the parameter form or the JSON editor. | Field | Description | | - | - | | Parameter name | Variable name used in context | | Data type | String, number, boolean, object, or array | | Item type | Displayed when Data type is set to Array. Specifies the type of items in the array: Any (JSON), String, Number, and Boolean. | | Required | Marks parameter as mandatory | | Default value | Optional fallback value | Example ```text theme={null} {{context.steps.start.input.email_text}} ``` *** ### Agent node Use the Agent node to invoke an agent during workflow execution. Agent nodes are typically used when workflow execution requires AI-driven reasoning, classification, decision-making, or conversational processing before continuing to the next workflow step. | Field | Description | | - | - | | Agent | Selects the agent to execute during workflow execution. | | Create an agent | Opens the Agents page to create a new agent if no suitable agent exists. | | Input | Defines the input passed to the agent. You can reference workflow variables and trigger payloads. | | Session ID (optional) | Defines the session identifier used during execution. Leave blank to create a new session. | | Timeout (seconds) | Defines how long the workflow waits for the agent response before timing out. | | Attachments (optional) | Add attachment URLs or IDs to provide files to the agent. You can enter a single attachment URL or ID, an array of attachment URLs or IDs, or add them individually. Up to 10 attachments are supported per invocation. | | On failure | Defines the behavior when agent execution fails. | *** ### Tool node Use the Tool node to execute a registered tool during workflow execution. Tool nodes are typically used to invoke reusable capabilities such as workflow tools, APIs, integrations, or external operations defined in the project. | Field | Description | | - | - | | Create a tool | Opens the Tools page to create a new tool if no suitable tool exists. | | Parameters | Defines the input parameters passed to the tool. Parameters can use static values or workflow expressions. | | Timeout (seconds) | Defines how long the workflow waits for tool execution before timing out. | | On failure | Defines the behavior when tool execution fails. | The timeout configured for a Tool node in a workflow overrides the timeout specified in the tool configuration. *** ### Function node Use the Function node to execute custom JavaScript logic during workflow execution. Function nodes are typically used for data transformation, validation, formatting, filtering, enrichment, and intermediate processing between workflow steps. | Field | Description | | - | - | | Context object | Use the `context` object to read and write workflow data within Function nodes. | | JavaScript | Defines the JavaScript logic executed during workflow execution. | | Timeout (seconds) | Defines how long the workflow waits for script execution before timing out. | | On failure | Defines the behavior when function execution fails. | **Use Workflow Context in Function Nodes** Use the `context` object to access workflow data. | Usage | Syntax | | - | - | | Trigger payload | `context.trigger.payload` | | Previous step output | `context.steps..output` | | Write workflow values | `context. = value` | Values written to the context object can be referenced in downstream workflow steps. *** ### Integration node Use the Integration node to execute integration connector actions during workflow execution. Integration nodes allow workflows to execute actions using configured integrations such as Gmail, Slack, GitHub, Microsoft Teams, Salesforce, HubSpot, Twilio, OpenAI, and other available connectors. | Field | Description | | - | - | | Select Integration & Action | Opens the integration catalog and available actions for the selected connector. | | Action Inputs | Configure the action-specific input fields. Available fields vary based on the selected integration action. | | On failure | Defines the behavior when integration execution fails. | For example, a Gmail integration may expose actions such as send or forward email, request approval in email, reply to email, and custom API calls. Depending on the selected action, additional fields can include: * Connection selection * Recipient email addresses * Subject and message content * Approval request settings * Search parameters * API request details If no integration connection exists, create an **auth profile** and **integration connection** before you configure the integration action. *** ### Human task node The Human task node pauses workflow execution and assigns tasks to users for human intervention. The task can include a form that the assignee completes before the workflow continues. Assigned users can open tasks in Inbox to review the task, provide the requested information, or complete the configured task action. For more information, see [Inbox](/agent-platform/inbox). When a workflow reaches a Human task node: * A pending task is created in the Inbox. * Assigned users receive notifications. * Workflow execution pauses until the task is completed. * The workflow resumes after the task is completed. The Human task node supports two ways to collect information from the assignee: * **Forms** — Select a reusable form created under **Resources > Forms**. For information about creating and configuring forms, see [Forms](/agent-platform/forms). * **Inline fields** — Define simple fields directly in the Human task node. **Task** Use the Task tab in the Human task node to define the task request, specify how the assignee provides information, and define the outcomes available for completing the task. | **Setting** | **Description** | | :- | :- | | **Subject** | Specify the subject of the task request. | | **Message** | Provide instructions or context for the assignee. | | **Form** | Select a form for the assignee to complete. Forms are created and managed under **Resources > Forms**. The fields defined in the form are presented to the assignee as part of the task. Values submitted by the assignee are available to subsequent workflow steps. | | **Inline fields** | Use inline fields when the task requires additional inputs that can be defined directly in the node instead of adding them to a form. For each field, specify the field name, type, label, whether the field is required, and an optional default value. | | **Outcomes** | Define the options that the assignee can select to indicate how the task was completed. The selected outcome is available to subsequent workflow steps. | **Assignees & SLA** Use the Assignees & SLA tab to define who can work on the task, how responses from multiple assignees are handled, and what happens as the task approaches or exceeds its completion deadline. | **Field** | **Description** | | :- | :- | | **Assignment** | Choose **Direct** to assign the task to a fixed list of people, or **Conditional** to assign the task based on rules. For conditional assignment, the first matching rule determines the assignee; **Default assignees** receive the task if no rule matches. | | **Consensus** | When multiple assignees are assigned, choose how their responses resolve the task. Available resolution modes are:
• **First response wins:** The first assignee to submit resolves the task.
• **Everyone assigned must submit:** The task is resolved after all assigned users submit.
• **N of the assignees must submit:** Specify the required number of responses (**N**) before the task is resolved.
You can also specify how the task is resolved when the required consensus cannot be reached. | | **Timeout** | Enable a timeout and specify the **Duration** and **Unit** to define the task completion deadline. | | **Reminders** | Add reminders to notify assignees before the deadline. Each reminder is triggered at a specified percentage of the deadline. You can also configure a tool to run when the reminder threshold is reached. | | **Escalation ladder** | Add escalation steps to take action as the task approaches or exceeds its deadline. For each step, specify when to escalate and choose whether to **Loop in a person/group** or **Go directly to a node**. Escalation steps run in the configured order. | | **On timeout** | Specify what happens when the task reaches its deadline without being resolved. | **Options** Use the Options tab to configure additional behavior for the Human task node, including external notifications, error handling, and analytics tags. | **Setting** | **Description** | | :- | :- | | **External notifications** | Enable **Send notifications outside the inbox** to notify assignees through external channels. You can add one or more notification channels and configure each channel separately.

Available channel options include Kore Email, Gmail Send Email, and Notify via Tool. For **email** channels, specify the message format and optionally override the default task, reminder, and escalation messages. For **Notify via Tool**, select how the tool runs and configure the tools to use when the task is created, a reminder is triggered, or an escalation occurs. | | **Error handling** | Enable **On failure** to specify the node to execute when the Human task node fails. | | **Analytics tags** | Add custom key-value dimensions to workflow metric events emitted when the task completes or fails. Values can reference the workflow context. | *** ### Condition node Use the Condition node to branch workflow execution based on conditional logic. Condition nodes evaluate expressions and route execution through different workflow paths depending on the evaluation result. The Condition node supports `if`, `else`, and `else if`. | Field | Description | | - | - | | Field | Defines the workflow variable or step output to evaluate. | | Operator | Defines the comparison operator used in the condition. | | Value | Defines the comparison value for the condition. | The **Else (default path)** executes when none of the configured conditions evaluate to true. Use **Add Else If** to configure additional conditional branches. Condition expressions can reference workflow variables and outputs from previous workflow steps. For example, `{{context.steps.NodeName.output.field}}`. Use Condition nodes to: * Route workflows based on API responses * Branch execution based on approval outcomes * Evaluate workflow variables * Handle success and failure scenarios * Create decision-based workflow paths *** ### Loop node The Loop node repeats a set of workflow steps for each item in a list. Use it to automate repetitive tasks such as processing records, calling APIs, or performing batch operations. The Loop node supports both Sequential (Loop) and Parallel (Batch) execution modes. Configure the nodes inside the loop, choose how to handle errors, and collect the results in an output variable for use by downstream nodes. | Field | Description | | - | - | | **Execution Mode** | Specifies how loop iterations are executed. Select either **Sequential (Loop)** to process one item at a time or **Parallel (Batch)** to process multiple items concurrently. | | **Source** | The input array that the Loop node processes. | | **Item Alias** | The variable that represents the current item during each iteration. | | **Output Variable** | Stores the aggregated results returned after all iterations complete. | #### Sequential execution Processes one item at a time. Each iteration completes before the next iteration begins. Use this mode when: * Processing items that depend on one another or must be executed in order. * Each iteration depends on the result of the previous iteration. * External systems require sequential processing. | Field | Description | | - | - | | **Max Iterations** | Maximum number of iterations to execute. | | **On Error** | Specifies whether to **Continue** processing the remaining iterations or **Terminate** the workflow when an iteration fails. | #### Parallel execution Processes multiple items simultaneously. Results are collected after all iterations complete. Some use cases of this mode are: * Iterations are independent. * Processing large collections of items. * Calling APIs or integrations that can run concurrently. | Field | Description | | - | - | | **Concurrency Limit** | Maximum number of iterations that execute simultaneously. | | **Stagger (ms)** | Delay, in milliseconds, between starting consecutive iterations. | | **On Error** | Specifies whether to **Continue** processing the remaining iterations or **Terminate** the workflow when an iteration fails. | | **Preserve Item Order** | Returns results in the same order as the input array. | Use a lower **Concurrency Limit** when calling external systems that enforce rate limits or concurrent request limits. **Configure Iteration Output** Use Iteration Output to map values from nodes inside the loop to the loop output. After the loop completes, the mapped values from all iterations are collected into the configured Output Variable, making them available to subsequent nodes in the workflow. **Example** Suppose an Integration node returns a list of customer IDs. Configure the Loop node to iterate through the list. During each iteration, invoke an API node to retrieve customer details, then use **Iteration Output** to capture the required values. After all iterations are complete, the aggregated customer details are stored in the configured output variable and passed to the next node in the workflow. *** ### Delay node Use the Delay node to pause workflow execution for a specified duration before continuing to the next workflow step. Delay nodes support scheduled continuation, wait periods, retry delays, and time-based workflow orchestration. | Field | Description | | - | - | | Duration | Defines the amount of time the workflow waits before resuming execution. | | Unit | Defines the duration unit. | | On failure | Defines the workflow step to execute if the delay step fails. | Supported duration units depend on the configured delay range. The allowed range is 30 seconds to 24 hours. Some use cases of Delay node are: * Pause workflows before continuing execution * Add retry wait periods * Delay notifications or follow-up actions * Coordinate scheduled workflow execution *** ### End node Use the End node to complete workflow execution and define the workflow output structure. You can define output mappings using either the parameter form or the JSON editor. The End node declares the workflow outputs returned after workflow execution completes. | Field | Description | | - | - | | Output field | Defines the output field name exposed by the workflow. | | Type | Defines the output data type. | | Expression | Maps workflow variables or step outputs to the output field. | | Description | Defines the output field description. | | Add output mapping | Adds additional workflow output mappings. | Output expressions can reference workflow variables and outputs from previous workflow steps. For example, `{{context.steps.NodeName.output}}` The End node generates the final workflow response returned after workflow execution completes. *** ## Export and import workflows You can export an individual workflow and import it into another project as an editable draft, making it easier to reuse or migrate workflows between projects. ### Export a workflow 1. Open the workflow you want to export. 2. Click **Export**. 3. The workflow is downloaded as a `.workflow.json` file. The workflow export includes the workflow definition and references to dependent components, such as agents, tools, and auth profiles. These dependent components are not exported with the workflow. Workflow trigger definitions are not included in workflow exports. You must configure triggers manually after importing the workflow. ### Import a workflow 1. From the Workflows page, click **Import**. 2. Click **Choose file** and select the workflow export file. 3. In the Import Workflow dialog, enter a name for the workflow, and click **Preview**. 4. Review the workflow details and dependencies. Missing dependencies are identified in the preview. 5. If dependencies are missing, resolve them or select **Import with unresolved dependencies unselected** to continue without them. 6. Click **Import**. The imported workflow is created as an editable draft. Unresolved dependencies must be manually relinked before you run or publish the workflow. *** ## Execute and inspect workflows After configuring workflow nodes and triggers, run the workflow to validate workflow behavior and inspect execution details. ### Run a workflow 1. Open the workflow. 2. Go to the **Flow** tab. 3. Click **Run**. During execution: * Workflow execution status is displayed. * Human workflow nodes pause execution until user action is completed. ### Execution details panel When a workflow runs, the execution details panel opens on the right side of the canvas. It displays: * Execution status * Execution duration * Flow log * Node inputs and outputs * Context data * Final workflow output Select nodes from the execution flow to inspect execution details for individual workflow steps. *** ## Configure triggers Triggers define how workflows are started. Open the **Triggers** tab from the workflow toolbar to create and manage workflow triggers. The workflow builder currently supports the following trigger types: | Trigger type | Description | | - | - | | **Webhook** | Starts workflows through external HTTP requests. | | **Cron schedule** | Runs workflows automatically on a schedule. | | **App triggers** | Starts workflows when an event occurs in a supported external application. | | **Intake form** | Starts a workflow when a user submits a form. | | **After session ends** | Starts a workflow when a user session ends, with optional session filters. | | **After an unsuccessful call** | Starts a workflow when a campaign call is unsuccessful on the final attempt, with optional filters. | ### Webhook trigger Use webhook triggers to invoke workflows from external systems, applications, or APIs. When a webhook trigger is created: * A webhook endpoint URL is automatically generated. * You can generate an API key for secure access. * Quick-start request examples are displayed for testing and integration. **Webhook execution modes** | Mode | Description | | - | - | | **Sync** | Waits for the workflow response. | | **Async + Poll** | External system polls for results. | | **Async Push** | Workflow returns results through a callback. | **Configure request and response payloads** You can customize the structure of webhook requests and responses. Select **Default** to use the standard webhook structure or **Custom** to define your own structure for either: * **Request** — The payload received by the workflow. * **Response** — The payload returned by the workflow. ### Cron schedule trigger Use cron schedule triggers to automatically execute workflows on a defined schedule. You can configure a schedule as **Daily**, **Weekly**, **Monthly**, **Once**, or **Custom cron**. For custom cron schedules, you can specify a 5-field cron expression and timezone. You can also optionally limit when the schedule is active. Typical use cases include: Daily processing jobs, Weekly reporting, Scheduled synchronization tasks, or Periodic maintenance workflows. ### App triggers Use app triggers to start workflows when an event occurs in a supported external application. To configure an app trigger: 1. Select **App triggers**. 2. Select an app from the list of supported applications. 3. Select the trigger event for the app. 4. Select an **Auth Profile**, if required. 5. Configure any additional fields or filters displayed for the selected trigger event. 6. Click **Create Trigger**. The available configuration options depend on the selected app and trigger event. ### Intake form trigger Use an Intake form trigger to start a workflow when a user submits a form. | **Setting** | **Description** | | :- | :- | | **Trigger name** | Specify a name for the trigger. | | **Form** | Select the form to use. | | **Form version** | Select the form version to use. | | **Who can submit** | Configure access by allowing anyone to submit the form or requiring Basic auth. When **Capture email** is enabled, the form collects the submitter's email address. | | **Field mapping** | Submitted form values are available in the workflow as `{{context.trigger.form.}}`. | | **Submit behavior** | Configure the submit button label and choose whether to display a message or redirect the user to a URL after submission. | | **Link and publishing** | Configure the form path and environment aliases for development, staging, and production environments. The trigger provides a public URL for the form. | | **Limit submissions** | Limit the number of submissions by specifying a submission cap. You can also specify when the form stops accepting submissions and the message displayed after the form closes. | Each form submission starts a new workflow run. ### After session ends trigger Use this trigger to start a workflow when a user session ends. You can optionally filter the sessions that trigger the workflow. Configure the following options: | Field | Description | | - | - | | **Agent** | Selects the agent associated with the session. | | **Channel** | Selects the channel associated with the session. | | **Terminal source** | Filters sessions based on the source that ended the session. | | **Disposition** | Filters sessions based on the selected disposition. | | **Verified users only** | Limits the trigger to sessions from verified users. | | **Transcript window** | Specifies the transcript window to include when the workflow is triggered. | ### After an unsuccessful call trigger Use this trigger to start a workflow when a campaign call is unsuccessful on the final attempt, such as when the call reaches voicemail or an answering machine. You can optionally filter which calls trigger the workflow. | **Field** | **Description** | | :- | :- | | **Campaign ID** | Filters calls based on the campaign ID. | | **Dialer type** | Filters calls based on the dialer type, such as **Agentless**, **Preview**, **Progressive**, or **Predictive**. | | **Disposition** | Filters calls based on the disposition, such as **Voicemail** or **Answering machine**. | ### Run a trigger manually You can manually run a configured trigger from the **Triggers** page by clicking its **Run** icon. The options displayed depend on the trigger type. Enter any required input and click **Run now** to execute the trigger immediately. *** ## Monitor workflow execution Use the **Monitor** tab to inspect workflow runs and troubleshoot execution. The Monitor tab provides: * **Run history** to review workflow executions and their outcomes. * **Health metrics** to assess workflow performance and failures, including response time and failure rate. * **Run details** to investigate an individual execution. Select a run to view its details. ### Run details The run details view provides a consolidated view of the execution, including its status, outcome, execution flow, and associated data. | View | What you can inspect | | - | - | | **Summary** | Run metadata, workflow version, trigger, environment, status, and outcome. | | **Flow log** | The execution flow and status of individual workflow steps, including their duration. | | **Data** | Payloads, input, and output associated with the run. | Use the **Summary** view to understand the overall result of a run, the **Flow log** to trace its execution, and the **Data** view to inspect the data processed by the workflow. *** ## Workflow versions Workflows support version management through draft and published versions. Each workflow starts with a draft version, which is used for editing and configuration changes. When a workflow is published: * A new workflow version is created. * The published version becomes available for execution. * The draft can continue to be updated independently. Use the **Versions** tab to: * View workflow versions. * View version status and publishing details. * Manage workflow versions. The Versions view displays: * Version number or draft state * Workflow status * Environment * Publishing information * Version actions *** ## Configure workflow settings Use the **Settings** tab to manage workflow metadata, variable namespaces, and workflow deletion. | Setting | Description | | - | - | | **Name** | The name of the workflow. | | **Slug** | The unique identifier for the workflow. | | **Description** | A description of the workflow. | | **Variable namespaces** | Select the variable namespaces that the workflow can resolve in environment and configuration expressions. If no namespace is selected, the default namespace is used. | | **Delete a workflow** | Permanently delete the workflow, including its versions and triggers. This action cannot be undone. | *** ## Attach workflows to agents Workflows can be attached to agents as reusable tools. This allows agents to invoke durable workflows during runtime interactions. Attached workflows allow agents to execute workflow logic during conversations and task execution. ### Register workflow as a tool 1. Go to **Tools > Workflow Tools.** 2. Click **Register Workflow**. 3. Configure the workflow tool settings. 4. Select the workflow and workflow version. 5. Select the trigger and execution mode (sync or async). 6. Click **Register Workflow**. ### Workflow tool definition After a workflow is registered, it can be attached to agents as a workflow tool. The platform automatically generates a DSL/ABL definition for the registered workflow tool. Workflow inputs are exposed as tool parameters in the generated definition. The generated definition includes: * Tool metadata * Workflow and trigger identifiers * Input parameters * Tool description Example: ```yaml theme={null} TOOLS: sendemail(email_text: string) -> object description: "This tool will send an email using Google Gmail" type: workflow ``` The generated definition is available from the DSL editor and can be used together with visual agent configuration on the interface. The platform maintains a one-to-one mapping between the visual configuration interface and the DSL/ABL definition. Changes made in one view are reflected in the other. The UI configuration is derived from the DSL definition. Changes made in the UI are reflected in the DSL, and changes made in the DSL are reflected in the UI. ### Attach workflow tools to agents After registering the workflow tool: 1. Open the agent. 2. Go to **Capabilities** > **Tools**. 3. Attach the workflow tool. 4. Configure tool settings if required. 5. Configure required input collection under **Capabilities** > **Gather Fields**. 6. Save the agent. The agent can now invoke the workflow during runtime. ### Understand gather and workflow inputs Gather fields can be configured from: **Capabilities** > **Gather Fields** Workflow tools can collect inputs using: * Automatic parameter inference * Gather fields Automatic parameter inference allows the agent to infer required tool inputs from the conversation context. Gather fields provide explicit input collection and validation for workflow tool parameters. Use gather fields when you need: * Structured prompting. * Controlled input collection. * Required field enforcement. * Validation and extraction guidance. * More deterministic workflow execution. # Business Solutions Source: https://koreai.mintlify.app/ai-for-service/solutions-overview Create customer experiences across voice and digital channels. Automate routine tasks and enable your employees. Streamline knowledge-intensive business processes. Docs of previous version of the Agent Platform. # Kore.ai Product Documentation Source: https://koreai.mintlify.app/home Build and deploy AI agents at enterprise scale — quickly, securely, and flexibly.
Kore.ai Agent Platform

\{ **Artemis** } the next-generation Kore.ai Agent Platform for building, deploying, and governing programmable agent systems at enterprise scale with certainty:

} title="Dual-Brain Architecture" description="A split-brain runtime: an agentic engine that reasons and a deterministic validator, running in parallel over a shared spine." /> } title="ABL™ — Agent Blueprint Language" description="One DSL for agents, flows, tools, and policies. Compile-time validated, version-controlled, AI-programmable." /> } title="Arch™ AI — The AI That Builds AI" description="A multi-agent system that drives the full lifecycle: interviews stakeholders, designs topology, writes ABL, learns from production." /> } title="Multi-Agent Orchestration" description="Compose specialists with HANDOFF, DELEGATE, FAN_OUT, and A2A — typed payloads, governed handoffs, unified traces." /> } title="Tools & Integrations" description="Five tool types (HTTP, Code, KB, Workflow, MCP) under one Auth Profile — agents reach every enterprise system without glue code." /> } title="Search & Knowledge AI" description="RAG, vector search, knowledge graph, and reranking — with intelligent pipelines that chunk, embed, and enrich automatically." /> } title="Evals, Quality & Observability" description="Persona × Scenario evaluation matrices, five-dimension quality monitoring, and 200+ trace events per session. Validated trust, not vibes." /> } title="Enterprise Foundation" description="SSO, RBAC, KMS (BYOK), guardrails, PII detection, audit logging — SOC-2, GDPR, FedRAMP ready. Deploy SaaS, on-prem, VPC." />
Resources

Learn, Connect, Get Help

} title="Academy" description="In-depth training courses on Kore.ai products, certifications, and feature deep-dives." /> } title="Community" description="Search threads, post questions, and get answers from Kore.ai users and experts." /> } title="Support" description="Open a ticket or browse the knowledge base for technical help from our team." />