Skip to content

Author a routine

A directive steers one turn. A routine carries a flow across many turns: it can collect values, call a skill, take a branch, and finish or hand off to a person. Use a routine for an order-issue intake, lead capture, or refund check.

In an agent’s Routines settings, choose New routine to start one, or select an existing routine to open the editor.

The routine document

The editor presents the routine as a document: the flow as readable instructions, with typed controls at each decision. The engine compiles it into the graph it runs, so you work with the routine instead of drawing the graph.

Every change saves into the agent’s private draft as you make it, so the document is always the form a colleague reads to learn what the agent does.

The Damaged delivery intake routine open in the routine editor, with the routine's name as the page title, a Draft pill beside it, the collapsible When to trigger section holding the Activation trigger, two required text slots named order_number and issue_description, and the first numbered step.

Work in the document

Document opens with a collapsible When to trigger section holding Starts when, followed by Instructions — the numbered steps and their branch rows. Information, Endings, and Completion export sit inside an Endings & information disclosure at the bottom, closed by default. A skill step shows its uses → sets bindings in its step editor, opened from the step’s own token, so the flow also shows where information comes from and goes without crowding the sentence.

Edit the starts-when line directly. In a step instruction, type @ to capture a value, then choose its type in Information. Use + Step to add chat, skill, approval, or action steps. Condition rows choose a Rule or AI decides decision and its target. Ending rows set the message for a completed or handed-off flow. Delete a step with Backspace in its empty instruction, the trash next to its reorder arrows on hover, or the step editor panel; a branch that pointed at it retargets to a new, blank ending.

Validation notes appear beside the row or field that needs attention. They point to issues such as a missing target, an unset required skill input, or a comparison whose value type needs adjustment.

Set the routine header

The routine’s name is the page title — click it and type a new one. A pill beside it reads Draft or Live, and the round button next to the pill switches between them. On a blank routine, Draft from procedure is a visible starting option; it is also in the overflow menu with Delete routine. Test opens the draft in Test Chat.

Open When to trigger, then Starts when, to set:

  • Priority — higher whole numbers win when several routines could start at once. Use 0 for the standard priority.
  • Reentry — what happens after the routine finishes in a conversation.
  • Activation trigger — a plain-language description of when the routine should start.

The trigger is judged by meaning, so write it the way an operator would describe the task: “customer reports coffee that arrived damaged, stale, or wrong.” The field shows this kind of example while it is empty.

You can also select an Answer coverage signal in the activation header. For example, a routine can be eligible when a request is Unanswered because of Insufficient evidence. This is an extra typed condition alongside the trigger. Normal activation, confirmation, and action permissions still apply; recognizing a gap alone does not start an external action.

Reentry

A routine can finish more than once in one conversation. Reentry decides what a later matching message does:

  • Once per conversation — the default for one-time tasks such as capturing a lead.
  • Every time it matches — for repeatable tasks such as looking up an order.
  • Let the assistant decide — the agent chooses whether to resume the run, start a fresh one, or leave the completed run in place.

Expose a routine as a tool

A routine normally starts when its trigger matches what a customer says. An AI agent calling your agent on a customer’s behalf can start it more directly: as a named tool, with the routine’s collected information filled in up front. Open Starts when and switch on Expose as a tool, then give the tool a name and a one-line description that tells a calling agent when to use it.

The name is what the calling agent invokes, so it follows the grammar tool catalogs expect: 2–63 characters of lower-case letters, digits, and underscores, starting with a letter — start_return, request_callback. The editor accepts whatever you type and reports a name outside that grammar as a validation note, the same way it reports an unreachable step; Review & Publish refuses the revision until the name is fixed. Two routines on the same agent cannot share a name, and the names the MCP surface keeps for itself — ask_agent, radioso_docs, radioso_doc_page, and get_conversation_updates — are refused as exposure_tool_name_reserved.

Once the agent is published with a tool name, that name stays fixed for the routine — even while you later switch exposure off — so a calling agent never finds the same routine under two names. A publish that renames it is refused with exposure_tool_name_changed. To offer the routine under a different name, switch its exposure off and create a new routine with the new name.

A routine whose activation has a gate cannot be exposed: a tool call would start it without the gate being checked. Such a routine stays reachable through conversation as before.

