ask_user tool is the core capability; ask_external
extends it to human actions that happen on an outside surface. This guide enables
the feature, asks in each answer format, answers from the CLI, and wires an
external callback end to end. For the model behind it, see
Interactions.
Enable interactions
Load theask_user tool module and the interactions HTTP router — the SSE inbox
stream plus the human answer door.
examples/interactions/enable_interactions.yaml
ask_user in scope can now pause and ask.
Ask a question
ask_user(question, answer_format=...) is called from inside a tool or agent run.
It records the question, blocks the caller, and returns the typed answer once a
person responds (or raises on timeout). One question declares one answer
format:
The first four are answered on the authenticated in-client surface. Example
ask_user calls (the tool input a caller passes):
group_id threads related questions, and timeout (seconds)
overrides the configured default before the call raises.
Show images and links with a question
Any question can carry media — images and links shown above the answer controls in the inbox — by passingmedia=[...] to ask_user. Media is
display-only: the person still answers through the answer_format. The canonical
use is a select that shows each choice:
{kind, url, caption?} object — an image (an absolute https
URL or a data:image/* URI) or a link (an absolute http(s) URL) — validated
against the contract before the question is stored:
examples/interactions/ask_with_media.yaml
ask_user tool using the same shape; media is an
array of the same objects:

A question with its media rendered in the Studio inbox, above the answer controls.
Answer from the CLI
A pending question surfaces on the authenticated SSE stream — the Studio inbox, or the CLI. List the current backlog, tail it live, or answer one:text
and select, a bool for confirm, an object for form:
examples/interactions/interactions_answer_help.sh
External questions and the callback flow
Theexternal format is for actions a person completes on an outside surface —
signing a document, approving a request, paying. The asking tool blocks exactly as
for any other format; only the delivery channel differs. The external system
delivers the answer back through a public callback door, not the answer door.
The ask_external transformer extension packages the whole flow. It wraps a tool
that builds an external resource from a callback_url and returns the URL to
visit. Attach it in the manifest, binding an optional webhook verifier that
authenticates the signed callback:
examples/interactions/attach_ask_external.yaml
verifier is author-bound extension config — it is closed over at build
time and is never an LLM-facing parameter, so a calling agent can neither drop nor
forge the callback authentication. The wrapped tool keeps its own inputs; the
extension injects question / answer_schema / timeout and hides
callback_url (the platform supplies it).
At run time, calling the composed tool:
- builds the external resource, substituting the minted
{callback_url}into thelink(or handing it to a callable link that returns the final URL); - blocks the caller while the person acts on that surface;
- accepts the answer when the external system POSTs the signed payload to
POST /api/interactions/callback/{ticket}(verified first when a verifier is bound), or when a person completes the GET confirm page; - validates the payload against the question’s
schemawhen one is declared, and returns it to the blocked caller.
External questions (and any
channel-delivered question) require
INTERACTIONS_PUBLIC_BASE_URL — an https:// origin, since the callback URL is a
bearer capability — and Redis. Callback payloads arrive through an
unauthenticated door, so treat every delivered answer as untrusted input.Deliver to a channel instead
Passingchannel="<name>" to ask_user hands delivery to a registered
channel plugin — Telegram, Slack, SMS/WhatsApp — instead
of the default inbox: the question mints a public callback ticket exactly like an
external ask, the plugin pushes it to the person, and the reply returns through
the same callback door. An optional recipient names the per-call address; an
unknown channel name or a delivery failure raises loudly before the run would wait
on an answer that can never arrive.
See also
- Interactions — the loop, the doors, and the security model.
- Triggers and webhooks — the verifier that authenticates a signed callback.
- Author a channel — deliver questions to an external medium.
- CLI reference — the full
tai interactionssurface.


