Microservices
Protect downstream microservices from ungoverned AI agent calls with AGP. Gateway pattern, sidecar pattern, and direct envelope verification — language and framework agnostic.
An AI agent calling your microservice is different from a human calling your microservice. The agent might be acting on a forged instruction, outside its delegated scope, or after its authority was revoked. AGP gives every service call a provenance — a signed, policy-cleared action envelope that proves the call was authorized before it was made.
AI agent
│
│ 1. Registers AGP task + gets action envelope
│ (capability check, policy evaluation, human approval if needed)
│
▼
AGP server → issues signed action envelope → agent
│
│ 2. Agent calls your microservice, presents action envelope
│
▼
API gateway / service
├─ Verifies envelope is valid, unrevoked, scoped to this service
├─ Rejects if missing, expired, revoked, or wrong scope
└─ Forwards to microservice if valid
↓
Microservice executes → result logged to AGP ledgerAGP is language and framework agnostic. The pattern works the same whether your services are Python FastAPI, Node.js Express, Go, Java Spring, or anything else. All that's required is that the service — or something in front of it — can make an HTTP call to the AGP verification endpoint.
Runtime enforcement — POST /agp/validate
Whichever pattern you choose below, the canonical check the downstream service should run is
POST /agp/validate. It re-runs the §10 gate at execution time — envelope existence,
agent identity, task status, capability not expired, capability not revoked, tool permitted,
policy verdict — and returns 200 PERMIT or 403 DENY with a per-check
breakdown. One call replaces the bespoke GET-and-inspect logic shown in the examples.
# Single call covers all §10 checks at runtime
curl -X POST https://your-agp-server/agp/validate \
-H "Authorization: Bearer $AGP_TOKEN" \
-H "Content-Type: application/json" \
-d '{"action_id":"env_...", "agent_id":"my-agent", "tool_id":"customer-service"}'
# 200 OK → {"valid": true, "enforcement_decision": "PERMIT", "checks": [...]}
# 403 → {"valid": false, "enforcement_decision": "DENY",
# "failed_check": "capability_not_revoked", "reason": "...", "checks": [...]}The patterns below pre-date the dedicated endpoint and remain valid if you need to inspect
additional envelope fields (e.g. compare claimed amount against authorised amount). For typical
enforcement, prefer /agp/validate.
Three patterns
Choose based on how much control you have over the infrastructure layer:
Gateway
Verify at the API gateway before any request reaches your services. One integration point covers all services.
Middleware
Verification middleware inside each service. More granular — each service checks its own required scope.
Direct
Service calls the AGP verification endpoint directly in its handler. No infrastructure changes needed.
Pattern 1 — Gateway verification
The agent attaches the AGP action envelope ID as a request header. The gateway calls the AGP verification endpoint before routing. If verification fails, the request is rejected at the edge — your services never see it.
Agent side — obtain and attach the envelope
from agp import AGPClient
agent = AGPClient("https://your-agp-server", client_id="my-agent", client_secret="...")
# Run the AGP pipeline to obtain a signed action envelope
with agent.task_session(
principal_id="my-agent",
requested_outcome="Fetch customer records for Q3 analysis",
risk_tier="medium",
) as session:
session.bind(sponsoring_entity="analytics-team",
accountable_owner="lead@example.com", jurisdiction="EU")
session.issue_capability(subject_agent="my-agent", issuer="analytics-team",
principal_id="my-agent",
permitted_actions=["customer_records.read"])
session.decide(agent_id="my-agent", selected_action="customer_records.read",
rationale="Q3 analysis, read-only, no PII export",
uncertainty_score=0.05)
receipt = session.execute(agent_id="my-agent", tool_id="customer-service",
operation={"query": "q3_cohort"},
capability_token_ref=session.capability_token.capability_id)
# Call the microservice — attach the action ID as a header
import httpx
response = httpx.get(
"https://api.internal/customer-service/records",
params={"query": "q3_cohort"},
headers={"X-AGP-Action-ID": receipt.action_id},
)Gateway side — verify before routing
Example using nginx with a Lua auth subrequest, or any gateway that supports external auth (Kong, AWS API Gateway, Envoy, Traefik):
-- nginx + lua-resty-http: external auth subrequest
access_by_lua_block {
local http = require("resty.http")
local action_id = ngx.req.get_headers()["X-AGP-Action-ID"]
if not action_id then
ngx.status = 401
ngx.say('{"error":"X-AGP-Action-ID header required"}')
ngx.exit(401)
end
local httpc = http.new()
local res = httpc:request_uri(
"https://your-agp-server/agp/action-envelopes/" .. action_id,
{ method = "GET",
headers = { ["Authorization"] = "Bearer " .. agp_token } }
)
if res.status ~= 200 then
ngx.status = 403
ngx.say('{"error":"AGP envelope invalid or revoked"}')
ngx.exit(403)
end
}One integration, all services.
Verify at the gateway and every service behind it is automatically protected — without touching any service code. Add a new service and it's covered immediately.
Pattern 2 — Per-service middleware
Verification middleware in each service gives you per-route scope enforcement. Different endpoints can require different AGP scopes — a write endpoint requires a write-scoped envelope, a read endpoint accepts a read-scoped one.
# FastAPI dependency — verifies the AGP action envelope on each request
import httpx
from fastapi import Depends, Header, HTTPException
AGP_SERVER = "https://your-agp-server"
AGP_TOKEN = "..." # service's own AGP bearer token
async def require_agp_envelope(
action_id: str = Header(alias="X-AGP-Action-ID"),
required_action: str = "*",
):
"""Verify the AGP action envelope and confirm the action is in scope."""
async with httpx.AsyncClient() as client:
r = await client.get(
f"{AGP_SERVER}/agp/action-envelopes/{action_id}",
headers={"Authorization": f"Bearer {AGP_TOKEN}"},
)
if r.status_code != 200:
raise HTTPException(403, detail="AGP envelope invalid or revoked")
envelope = r.json()
# Verify the action matches what this endpoint expects
if required_action != "*" and envelope["tool_id"] != required_action:
raise HTTPException(403, detail=f"Envelope scoped to '{envelope['tool_id']}', not '{required_action}'")
return envelope
# Apply per-route — different endpoints can require different scopes
from fastapi import FastAPI
app = FastAPI()
@app.get("/records")
async def get_records(
envelope = Depends(lambda: require_agp_envelope(required_action="customer_records.read"))
):
# Only reached if envelope is valid and scoped to customer_records.read
...
@app.post("/records")
async def create_record(
envelope = Depends(lambda: require_agp_envelope(required_action="customer_records.write"))
):
# Requires a write-scoped envelope — a read envelope is rejected here
...// TypeScript / Express — AGP envelope middleware
import express, { Request, Response, NextFunction } from "express";
const AGP_SERVER = "https://your-agp-server";
function requireAGPEnvelope(requiredAction?: string) {
return async (req: Request, res: Response, next: NextFunction) => {
const actionId = req.headers["x-agp-action-id"] as string;
if (!actionId) {
return res.status(401).json({ error: "X-AGP-Action-ID header required" });
}
const r = await fetch(`${AGP_SERVER}/agp/action-envelopes/${actionId}`, {
headers: { Authorization: `Bearer ${process.env.AGP_TOKEN}` },
});
if (!r.ok) {
return res.status(403).json({ error: "AGP envelope invalid or revoked" });
}
const envelope = await r.json();
if (requiredAction && envelope.tool_id !== requiredAction) {
return res.status(403).json({ error: `Envelope scoped to '${envelope.tool_id}'` });
}
res.locals.agpEnvelope = envelope;
next();
};
}
// Apply per-route
const router = express.Router();
router.get("/records", requireAGPEnvelope("customer_records.read"), getRecords);
router.post("/records", requireAGPEnvelope("customer_records.write"), createRecord);Pattern 3 — Direct verification
The simplest integration: the service handler calls the AGP verification endpoint directly, with no middleware or gateway changes. Good for adding governance to a single high-risk endpoint without broader infrastructure changes.
# Minimal: verify inline in the handler
import httpx
def approve_payment(vendor: str, amount_usd: float, agp_action_id: str):
# Verify the action envelope before doing anything
r = httpx.get(
f"https://your-agp-server/agp/action-envelopes/{agp_action_id}",
headers={"Authorization": f"Bearer {AGP_TOKEN}"},
)
if r.status_code != 200:
raise PermissionError("No valid AGP action envelope")
envelope = r.json()
if envelope["tool_id"] != "approve_payment":
raise PermissionError("Envelope not scoped for approve_payment")
if envelope.get("operation", {}).get("amount_usd", 0) < amount_usd:
raise PermissionError("Amount exceeds what was authorized in the envelope")
# All checks pass — proceed
_do_approve_payment(vendor, amount_usd)The AGP server as a microservice
The AGP reference server is itself a microservice — deploy it alongside your existing services. It has no external dependencies beyond a database for storing governance objects. The Python SDK and TypeScript SDK handle authentication and connection management for your agent-side code.
# docker-compose.yml — AGP alongside your services
services:
agp-server:
image: python:3.12-slim
command: uvicorn main:app --host 0.0.0.0 --port 8099
working_dir: /app
volumes: [./server:/app]
environment:
AGP_AUTH_SECRET: ${AGP_AUTH_SECRET}
AGP_CLIENT_ID: ${AGP_CLIENT_ID}
AGP_CLIENT_SECRET: ${AGP_CLIENT_SECRET}
ports: ["8099:8099"]
payment-service:
build: ./payment-service
environment:
AGP_SERVER_URL: http://agp-server:8099
AGP_TOKEN: ${PAYMENT_SERVICE_AGP_TOKEN}
customer-service:
build: ./customer-service
environment:
AGP_SERVER_URL: http://agp-server:8099
AGP_TOKEN: ${CUSTOMER_SERVICE_AGP_TOKEN}One AGP server governs the whole estate.
Every agent, every microservice, every action envelope goes through the same AGP server. One place to check the audit ledger. One place to revoke capability tokens. One policy engine to update when your rules change.
What you get
Every service call has provenance
The action envelope proves the call was policy-cleared and capability-checked before the agent made it. Services don't have to trust the agent — they verify the envelope. If it's valid, the action was authorized.
Scope enforcement at the service boundary
An agent's action envelope is scoped to a specific tool_id. A read-scoped envelope is rejected by a write endpoint — even if the agent presents a valid token. Scope is checked at verification time, not at issuance time.
Distributed audit in one place
Every service call that passes through the AGP gate is logged to the audit ledger. No log aggregation across services — one ledger replay shows the full sequence of agent actions across your entire microservice estate.
Revocation propagates instantly
Revoke a capability token and any subsequent service call presenting an envelope from that token is immediately rejected — at the gateway or in the middleware. No cache to flush, no services to notify individually.
Next steps
Universal Commerce Protocol (UCP)
Add AGP governance to Universal Commerce Protocol workflows. Govern which agents can initiate checkouts, enforce spend limits, and create an immutable audit trail for every agentic commerce transaction.
Explainers
Explainer guides for AGP — the open agent governance protocol for autonomous AI agents. Clear resources on AI agent governance for executives, architects, and developers.