What a calling agent sees

Once the agent is published, a calling agent lists the routine as a tool built from the information the routine collects. Each declared slot becomes one input: a text slot is a string, number and yes/no slots keep their types, an email slot is a string that must look like an address, and a date slot is a string that must be an ISO calendar day (2026-09-01). A slot marked required is required in the tool; the slot’s description travels with it, so a clear description helps the caller fill it correctly. A routine with no slots is still a valid tool — a “request a callback” routine needs nothing up front.

A call with values filled in skips the steps that would have asked for them and lands on the first step that still needs something: a slot the caller left out, an approval, a skill. From there the routine runs exactly as it does in chat, and the reply names the routine, where it stopped, and every slot it still needs. A call that does not match the tool’s inputs is refused before the conversation records anything. Activity shows a tool call as a block with the tool name and the values it carried. The calling agent guide shows the catalog and the call from the caller’s side.

Collect values and add steps

A value is information the routine collects, such as an order number or email. In Document, type @ in an instruction to add a value like @order_number. The routine waits at a step that asks for a value, stores the visitor’s reply, then continues through the flow.

Choose a type — text, number, boolean, email, or date — in Information. Open a value’s row there to switch Required off when the flow may reach an ending while the value remains empty, turn on Editable after completion when a visitor may correct it after the routine ends (see Changes after a routine ends), or turn on Sensitive when it is something the visitor would not want copied around. Ray switches Editable after completion and Sensitive on or off for a value, and only when you ask.

The type decides what the routine keeps. An email has to look like an address, a date has to be a real day on the calendar, and a number has to be a number. When a visitor’s answer does not fit — “giulia at example” for an email, “the 31st of February” for a date — the routine keeps nothing for that value and asks the step again. The turn trace lists the value under rejectedSlots, by name only. The same list can name a value the routine never defined — the model read more from the visitor’s message than any step asked for; it is dropped before it reaches a step, a skill, or an action.

The @ menu also lists what the agent already knows about the visitor under Visitor context. Choose Current page to place it in a step, and the step reads the page’s URL, title, and language when it runs — enough to write “If the visitor is on a program page, confirm that is the program they want to book; otherwise ask which program @program”. A host-defined context variable the agent has enabled appears in the same list. This is a value the step reads, not one it stores: nothing is added to Information, so ask for anything you want to keep with an @ slot.

A blank numbered line always sits after the last step — click it to write a chat step directly. The + Step menu adds chat, skill, approval, and action steps. A skill step can use a fixed input or a value the routine holds, then assign its outputs to values that later steps can read. For example, a refund lookup can use @order_number and set @refund_status for a later branch.

A chat step’s instruction is the controlling instruction for its reply. The agent’s directives still shape how the reply is said — tone, formality, what it must not claim — but they don’t swap the step’s question for a redirect or a hand-off, and they don’t add contact details or links the step doesn’t ask for. A step that asks for a name and email asks for them even when an Always directive tells open answers to send booking questions to reception. See Directives on a routine turn.

A chat step’s reply takes its facts from the step’s own instruction, including any Visitor context chip placed in it, and from the sources it has on that turn: the text of the page the visitor is on, when the step references Current page and the embed sends page content, and the documents a Retrieval context step fetched earlier in the same turn. A chat step searches no documents itself. When a step says “tell them the edition dates from the page” and the visitor isn’t on a course page, the reply is told it has no page text and no documents, so it asks the visitor for their dates instead of stating the course’s. A visitor’s own dates, such as “I’m free between the 17th and 27th”, reach it as the visitor’s words, not as a source. Put a fact every visitor needs in the instruction itself, or in a page or document the step reads.

Sensitive values

