A2A in the Agentic Reasoning Engine
Native A2A support, where Moveworks calls a remote agent through its Agent Card, is available only on the Agentic Orchestrator. The Agentic Orchestrator is in Controlled Availability and comes to your Enterprise Assistant as an upgrade in Q1 2027. Capabilities, limits, and interface labels described here may change before general availability. To request access, contact your Moveworks account team.
Until your Enterprise Assistant moves to the Agentic Orchestrator, it runs on the Agentic Reasoning Engine, which cannot call a remote agent through its Agent Card. You can still connect it to external AI agents, LLMs, and AI-powered applications: wrap the external API in a Tool Studio plugin with HTTP Actions. This cookbook covers how to keep conversation context across turns, handle asynchronous APIs that require polling, manage cost and data privacy, and make the plugin trigger reliably.
Before You Start
- On the Agentic Orchestrator with an agent that publishes an Agent Card? Register the agent under A2A Agents in Tool Studio instead and select it for your assistant; the Orchestrator manages the conversation for you. See A2A Agents. Nothing on this page is needed.
- Otherwise, build a plugin. A plugin wraps the external API call so you keep control of when it triggers, through the plugin’s name, description, and utterances; what it sends, through slots and validation; how the reply is presented; and how data flows, through your configured connectors.
- A trusted MCP server is an option for bounded lookups. If the external system already exposes the operations you need as an MCP server, MCP Workspace can connect it without a plugin. Use a plugin when you need organization-specific logic or tighter control over inputs and outputs.
If you’re trying to connect to a Foundation Model like GPT, try our built-in plugin: QuickGPT.
Architecture Decisions
Context Engineering
There are three ways that you can manage context, each with their own pros & cons.
Example 1: Reasoning Engine Context via Slots (Simplest)
This approach lets the Agentic Reasoning Engine manage conversation context for you. The reasoning engine tracks the conversation history and decides what context to pass to your external API on each turn. This is the fastest way to get started, no thread tracking or database needed.
Your plugin will look something like this:
For the easiest implementation, we recommend the following high-level approach.
Create a Conversation Process with an action activity
Create a Conversation Process with an action activity for your agent’s API. This is the core of your plugin — it defines the flow that calls the external API and returns the response. Start by creating the process and adding an action activity that points to the HTTP Action you’ll configure in the next step.

