Skip to content

Agent approval demo

An AI agent requests a campaign creation through the Vauchflow MCP server using a scoped vf_ak_* key. Instead of executing immediately, the server responds with 202 Accepted and a pending_approval result. An operator reviews the request on the dashboard, approves it, and the campaign is created — at which point the agent.action.executed webhook fires and the full audit trail is permanently recorded.

This is the governance differentiator: the agent never has direct write power. High-risk actions are held for a human operator by default, with a complete, tamper-evident record of who requested what and who authorized it.

  • A Vauchflow tenant on Starter or above. Agent keys (vf_ak_*) are gated on the Starter plan; Free-plan tenants do not have access to the agent key endpoint.
  • A secret key (vf_sk_*) to issue the agent key. You can find or create one in the dashboard under Settings → API Keys.
  • An MCP-capable client. This walkthrough uses Claude Desktop as the worked example; any MCP 2024-11-05-compatible client works.
  • A webhook endpoint to receive agent.action.executed — or you can poll the approval ID directly. The demo shows both approaches.

Use your secret key to create a short-lived agent key scoped to exactly the operations this agent needs. For campaign creation plus read-back, grant CAMPAIGN_WRITE and CAMPAIGN_READ.

Terminal window
curl -X POST https://api.vauchflow.com/v1/api-keys/agent \
-H "X-API-Key: vf_sk_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Claude Desktop — campaign agent",
"scopes": ["CAMPAIGN_WRITE", "CAMPAIGN_READ"],
"ttlDays": 7
}'

Response:

{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Claude Desktop demo",
"rawKey": "vf_ak_...",
"displayKey": "vf_ak_Ab...",
"type": "AGENT",
"environment": "SANDBOX",
"status": "ACTIVE",
"scopes": ["CAMPAIGN_WRITE", "CAMPAIGN_READ"],
"expiresAt": "2026-05-25T10:00:00Z"
}

Agent keys are revocation-only — the rotation endpoint rejects them with 422. To replace a key, revoke the old one and issue a new one. See AI agents overview for the full TTL semantics.

Paste the following into your Claude Desktop configuration file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS; the equivalent path on Windows and Linux is documented in Claude Desktop’s settings):

{
"mcpServers": {
"vauchflow": {
"url": "https://api.vauchflow.com/v1/mcp",
"headers": {
"X-API-Key": "vf_ak_your-agent-key-here"
}
}
}
}

Restart Claude Desktop, then type a natural-language prompt in the chat window:

Create a Black Friday campaign with a 20% discount, max 1000 redemptions.

Claude Desktop’s tool-calling loop sees the prompt, selects the create_campaign MCP tool, and sends a JSON-RPC tools/call request to POST /v1/mcp with the structured campaign parameters. The agent key in the header scopes the call to CAMPAIGN_WRITE only — it cannot touch vouchers, customers, billing, or any other surface. See MCP server for the full tool list and required scopes.

Step 3 — The server responds 202 pending_approval

Section titled “Step 3 — The server responds 202 pending_approval”

Because create_campaign is a high-risk write and the caller is a vf_ak_* key, the request routes to the operator approval queue rather than executing immediately. The MCP tool returns a successful result — not a 4xx error — with a pending_approval status.

The full JSON-RPC wire response Claude Desktop receives:

{
"jsonrpc": "2.0",
"id": "<req-id>",
"result": {
"content": [{
"type": "text",
"text": "{\"status\":\"pending_approval\",\"approval_id\":\"d4e5f6a7-b8c9-4d01-a234-56789abcdef0\",\"action_type\":\"create_campaign\",\"expires_at\":\"2026-05-18T10:15:00Z\",\"poll_url\":\"/v1/agent-approvals/d4e5f6a7-b8c9-4d01-a234-56789abcdef0\",\"message\":\"This action requires operator approval before execution.\"}"
}],
"isError": false
}
}

Claude surfaces this to the user as something like:

Your campaign creation request was sent for operator approval. Approval ID: d4e5f6a7-b8c9-4d01-a234-56789abcdef0. I’ll let you know the outcome once an operator reviews it.

This is the designed response, not an error. The agent has successfully submitted the request into a governed queue. From this point the agent can poll GET /v1/agent-approvals/d4e5f6a7-... with its vf_ak_* key, or wait for the agent.action.executed webhook event.

For the full lifecycle state machine, the default policy table (which actions require approval versus auto-execute), and per-tenant policy override instructions, see Approval queue.

Step 4 — Operator approves on the dashboard

Section titled “Step 4 — Operator approves on the dashboard”

When a pending_approval entry is created, the dashboard sidebar shows a badge with the pending count (updated by a background poll so operators see new requests without refreshing).

Operators navigate to Activity → Agent Approvals to see the queue. Each entry displays:

  • The action type (create_campaign), the time it arrived, and the remaining time before expiry.
  • A structured request body summary — a human-readable digest of the parameters (campaign name, discount type, discount value, max redemptions). The raw JSON body is never shown in the UI or included in API responses: a PII guard prevents personally identifiable data from surfacing in operator views.
  • An Approve button and a Reject button.