Say a class-booking routine asks “Any injuries we should know about?” and keeps the answer in @injury. Turn on Sensitive for that field, and the answer stays where the conversation runs: later steps still read @injury, and your team still reads it in the Inbox transcript. Every copy that leaves the conversation shows it masked:

  • The team notice lists the field as Injury: sensitive, open the conversation to read it, and a {{slot.injury}} in its subject or intro reads (sensitive). The notice’s Open: link goes straight to the conversation. The notice webhook, a completion export, and an action step’s payload carry [redacted].
  • A skill step with no inputs bound hands the routine’s values to its skill with @injury as [redacted]. A skill receives the value only when the step binds one of its inputs to @injury — the binding shows on the step, so anyone reading the routine sees where the value goes. Switching an input to a variable never picks a sensitive field for you, and Ray flags a step whose skill would receive one as [redacted].
  • A Test Chat trace shows [redacted], and Continue in Test Chat copies the conversation with the sensitive turns withheld and the routine’s value as [redacted]. A branch rule or AI condition that reads @injury then sees [redacted], so the continued test can branch differently than the conversation did.
  • The visitor’s reply to a step that asks for @injury is marked as soon as it arrives, and so is every turn after which the routine holds the value. Any message sent while that step waits is marked, an off-topic question included, so it stays out of Audience Pulse and eval snapshots too. Marked turns read as [redacted] in the conversation summary, Audience Pulse, a notice’s AI summary, and an eval snapshot, which also captures the routine’s value as [redacted]. To replay such a turn with a value, run the case with a routine start state of your own (see Evals).

Only you mark a field sensitive: Radioso never decides it from the field’s name or the visitor’s words, and Ray sets it only when you ask. The model the agent runs on still reads the value, since it is part of the conversation. A value the visitor gives while no step is asking for it, before its step or as a correction after the routine has finished, is marked only when the routine stores it, and once the routine has ended a later reply that mentions the value is not masked.

Changes after a routine ends

Say a visitor gives an arrival date of 5 January, then tells your booking routine they’re only browsing, and it ends without telling anyone. With @arrival marked Editable after completion, “actually I’d arrive on 9 January” updates the date the routine holds, and the agent confirms it noted the change. It reads “9 January” against today’s date, so the year comes out right.

The agent takes a change only when the change is real:

  • The routine holds the value. A routine that ended before the visitor gave a date holds no @arrival, so “I’d like to book from 8 to 10 January” is a new request, not a change.
  • Nothing the routine collected has left the conversation. Once its ending notified your team, handed off, or sent the completion export, or a skill, action, or approval step ran, a changed value would reach none of them. The agent takes no change and answers the message like any other. Every skill step counts, even one that only looks something up.
  • The visitor is changing what they gave. A request for something new, such as another booking with different dates, is not a change either.

A message that isn’t a change goes on as any message does, and the routine’s reentry setting decides what happens to it. Under Once per conversation, the default, the finished routine doesn’t start again, so “I’d like to book from 8 to 10 January” gets a normal answer. Under Every time it matches it starts a new booking. The agent keeps a finished routine for 30 minutes after its last update; after that, no change is taken.

When a routine can’t end without sending what it collected, Editable after completion does nothing on its fields. The field’s row says so beside the switch, and Ray’s validate_routine reports each such field as mutable_slot_not_correctable. Neither stops the routine from saving or publishing.

Set branches, approvals, and endings

Add a condition row beneath a step — the round + under its branches, or + Condition in the step’s own editor — to choose its next target: another step, Finish, or Hand off. Rows run in order. Use Rule for an exact typed check, skill outcome, filled-value check, or bounded retry. Use AI decides when the agent needs to judge the condition in the conversation; type @ in the condition box to reference a collected value.

An approval step pauses the routine for a workspace member. Add it with + Step, name the decision, then give each choice a label and target. The initial Approve and Decline choices suit many flows; a choice such as Ask for receipt can direct the flow to collect more information. The selected choice continues along its decision edge.

An ending row completes the routine with Finish or escalates it with Hand off. Set a message on the ending the agent should deliver. Named endings let different paths provide different completion messages. A Hand off ending also sends the operators the routine’s name and every value it collected, by email, the contact webhook, and Slack, so the person picking the conversation up starts with what the visitor already said.

A Finish ending can send that same notice while the agent keeps the conversation. Open the ending and turn on Notify the team: a booking routine then tells reception about the booking, and the agent goes on to ask whether there is anything else it can help with. The switch stays on for a Hand off, which always notifies. Subject and Intro are optional — type @ to insert a value the routine collects, as in New booking: {{slot.guest_name}}; a subject holds up to 200 characters and an intro up to 2,000. Blank fields use the default subject, such as Book accommodation: completed, and every notice lists all the collected values below the intro, so a short subject never hides what the guest said. A value the routine did not collect shows as —. The webhook body is documented under Operator notifications.