Set up an HTTP action
Set up an HTTP action to call the external agent’s API. Here’s an example using the Anthropic API:
Map the slots to the action activity
Map the slots to the action activity in your conversation process. Pass the slots into the API call using DSL:
Example 2: API-Managed Thread with an Optional Thread ID Slot (recommended if available)
Some external APIs keep track of the conversation thread for you — you send a thread_id with each request and the API maintains the full message history on its side. Examples include OpenAI’s Assistants API, where the API stores all messages in a thread and you simply reference the thread ID on subsequent calls.
The key design pattern is to make the thread_id slot optional so that it sends null on the first turn (when no thread exists yet) and carries the returned thread ID forward on subsequent turns.
Slot Configuration
Create a slot for the thread ID with the following configuration:
The inference policy set to “Always Infer” means:
- First turn: No thread ID exists in context, so the reasoning engine infers
null. - Subsequent turns: The thread ID was returned in the previous response and exists in context, so the reasoning engine infers it automatically.
Conversation Process Implementation
This can be handled directly in a Conversation Process — no compound action or switch needed. The thread_id slot is truthy/falsy, so you can use DSL to conditionally pass it to the external API.
HTTP Action
Set up a single HTTP Action that accepts both the thread_id and the user message. The API should always return a thread_id in the response so it can be carried forward.
If the external API doesn’t automatically generate a new thread when thread_id is null, add an action step or logic in your compound action to create a new thread first, then pass the resulting ID to the main API call.
Conversation Process Setup
In your Conversation Process, wire the slots directly to the action activity. The input mapping uses DSL — since thread_id is falsy on the first turn, you can pass it as-is:
The action activity returns the response to the user. The thread_id from the API response is now part of the conversation context, so on the next turn the reasoning engine will automatically infer it into the slot.
Important: Make sure the thread_id is visible in the response output shown to the conversation. This is what allows the reasoning engine to pick it up as context on the next turn and infer it into the slot automatically. If the API returns it but it’s not surfaced in the process output, the reasoning engine won’t have it available to infer.
Example 3: Build Your Own Thread Store (ServiceNow Table or Custom Database)
Many external agents and LLMs don’t offer an API that keeps track of the thread for you, which means every API call is stateless — the external system has no memory of prior turns. You can solve this by creating your own thread tracking mechanism using a ServiceNow table (or any database accessible via API).
Create the ServiceNow Table
In your ServiceNow instance, navigate to System Definition > Tables and create a new custom table. A recommended setup:
Table name example: u_agent_thread_log | Label: Agent Thread Log
Set the u_conversation_history column to a max length of 65000 (the ServiceNow string max) or use a multi-line text field. For very long conversations, consider a strategy to trim older messages and keep only the most recent N turns.
Add a Business Rule for Auto-generating Thread IDs
Create a Before Insert business rule on u_agent_thread_log to automatically generate a unique u_thread_id when a new record is created. This way, your compound action only needs to POST the u_user_id and the first message — the thread ID is generated server-side.
Create the ServiceNow REST APIs
You need three operations, which you can accomplish via the standard Table API or a Scripted REST API:
Option A: Use the standard Table API
Option B: Create a Scripted REST API for cleaner endpoints and built-in logic (e.g., auto-trimming old messages, validating JSON structure). This is recommended if you want to encapsulate the history-append logic server-side rather than in your compound action.
Build HTTP Actions in Tool Studio
Create three HTTP Actions in Tool Studio, one for each operation:
- Create_Thread_Action —
POSTto create a new record. Send the user’s first message as the initialu_conversation_historyvalue (e.g.,[{"role": "user", "content": "..."}]). Returns thesys_idandu_thread_id. - Get_Thread_Action —
GETto retrieve the conversation history byu_thread_id. Returns theu_conversation_historyJSON string. - Update_Thread_Action —
PATCHto update the record with the latest user message and assistant response appended to the history.
Wire It Together in Your Compound Action
How the flow works:
- On the first turn, the compound action calls Create_Thread_Action, then calls the external LLM API with the user’s message, then calls Update_Thread_Action to store both the user message and the LLM response. The
u_thread_idis returned to the reasoning engine. - On subsequent turns, the reasoning engine passes the
u_thread_id(collected as a slot with inference policy set to auto-infer). The compound action calls Get_Thread_Action to retrieve history, constructs the full message array, calls the external LLM API, then calls Update_Thread_Action to append the new exchange. - Collect the
thread_idas a slot with an inference policy set to automatically infer from context — the reasoning engine will carry it forward across turns without asking the user.
This approach gives you full context continuity with any stateless API, and the conversation history lives in a system you control.
Handling Asynchronous APIs
Some external agents and APIs don’t return results immediately. Instead, they accept a request, return a job or task ID, and require you to poll for the result. You can handle this pattern in Tool Studio using a compound action with chained action steps and delay_config to space out polling attempts.
The pattern: Submit, wait, and poll with stacking intervals
Rather than polling aggressively (which wastes API calls and may hit rate limits) or waiting too long (which degrades user experience), use a stacking wait strategy that starts short and gets progressively longer:
- Submit the request — Call the external API to kick off the async job. Capture the
job_idortask_idfrom the response. - Wait 15 seconds, then poll — Use
delay_configon the next action step to pause, then call the status endpoint. - If not ready, wait 1 minute, then poll again — Use a
switchto check the status. If still processing, hit a second polling step with a longer delay. - If still not ready, wait 5 minutes, then poll a final time — A last attempt with a longer window for slow-running jobs.
- Return the result — If the job completes at any polling step, return the result.
Adjust the polling intervals based on the expected response time of your external system. For APIs that typically respond in under a minute, you might use 5s -> 15s -> 1m. For long-running jobs, consider 1m -> 5m -> 15m. Set the last poll for the upper bound of the system you are connecting to.
Token Consumption and Cost
LLM providers charge based on the number of tokens processed (both input prompt and output generation). Long conversations or large documents can become expensive quickly.
Best Practices:
- Set Limits: Always use the
max_tokensparameter in your API calls to cap the length of the response and prevent unexpectedly large (and expensive) outputs. - Be Concise: Encourage users and design system prompts to be as concise as possible.
- Monitor Usage: Regularly check your API usage and cost dashboards on the LLM provider’s platform.
- Choose the Right Model: For simpler tasks, consider using smaller, faster, and cheaper models instead of the most powerful (and most expensive) ones.
Data Security & Privacy
Standard public LLM APIs may use your prompt data to train their models. Sending Personally Identifiable Information (PII) or sensitive company data is a significant risk.
Best Practices:
- Consult Your Security Team: Always review the data privacy and terms of service for any LLM provider.
- Prefer Enterprise Offerings: Whenever possible, use enterprise-grade services like Azure OpenAI or an OpenAI Enterprise agreement, which typically guarantee that your data will not be used for model training.
- Anonymize Data: If you must send potentially sensitive information, build steps in your workflow to find and replace sensitive data with placeholders before sending it to the LLM. You can use our LLM Actions to do this.
- Educate users: Inform users about what data is being sent to a third-party service and advise them against submitting sensitive information. You can do this through a Content Activity & enabling the Activity Confirmation Policy on your API call.
Plugin Selection
Triggering reliability can vary depending on the use case and breadth of positive utterance subject matter. Below are some options to optimize your LLM plugins:
- Define Diverse But Specific Utterances: In your plugin’s trigger configuration, provide a wide range of example phrases. For a summarization plugin, this could include:
- “summarize this document”
- “give me the tl;dr”
- “what are the key points of this?”
- “can you create an executive summary”
- Define a trigger keyword: Assign a deterministic triggering phrase to your plugin so that users can trigger the plugin on command — this will help ensure the agent is always called.
- Use a System Prompt: Instead of relying on the user to frame their entire request, use the
systemmessage (or an equivalent field) in your API request body. This pre-prompts the LLM with its role or instructions (e.g., “You are an expert at rewriting text to be more professional”). The user then only needs to provide the core input, making the interaction much smoother.

