Skip to Content
Control LayerrequestApproval

requestApproval

Problem

Some tool calls are too high-stakes to let an AI agent execute without a human reviewing them first — deleting data, sending emails, making payments, publishing content. Building a human-in-the-loop pause into an agent loop manually is messy and hard to reason about.


Solution

requestApproval is a clean async gate you add before any tool executes. You provide a handler function. The handler decides — synchronously or asynchronously — whether to approve the call. If it returns false, the tool is blocked. If it returns true, execution continues.


Feature & Use-Case

Use requestApproval when:

  • Certain tools should require human sign-off before running (payment, deletion, messaging)
  • You are building a supervised agent where a UI or webhook confirms each tool call
  • You want a conditional approval gate — some tools always approved, others need review based on input values
  • You are building for a compliance-sensitive environment and need audit trail hooks

Import

import { requestApproval } from "llm-layer-engine";

Function Signature

async function requestApproval( config: ApprovalConfig, ctx: ApprovalContext ): Promise<boolean>

Types

type ApprovalContext = { toolName: string; userId?: string; input?: unknown; metadata?: Record<string, unknown>; }; interface ApprovalConfig { handler?: ApprovalHandler; } type ApprovalHandler = (ctx: ApprovalContext) => boolean | Promise<boolean>;

Parameters

ParameterTypeRequiredDescription
config.handlerApprovalHandlerYour approval logic. If omitted, all calls are auto-approved
ctx.toolNamestringName of the tool awaiting approval
ctx.userIdstringUser who triggered the agent
ctx.inputunknownThe tool’s input data — for the approver to inspect
ctx.metadataobjectExtra context (e.g. session ID, request ID)

Example — Block High-Risk Tools

import { requestApproval } from "llm-layer-engine"; const approvalConfig = { handler: async ({ toolName, input }) => { const requiresApproval = ["send_email", "delete_record", "charge_card"]; if (requiresApproval.includes(toolName)) { // In production: send to approval queue, webhook, or UI // Here: auto-deny for example console.log(`Approval required for ${toolName}`, input); return false; } return true; // Safe tools auto-approved }, }; const approved = await requestApproval(approvalConfig, { toolName: "send_email", userId: "user_789", input: { to: "client@example.com", subject: "Invoice" }, }); if (!approved) { console.log("Tool call blocked — awaiting human approval"); }

Example — Webhook-Based Approval (Async)

import { requestApproval } from "llm-layer-engine"; import axios from "axios"; const approvalConfig = { handler: async ({ toolName, input, userId }) => { // Send approval request to your backend const { data } = await axios.post("https://your-app.com/approvals", { toolName, input, userId, }); return data.approved === true; }, };

Your handler can be fully async — await a webhook, a database query, or a user response from your UI.


Example — No Handler (Auto-Approve All)

const approved = await requestApproval({}, { toolName: "get_weather", }); console.log(approved); // → true

No handler means everything is auto-approved — safe default for low-risk tools.


Conclusion

requestApproval is your human-in-the-loop gate. With no handler it is invisible — all calls pass through. Add a handler the moment any tool needs review before execution. It works well alongside checkPermission: permissions handle role-based blocking, approval handles explicit human confirmation for sensitive actions.

Last updated on