Turn on Include AI summary when the team also needs relevant context the routine did not collect in a field. Radioso generates it after the ending has queued its notice, keeps the collected values as the authoritative record, and labels the extra section AI summary. If generation cannot finish, the notice still sends without that section.

Under Notify the team, the Send with select chooses which notify skill delivers the notice: Default, or any enabled notify skill the routine can name directly. The line underneath shows where that choice goes right now — Valmora’s booking routine names its notify_bookings skill on the Booked ending, so the line reads Sends to marta@valmora.example (waiting for confirmation) until Marta uses the confirmation link. Confirmed addresses have no suffix; grandfathered, revoked, and bounce- or complaint-suppressed addresses carry their own status beside the address. Choosing Default falls back to the chain above: the line then shows the contact_human recipients, the agent’s own delivery setting, or the workspace owner’s email, whichever resolves first. A Hand off ending offers the same select, since it always notifies.

Replies go to picks the email field whose address reception’s reply goes to: any of the routine’s email fields, or No reply-to. With guest_email chosen on Valmora’s Booked ending, Marta hits Reply and writes straight to the guest. Radioso uses only the field you pick — a gift booking that collects the buyer’s address and the recipient’s is answered at whichever one you chose. A notice you turn on, or a new Hand off ending, starts on the routine’s only email field when there is exactly one, and on No reply-to when there are several. A guest who never gave that address gets a notice with no reply-to.

When the routine has an email field and a notifying ending’s Replies go to is No reply-to, a note under the select reads “Replies won’t reach the visitor. Choose their email field.” The ending still saves and the routine still publishes, so a routine with nobody worth replying to stays as it is.

The first five contact or team-notice dispatches in a conversation are eligible for delivery. After that, Radioso records a conversation_dispatch_cap outcome and skips the dispatch. Email recipient attempts also count against the workspace’s daily plan allowance; reaching it records workspace_daily_email_cap and leaves a configured webhook active. An external address must confirm within its workspace before it receives a notice. Adding it to a notify skill sends the confirmation message right away, so Marta can confirm before the first booking arrives. An address configured before confirmation existed keeps receiving notices and gets the confirmation message with its first one; it has 14 days from that message, and at least until its recorded deadline, to confirm. Every notice carries a one-click unsubscribe link, and a hard bounce or complaint suppresses notification mail to that address across the deployment. When the workspace’s daily email allowance stops a notice, a line under the destination says so; the allowance resets at midnight UTC, and a configured webhook still receives the notice. Routine slot text is capped at 1,000 characters before it reaches either delivery, and a collected reply-to is used only when it is a valid email address.

A step is asked the same way at most three times in a row when the visitor’s replies give it nothing new. From then on it asks in different words, saying plainly what it needs and giving an example of an answer that works. The limit never fires a row you set: a Hand off row may be the step’s confirmation, and taking it would send a request the visitor never confirmed.

What comes next depends on whether the routine hands off at all. When some row leads to a Hand off ending, a visitor who still gives the step nothing after it asked differently goes to a person. The routine ends on that step, tells the visitor someone will continue the conversation, and notifies your operators with the reason routine_stuck and every value collected so far. The Hand off ending’s message and the completion export stay for visitors who reach that ending. A routine with no Hand off ending stays on the step until the visitor answers it. To give a visitor an earlier way out of a step that collects something hard to get right — a booking reference, an exact date — add a row for it, such as AI decides “the visitor wants to talk to a person” → Hand off.

Validation and publishing

The editor validates as you work. A routine is one of the agent’s scoped editable areas, alongside directives, skills, and context variables. All four share one publication boundary: Review & Publish on the agent snapshots them together into a revision, and that revision serves conversations. So an edit changes what the agent will run after the next Review & Publish, and nothing before it.

A conversation keeps the revision it started on. A visitor part-way through a routine finishes on the version they began with, while you work on the next one.

Each routine also carries an enabled flag — the Draft/Live pill in its header. Switch a routine to Draft to take it out of play while keeping everything you built — useful for comparing agent behavior with and without it, or parking a flow you are still working out.

Try a routine before it ships

