A software agent acts on a person’s behalf, and someone has to say “yes” to the risky part of that
work — consent that is worth little if it is collected on the very screen the agent controls. Why
this matters and for whom is covered on the
Approving AI-agent actions page. Here is
the engineering side.
What it builds on
The scenario needs no separate protocol: confirming an action is the same transaction as
confirming a sign-in, only with a different purpose. A transaction with an explicit state and a
single outcome, a message in a trusted channel, the person’s answer and an entry in the audit
trail — all of it already exists in Veriqa’s core.
For confirming an action rather than a sign-in, the product has:
- Server-to-server creation of a confirmation transaction. The application calls
POST /api/transaction/confirmation, authenticating with client credentials — no OIDC request
and no user redirect. The response carries the entry point for the person: the channel’s deep link and
the same address as a QR image, with a “Powered by Veriqa” attribution strip under the code. The
application reads the outcome with
GET /api/transaction/{id}/result.
- Concrete wording of the confirmation. The request names a declared action type
(
action_type), and the person sees not “confirm sign-in” but that action’s message: which
action, over what, with which parameters.
- A safe way to carry the action’s data. Free-form text from the application never reaches
the user’s message — that is a direct route to substituting the meaning of the confirmation. The
data travels in typed slots (
slot_values) against the schema declared for the action type and
is validated before the transaction is created.
When you build the agent yourself and its tools are known in advance, nothing more is needed. The
approval goes where the tools are invoked, not into the prompt:
- Each tool with consequences gets an approval policy in your code: which declared action type
asks the person, and which arguments of the call fill its slots.
- The code that invokes tools checks the policy. For such a tool it creates the confirmation, shows
or sends its entry point, and waits for the outcome.
- The tool runs only on
confirmed. On declined or expired the harness returns a refusal as the
result of the call, the same way it reports any other failed call to the model.
Whatever the model decides to call, a tool with a policy does not run without a confirmed
transaction. The person reads the slot values of a declared action type, never text the agent wrote.
The runnable example is samples/dotnet/inproc/agent-approval in the repository. A support agent
handles a refund ticket with three tools: lookup_order and send_customer_email run on their own,
while every refund_payment call waits for a person to approve it in Telegram. The run log records who
approved. For a single destructive action in an ordinary application — deleting a project, closing an
account — see samples/dotnet/inproc/step-up: there the backend also checks that the person who
confirmed is the owner of the account (expected_identities).
For a harness whose tools you do not write, samples/claude-code shows the same approval plugged
into Claude Code through its hooks — see Claude Code mod.
What does not exist today
What is missing is the generic layer for agents whose tools the integrator does not write — agent
platforms and harnesses where an approval should plug in without code of your own:
- a vocabulary of actions — a shared language for “what are we confirming”, rather than a
free-form set of action types per integrator;
- an integration surface for agent platforms and tool protocols, such as an approval step a
tool server declares by itself;
- approval policies around confirmations: what always requires a human, what only above a
threshold, what can be granted once and reused.
Where its limits are
The scenario inherits the boundary of the rest of Veriqa: confirmation rests on the person’s
trusted channel and on the API of that channel’s platform.
This is a strong and convenient step for scenarios where such trust is appropriate, but not
cryptographic proof of intent. Taking part in the scheme does not turn a messenger into a
hardware security module. Where the risk model demands more, use stronger measures.