Skip to content

Use case

Agent Approvals Through Webhooks ​

Pause an agent before it runs a risky tool, get a signed webhook, and approve or deny from your own backend with the client API.

  • Stack Console + Console SDK
  • Pattern Agents · Governance
  • Read ~6 min

Overview ​

Agents that change things, such as running commands or editing records, should ask first. Console can pause a run, wait for an answer, and notify your system by webhook. Your backend decides, using your own policy, and answers through the client API.

When to reach for this use case

Use it when approvals must be decided by software or by your own review tool, not only by a person clicking in the Console UI.

Run the example ​

Runnable code for this pattern is public on GitHub:

Architecture ​

The agent has Ask before tools run enabled (and optionally Let the agent ask the user questions) and is published. A background run pauses with status requires_action.

Console sends an action.required webhook with ids only. The tool name and arguments come from GET /agents/sessions/{id} under pending_actions.

Your receiver verifies the signature, deduplicates on webhook-id, acknowledges quickly, decides, and calls POST /api/client/v1/agents/sessions/{conversationId}/actions/{actionId}.

1. Verify The Webhook ​

typescript
import express from "express";
import { verifyWebhook } from "@cognipeer/console-sdk/webhooks";

const app = express();
// express.raw, not express.json: the signature covers the exact bytes of the body.
app.post("/hooks/cognipeer", express.raw({ type: "application/json", limit: "1mb" }), (req, res) => {
  const ok = verifyWebhook({
    rawBody: req.body as Buffer,
    signatureHeader: req.get("x-cognipeer-signature"), // "t=<unix>,v1=<hex>"
    secret: process.env.COGNIPEER_WEBHOOK_SECRET!,
  });
  if (!ok) return res.sendStatus(400);
  res.sendStatus(204); // acknowledge first, work later
  // parse the event, dedupe on webhook-id, handle action.required
});

The example src/server.ts shows the complete handler, including dedupe and error handling.

2. Decide And Answer ​

Put your policy in one function. The demo denies a tool call whose arguments match a deny list, allows everything else, and answers a question with its first option. The answer is { "decision": "allow" }, or a deny with a comment the agent reads.

An action can be answered once. A second answer returns 409 action_not_pending, and after the timeout 410 action_expired. Other documented codes include action_cancelled, action_forbidden, invalid_resolution, action_not_found and session_not_found.

3. Start A Run That Needs Approval ​

The example script starts a background run (background: true) and watches it until it finishes. The agent must allow background execution, otherwise the API answers 400 agent_background_disabled.

Operational Notes ​

  • Console refuses webhook URLs that resolve to private addresses, so use a public URL or a tunnel during development.
  • If your receiver is down, Console retries over about two days, and the request stays open until the agent's wait time runs out.
  • Page tools (client_tool) can only be answered by the person's browser, not by a server receiver. See Embedded Portal With Page Tools.
  • Keep the dedupe set in a durable store in production.

Result ​

Risky tool calls are held until your own policy, or your own review system, says yes.

See every pattern on the use case index, or browse all Cognipeer libraries.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub