Before diving in, we strongly recommend reading the Data Retrieval Cookbook
That cookbook establishes the core patterns for finding and shaping data skills this cookbook relies on. Once you understand how to retrieve objects deterministically, this guide shows you how to safely write updates back to your System(s).
Updating enterprise systems is deceptively hard. Users speak in loose natural language, while systems like ServiceNow, Workday, and Snowflake require precise, auditable, and fully specified writes. This cookbook explains how Agent Studio bridges that gap by turning intent into trusted system of record updates with writes that are safe, validated, and reversible where supported.
At the heart of this cookbook is the Data Transaction Pattern, a deterministic flow that every write operation should follow to stay consistent, compliant, and easy to reason about.
This keeps updates correct, predictable, and audit-friendly, even when the conversation is ambiguous.
Example (simple update)
“Set INC0012345 priority to High.”
→ The system resolves the incident, validates the change, previews it, then writes:
Assistant preview:
“I’m about to change INC0012345 priority from Moderate to Critical. Should I proceed?”
Example (heavier multi-step)
“Reassign all open P1 tickets in EMEA to @jdoe and add a note.”
→ The assistant resolves the ticket set, then applies a series of targeted write operations. Updating owner, adding notes, and informs the user of the changes made.
This cookbook builds on that flow and expands it into a set of practical patterns you can reuse:
By the end, you’ll know how to design write flows that are:
In short, this cookbook provides the patterns you need to build production-grade, system-of-record writes that users can trust and enterprises can depend on.
Plugins should be atomic, predictable, and reusable. If one plugin can complete a change reliably, use one. If the change spans multiple distinct responsibilities, create multiple plugins and rely on the Reasoning Engine to call them for the user’s needs. Avoid overly complex plugins when not necessary with needless paths and conversational turns.
Use when
How it works in Agent Studio
target_incident).Flow
Example: ServiceNow — change assigned_to (inline resolve + write)
Single plugin (resolve → Make the change)
Conversation
assigned_to for the chosen sys_id.One Trade-off with this approach is it may add an extra turn in conversation depending on context
Tip: Keep the resolver’s list short and consistent in shape. This preserves token budget and keeps the selection UX fast and reliable. Filtering in the resolver helps here.
Use when:
Flow
Example: ServiceNow — change assigned_to
Lookup plugin (returns incident info)
Write to System of Record plugin (Set ticket owner and takes an incident sys_id and returns ticket id and new user assigned to)
Conversation
sys_id.The reasoning engine is able to take the returned context of the initial plugin called and pass it into the 2nd plugin when the user asks because the return mapper of the 1st plugin returns the sys_id of all the tickets.
Some write operations require substantial upstream context that shouldn’t be left to probabilistic reasoning. Scheduling is a classic case. When a user says, “Book 30 minutes with Alice and Brian tomorrow afternoon,” the actual event creation depends on deterministic prep:
Because this workflow involves multiple network calls and set intersection logic across calendars, we implement it as a compound action. The reasoning engine gathers a few high value slots (attendees, window, duration) and receives only the final common availabilitysmall, structured output that’s easy to display or pass into another plugin.
If you embed every sub-call directly in a conversational process, each intermediate response is exposed to the reasoning engine, increasing token use and ambiguity. Prefer returning only the minimal result, which keeps context tight and improves reliability.
A good rule is any time you have multiple actions in succession that don’t require additional input at each step is to use a compound action for those components.
Users often ask for fan out writes, e.g., “Set all open P1 incidents in EMEA to Awaiting User and add a note,” or “Move these five opportunities to Negotiation and tag the exec owner.” The safest approach is to keep each plugin single-purpose (one record, one change to system of record type), then let the Moveworks Reasoning Engine plan multiple calls as needed. Avoid “mega-plugins” that attempt to infer targets, compute diffs, and apply heterogeneous mutations they increase ambiguity and conversational complexity.
Design rules
set_incident_state, update_opportunity_stage, toggle_feature_flag).This is true for most scenarios, if the context is too large (trying to pass large structured objects with many fields or a very large amount of data 7k+ tokens) Then it is best to avoid letting the reasoning engine handle the updates.
The better approach in those scenarios is to use compound actions with for loops to deterministically make large updates to many records at once.
Intent: “For my open P1 incidents in EMEA, set state to Awaiting User and add a note ‘Pending customer response’.”
Plan:
Having one plugin that can return the list of the user’s open tickets and the details on them allows the reasoning engine to retrieve all the open tickets then it will call the plugin to change the state to Awaiting user for N amount of tickets
Sometimes you may want to explicitly ask the user if they want to set an extra field —e.g., add an assignment group when creating or updating a ServiceNow incident. Most APIs expect either a concrete value (like a sys_id) or null/empty string. In Agent Studio, handle this cleanly by gating the write with a Decision Policy and a simple boolean slot.
Ask intent, not value (yet):
Create a boolean slot like does_user_want_to_add_assignment_group that captures whether the user wants to include the field.
Branch with a Decision Policy:
does_user_want_to_add_assignment_group is False → call the action with assignment_group set to null , empty string (in yaml input args is assignment_group: ‘””’,(or simply omit the field, per your API’s semantics).assignment_group using your Dynamic resolver (if you need to get a list of assignment_groups for the user to select from to get the associated id otherwise you could just take something like a string), then call the same action with that ID.Keep comfirmation on for the action activity so the user can review which fields will be written, including when an optional field is left blank.
Slot Config
Decision policy
Tips
does_user_want_to_add_assignment_group = False and only make it true if the user explicitly provides itUse hierarchical resolvers when one Slot depends on another Slot’s value. This is accomplished with the context passing in resolvers feature.
Example payload: resolve a purchase_order first, then use that selection to resolve a line_item.
Use this pattern when:
High-level flow
purchase_order) resolves normally using a dynamic resolver.line_item) has a strategy mapping whose method takes data from the parent as an input.
data.purchase_order (the parent Slot value) is passed into the child resolver method input.data object (includes either:purchase_order object)Step-by-step: set up a parent → child resolver
Define the parent Slot and resolver (e.g. purchase_order)
purchase_order Slot.Result: once resolved, the purchase_order Slot value is available on data.purchase_order at the Plugin level.