+------------------------------------------------+
| Agent Approval Request |
| Action: create_campaign |
| Submitted: 2026-05-18T10:00:12Z |
| Expires: 2026-05-18T10:15:12Z (15 min) |
| |
| Request summary |
| name Black Friday |
| discountType PERCENTAGE |
| discountValue 20 |
| maxRedemptions 1000 |
| |
| Requested by agent key: vf_ak_Ab… (id: …) |
| |
| [ Approve ] [ Reject ] |
+------------------------------------------------+

(Described UI — not a screenshot)

Who can approve: dashboard users with the OWNER or ADMIN role. Users with the DEVELOPER role can view the list and entry detail but the approve and reject buttons are not rendered for them. Approval via the REST API also requires an OWNER or ADMIN role (or a secret key). See API reference — Agent approvals for the endpoint permission table.

Default expiry: 15 minutes from creation. Entries past their expiresAt are swept automatically. If the TTL elapses before an operator acts, the entry transitions to expired and the action will not execute.

Step 5 — Campaign is created; agent.action.executed fires

Section titled “Step 5 — Campaign is created; agent.action.executed fires”

Once an operator approves the entry, Vauchflow replays the stored request under a fresh tenant context. On success the approval entry transitions to executed.

Polling the approval entry (using the originating agent key):

Terminal window
curl https://api.vauchflow.com/v1/agent-approvals/d4e5f6a7-b8c9-4d01-a234-56789abcdef0 \
-H "X-API-Key: vf_ak_your-agent-key"

Response when executed:

{
"approvalId": "d4e5f6a7-b8c9-4d01-a234-56789abcdef0",
"status": "EXECUTED",
"executionResult": {
"id": "cmp_01HX...",
"name": "Black Friday",
"status": "ACTIVE"
}
}

The agent.action.executed webhook event — if you have subscribed to agent.action.* events, your endpoint receives:

{
"id": "evt_01HX...",
"type": "agent.action.executed",
"timestamp": "2026-05-18T10:08:44Z",
"data": {
"approvalId": "d4e5f6a7-b8c9-4d01-a234-56789abcdef0",
"actionType": "create_campaign",
"executionResult": {
"id": "cmp_01HX...",
"name": "Black Friday",
"status": "ACTIVE"
}
}
}

Every delivery carries the X-Vauchflow-Signature: t=<timestamp>,v1=<hmac-sha256> header — verify it against your signing secret to reject replays. See Webhooks — Verifying signatures for the reference verification snippet; do not reimplement the algorithm from scratch.

The agent_approvals row persists permanently and is queryable by operators via the REST API and dashboard. It contains:

  • The originating agent key id — identifies which vf_ak_* key submitted the request (never the raw key value).
  • The request_body_summary — a structured digest of the parameters. The raw request body is never stored in the approvals table.
  • The operator user id who approved (or rejected) the entry, with timestamp.
  • The executionResult — the created or updated entity from the replay, exactly as returned by the underlying use case.
  • Full timestamps: created_at, updated_at, expires_at, plus the operator action timestamp.

If the agent sent the optional Vauchflow-Agent-Context header, the following fields are also recorded and queryable:

Audit fieldSource
agentThe agent= key — e.g. claude-desktop
clientThe client= key — e.g. vauchflow-mcp@0.1.0
prompt_fpAn 8-character hex fingerprint of the originating prompt — computed client-side; the raw prompt is never transmitted or stored
convThe conv= conversation id — links multi-turn actions to a single session

Example header as sent by Claude Desktop:

Vauchflow-Agent-Context: agent=claude-desktop;client=vauchflow-mcp@0.1.0;prompt_fp=8a3f1b2c;conv=conv_01H8XYZ

The full lifecycle is also visible via the webhook event stream in chronological order:

agent.action.pending_approvalagent.action.approvedagent.action.executed

For the rejection and expiry paths the terminal events are agent.action.rejected and agent.action.expired respectively. Subscribe to all five event types on a single webhook endpoint to build a complete event log without polling. See Webhooks — Event types for the full event table.

What “production-safe” actually means here

Section titled “What “production-safe” actually means here”

The governance properties that make this safe for enterprise use:

  • The agent never has direct write power. A vf_ak_* key is scoped to exactly the operations you grant, expires automatically (1–90 days, default 7), and is revocation-only. There is no rotation path that extends its lifetime — revoke and reissue.
  • Human-in-the-loop is default-on for high-risk writes. Campaign creation, bulk voucher issuance above the threshold, and webhook configuration route to the approval queue by default. Per-tenant policy overrides exist, but the defaults are conservative. An operator must take an explicit action to bypass the queue for any of these operations.
  • The audit trail is comprehensive and independently auditable. Every approval entry records the scoped key id, the request digest, the operator id and timestamp, the execution outcome, and the agent context fingerprint. Compliance and security teams have the data they need — the agent identity, the conversation trace, and the human authorization record — without procuring a separate audit add-on or parsing application logs.
  • AI agents overview — decision matrix, key scopes, and integration surfaces.
  • Approval queue — full policy table, lifecycle states, and operator reference.
  • MCP server — complete tool list, dry-run pattern, and JSON-RPC error codes.