Choose Test to open the agent’s Test Chat with the draft selected. The draft holds the routine you are editing alongside the rest of the agent’s unpublished work, so the routine runs turn by turn in that conversation exactly as a publish would ship it, then returns to normal answering when it finishes. The routine has to be Live: one still marked Draft sits outside the draft’s activation set, so Test is disabled until you switch it over.

In Test Chat, you can also compare the draft with the published revision and run selected eval cases. Switching which version you test starts a fresh test conversation, so routine state from one cannot mix into the other. Evals are optional evidence for the publish decision.

Use the test to check the trigger, information collection, skill bindings, branches, endings, and handoffs before you publish the agent.

A private test keeps skills with outward effects off by default, so nothing sends to a real customer while you work. A tool step that calls an external MCP tool, a webhook, or a customer email or Slack skill reports failed with the reason skills are off in this test, and the routine takes its failed branch, so you can watch that path too. Choose Run skills for real in the Test Chat actions menu to start a fresh private test where every skill runs exactly as it would for a customer. Retrieval skills run in both modes, and test values still stand in for context variables either way. Action steps, handoffs, team notices, and completion export stay off in every private test; a test turn that reaches an ending that notifies shows the notice it would send, and the address replies would go to, in the turn’s trace instead.

A notify skill delivers through the saved conversation, so it stays off in every private test even with Run skills for real on. Retry re-runs the turn, so a skill the first attempt already fired fires again. Run evals always replays with skills off, and you can only capture an eval case from a test that kept skills off.

Common failure modes

  • A routine fails to start: the trigger describes the wrong situation, or a higher-priority routine claims the turn first. Test the trigger with Test.
  • A coverage condition does not activate: inspect the originating turn. A failed or unavailable assessment is recorded as not assessed and never activates a coverage condition.
  • A step skips value collection: add the value with @ in the instruction so the step refers to the stored information.
  • A step’s reply carries text from a directive: directives shape every routine reply. The turn trace lists them under routineStep.steeringDirectives on the directive_steering stage; narrow or turn off the directive when its wording does not belong in the flow.
  • A backward jump fails validation: set Max N so the loop has a bounded number of passes.
  • A routine never starts at all: check that its pill reads Live, not Draft.
  • A team notice shows — where a value belongs: the ending’s subject or intro references a value the routine did not collect on that path. Collect it in an earlier step, or drop it from the notice text.
  • A finish sends no team notice: check that Notify the team is on for that ending, and that the agent has a contact recipient — the notice goes where contact requests go.
  • The Send with select shows a name as (unavailable): the notify skill it names was deleted, turned off, or can no longer be named by a routine. Validation flags the ending until you pick another skill or switch back to Default. The notice is never dropped meanwhile: a skill that still exists and is on keeps sending, and otherwise the notice goes to the default destination, or to the agent’s contact settings or the workspace owner when contact_human is off.
  • The Replies go to select shows a field as (not an email field) or (sensitive): its type changed, or it was marked sensitive, after you picked it. A sensitive address never leaves the conversation, so it can’t take replies. Validation reports operator_notice_reply_to_slot_invalid until you pick another email field or No reply-to.
  • A visitor’s change after the routine ended isn’t taken, though the field is Editable after completion: the routine held no value for it, or its ending already notified the team, handed off, or exported, or a skill, action, or approval step ran. The turn’s span records why as routine.slot_correction.decision (not_held, already_sent, or new_request when the message asked for something new). When every way through the routine sends what it collected, validate_routine flags the field as mutable_slot_not_correctable.
  • A booking request after a booking routine ended gets a normal answer instead of a new booking: the routine runs Once per conversation. Set its reentry to Every time it matches to let it start again.
  • A webhook or Slack skill receives [redacted] for a value: the field is marked sensitive and the step passes it without a binding. Bind the skill’s input to the field on the step, or turn Sensitive off.
  • Reception’s reply goes nowhere useful, or the email has no reply-to: check Replies go to on that ending, and that the routine asked for that field on the path the visitor took. The editor shows a reminder under the select whenever a notifying ending has an email field available and none chosen — it never blocks saving, so a routine can ship with the reminder still showing.
  • Review & Publish refuses the agent with exposure_tool_name_changed: a published tool name is fixed. Keep the name, or switch exposure off and create a new routine under the new name.