Define the child Slot’s resolver method (e.g. line_item)
Now create a Slot that depends on the parent:
Create a line_item Slot.
Attach a Resolver Strategy with a dynamic Resolver Method, e.g. select_line_item.
In the Input Arguments JSON schema for select_line_item:
purchase_order input is going to come from Plugin context, so it does not have to be included here.
"purchase_order": { "type": "string" }) for reuse, but it’s optional and will be overwritten with the mapped input.Map parent Slot into child resolver inputs (Strategy Mapping)
On the line_item Slot config:
Scroll to the Resolver Strategy section.
Click View Strategy Mapping.
Locate the mapper box for your pick_line_item method.
Map the parent Slot into the resolver method input, e.g.:

What this does:
line_item, the system will:
purchase_order.id Slot value.purchase_order_id input.purchase_order.You can also pass other context if needed for example:
Wire the child resolver’s Action input mapper
On the Resolver Method’s Action for select_line_item:
data to reference both:
purchase_order_id)search_query)Example:

At this point:
data.purchase_order comes from the Strategy Mapping (parent Slot).data.search_query comes from the LLM based on the Input Arguments schema.Ensure correct Slot ordering
For this to work reliably, the parent Slot must exist before the child Slot tries to use it.
line_item is required (Activity or Decision Policy):
purchase_order is selected as a required Slot before line_item.data.purchase_order may be missing when Strategy Mapping runs, and the resolver or its Action may fail.Some downstream APIs require canonical, country-specific formats—for example, postal addresses or phone numbers. While a well-prompted slot often captures these correctly, you’ll sometimes need an extra normalization/validation step before you write.
mw.generate_text_action to normalize user input into the exact shape your API expects, then pass that value to your write action. Keep Require consent on so users can review the normalized value before you send it.mw.generate_text_action)Flow
address_raw via a slot (optionally collect country).Generative action (illustrative example)
Using built-in action mw.generate_text_action
Preview messaging
I’ll update the address to:
Original: “1600 Amphitheatre pkwy , Mountain View”
Normalized: “1600 Amphitheatre Pkwy, Mountain View, CA 94043, United States”
Proceed?
Flow
phone_raw and, if possible, country or country_code.+16505550100) with mw.generate_text_action.Generative action (illustrative example)
Using built-in action mw.generate_text_action
Preview messaging
I’ll update the phone to +44 20 7946 0958. Confirm?
If you have a first-party validator (postal service, compliance gateway, or carrier lookup), call it after slot collection and before the write. Use the API’s status to guide the UX:
Using a Decision Policy to restart Slot collection when the validation API says the value is invalid
