Human Approval for Amazon Bedrock AgentCore Agents
Bedrock AgentCore agents can call real AWS APIs on their own schedule — this shows how to put an actual human decision, not just an AWS trace log, between an AgentCore tool and the action it takes.
AgentCore's tracing isn't the same as someone saying yes
AgentCore Runtime gives you session isolation, a gateway for tool invocation, and a governance layer for rolling agents out incrementally. That's infrastructure for running the agent safely — it is not the same as a person looking at this specific claim, this specific payout, this specific customer and deciding it's correct before it goes out. A trace you can review after the fact tells you what happened. A gate tells you before it happens, and lets someone stop it.
The pattern below wraps a single AgentCore tool function so the side effect it triggers cannot run without an approved decision coming back from Impri.
The gate: three calls around your AgentCore tool
This is a claims-payout tool exposed to the agent. The tool itself is the chokepoint — the agent has no other path to issue_payout.
import os, time, requests
IMPRI = "https://api.impri.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['IMPRI_API_KEY']}"}
def close_insurance_claim(claim_id: str, payout_usd: float, summary: str) -> dict:
"""Tool exposed to the AgentCore agent via the Gateway."""
action = requests.post(f"{IMPRI}/v1/actions", headers=HEADERS, json={
"kind": "claim.payout",
"title": f"Approve payout ${payout_usd:,.2f} for claim {claim_id}",
"preview": {"format": "markdown", "body": summary},
"idempotent": False,
"undo": f"Reverse payout via claims-admin console for {claim_id}",
"expires_in": 14400,
}).json()
while True:
result = requests.get(f"{IMPRI}/v1/actions/{action['id']}", headers=HEADERS).json()
if result["status"] != "pending":
break
time.sleep(10)
if result["status"] != "approved":
return {"status": result["status"], "executed": False}
payout_id = issue_payout(claim_id, payout_usd) # your actual AWS-side call
requests.post(f"{IMPRI}/v1/actions/{action['id']}/result", headers=HEADERS,
json={"status": "executed", "payload": {"payout_id": payout_id}})
return {"status": "approved", "executed": True, "payout_id": payout_id}issue_payout — wherever it lives, Lambda, Step Functions, a direct SDK call — only runs after result["status"] == "approved". There's no branch in this function that reaches it otherwise.
Don't block a Runtime session past its budget
Bedrock AgentCore Runtime sessions run for a bounded duration, and a claim payout might sit unreviewed for hours, not seconds. If your approval SLA is longer than a session can afford to hold open, don't poll synchronously inside the tool call. Instead:
- The tool pushes the Impri action and returns immediately with
{"status": "pending", "action_id": ...}. - A separate process — an EventBridge-scheduled Lambda, or a small worker outside AgentCore — polls
GET /v1/actions/:idon its own cadence. - Once
status != "pending", that worker callsissue_payoutand posts the result, then notifies the requester (Slack, email, whatever your stack already uses) that the claim was closed.
The AgentCore session ends; the approval record and the eventual execution don't depend on it staying open.
Where responsibilities split
| Concern | Owned by |
|---|---|
| Running the agent, invoking tools, session isolation | AgentCore Runtime |
| Enterprise-wide tracing and rollout governance | AgentCore's governance layer |
| Whether this payout is correct | A human, via the Impri inbox card |
| Storing the decision and the audit trail of who approved what | Impri |
| Actually moving the money | Your issue_payout code |
Impri doesn't know what a valid insurance claim looks like, and it doesn't touch AWS. It stores the proposed action, shows it to a person, and hands back a decision your code has to act on.
What this doesn't protect against
If the agent (or something impersonating it) has the AWS credentials to call the payout API directly, wrapping one tool function doesn't stop that path — Impri is only a gate on the code that calls it. Keep the payout credential scoped to the wrapped tool, not handed to the agent's general-purpose execution environment. And treat any claim summary sourced from an external form or email as data, not instructions, before it lands in preview.body — AgentCore doesn't sanitize that for you either.
Next: the quickstart covers getting an API key for cloud or self-host, the Python SDK wraps these three calls if you'd rather not hand-roll the polling loop, and audit log covers what's retained for compliance review after a payout is approved.