Decide per input. Pause before the side effect. Resume when approved.

Mark a tool with requiresApproval and the run stops immediately before it executes, returning the tool name, input, and reason. Your server decides who may approve, then resumes the run from a JSON-safe continuation.

From tool call to signed-off action.

The agent pauses. Your code owns who approves and how decisions are stored.

  1. 01

    Guard the tool

    Add requiresApproval to createTool(). Return false to run automatically, or a reason to pause.

  2. 02

    Run pauses

    generate() returns an interaction with the tool name, input, and reason, plus a continuation.

  3. 03

    Review on your server

    Store the continuation, authenticate the reviewer, and claim the interaction atomically.

  4. 04

    Resume

    agent.resume() continues as a linked phase. The tool executes, or the denial reaches the model.

Approval as part of the run, not a bolt-on.

Per-input decisions

The approval callback sees the input, so a $5 refund can run while a $500 one waits.

Structured questions

createQuestionTool() lets the agent ask a person for a typed answer, using the same pause and resume.

Linked phases

Each resume gets a new run ID with a link back to the original, so traces stay connected.

Restart-safe with durable

With @anvia/durable, pending approvals survive restarts. Restarting never approves work. Experimental.

Pause a refund until someone approves it.

The reason travels with the interaction, so the reviewer sees exactly what is being asked.

refund.ts
1const issueRefund = createTool({2  name: 'issue_refund',3  description: 'Issue a customer refund in USD.',4  inputSchema: z.object({ orderId: z.string(), amount: z.number().positive() }),5  requiresApproval: ({ orderId, amount }) => ({6    reason: `Review $${amount} refund for ${orderId}.`,7  }),8  async execute({ orderId, amount }) {9    return refundService.issue({ orderId, amount })10  },11})1213let result = await agent.generate({ prompt: 'Refund $25 for order A-100.' })14if (result.type === 'interaction' && result.interaction.type === 'tool-approval') {15  const { toolName, input } = result.interaction16  const decision = await approvalService.decide({ toolName, input })17  result = await agent.resume(result.continuation, {18    type: 'tool-approval',19    approved: decision.approved,20    reason: decision.reason,21  })22}23if (result.type === 'response') console.log(result.output)

Approval is not authentication. Core does not store continuations or guarantee exactly-once execution, so store them on your server, reject duplicate decisions, and keep side effects idempotent.

Build it.

Start from the guide, run the example, or look at what else Anvia handles.

Anvia