Skip to main content
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:
  1. 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.
  2. 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.
  3. 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.

Available today: an agent whose tools you know

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:
  1. 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.
  2. 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.
  3. 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.