Tool Approvals
Use Studio to approve protected tool calls.
Studio can handle tool approvals for agents that use requireApproval(...) in an onToolCall hook.
1. Mark a Tool as Protected
import { AgentBuilder, createHook, createTool, requireApproval } from "@anvia/core";
import { OpenAIClient } from "@anvia/openai";
import { Studio } from "@anvia/studio";
import { z } from "zod";
const refundOrder = createTool({
name: "refund_order",
description: "Issue a customer refund.",
input: z.object({
orderId: z.string(),
amount: z.number().positive(),
reason: z.string(),
}),
output: z.object({
refundId: z.string(),
status: z.enum(["issued"]),
}),
async execute({ orderId }) {
return {
refundId: `rf_${orderId.toLowerCase()}`,
status: "issued" as const,
};
},
});
const approvalHook = createHook({
onToolCall({ toolName }) {
if (toolName === "refund_order") {
return requireApproval({
reason: "Refunds require staff approval.",
timeoutMs: 120_000,
rejectMessage: "Refund rejected in Anvia Studio.",
timeoutMessage: "Refund approval timed out.",
});
}
return { type: "continue" };
},
});
const client = new OpenAIClient({ apiKey });
const model = client.completionModel("gpt-5");
const agent = new AgentBuilder("support-operations", model)
.instructions("Look up order state before refunding. Keep the final answer short.")
.tool(refundOrder)
.hook(approvalHook)
.defaultMaxTurns(3)
.build();
new Studio([agent]).start({ port: 4021 });2. Start a Streaming Run
Approvals are most useful with streaming because the pending approval event can reach the UI while the run waits.
curl -N -X POST http://localhost:4021/agents/support-operations/runs \
-H 'content-type: application/json' \
-d '{"message":"Refund order ORD-1001 for 25 dollars.","stream":true}'When the model calls refund_order, Studio emits a tool_approval_request event and keeps the tool paused.
3. Approve or Reject
curl http://localhost:4021/approvals?status=pendingcurl -X POST http://localhost:4021/approvals/approval_123/decision \
-H 'content-type: application/json' \
-d '{"approved":true,"reason":"Support lead approved."}'Reject with approved: false.
curl -X POST http://localhost:4021/approvals/approval_123/decision \
-H 'content-type: application/json' \
-d '{"approved":false,"reason":"Amount needs finance review."}'Approval Flow
- The model requests a tool call.
- Your hook returns
requireApproval(...). - Studio creates a pending approval.
- A human approves or rejects it in the UI or API.
- Anvia either runs the tool or sends the rejection message back to the model.
Use tool approval for side effects such as refunds, account changes, deletes, external messages, and workflow transitions.
