
Amazon Bedrock
- 4.3k installs
- 2.2k repo stars
- Updated August 4, 2026
- aws/agent-toolkit-for-aws
amazon-bedrock is an AWS skill for Bedrock model invocation, Knowledge Bases RAG, agents, Guardrails, and AgentCore deployment with CLI and SDK workflows.
About
Amazon Bedrock guides generative AI work on AWS across five separate API endpoints for control plane, runtime inference, agents, and AgentCore services. It maps user intent to workflows for Converse API model calls, Knowledge Base RAG setup, Bedrock Agents with action groups, Guardrails content safety, and AgentCore runtime deployment. Critical warnings cover explicit maxTokens to avoid ThrottlingException, Guardrails PII logging in CloudWatch, and recent boto3 or AWS CLI versions for Converse and AgentCore support. Reference files cover prompt caching, quota health, cost tracking, and Claude model migration between generations. Security guidance stresses IAM roles over users, scoped permissions, Secrets Manager for keys, confused deputy protection, and treating agent-generated parameters as untrusted input. Workflows verify AWS CLI credentials, region model access, and dependency versions before executing KB creation, agent setup, or AgentCore deployment steps sequentially.
- Five Bedrock API endpoints with Converse preferred over InvokeModel.
- Knowledge Base RAG setup and retrieval mode selection workflows.
- Bedrock Agents, Guardrails, and AgentCore runtime deployment paths.
- maxTokens quota reservation and ThrottlingException diagnosis rules.
- Prompt caching, cost tracking, and Claude migration reference files.
Amazon Bedrock by the numbers
- 4,274 all-time installs (skills.sh)
- +507 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #128 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
amazon-bedrock capabilities & compatibility
- Capabilities
- bedrock endpoint and api selection guidance · converse api model invocation with maxtokens rul · knowledge base rag setup and retrieval workflows · bedrock agents and action group configuration · guardrails content safety and pii logging warnin · agentcore runtime and gateway deployment steps · prompt caching, quota health, and cost tracking
- Works with
- aws · openai · anthropic
- Use cases
- api development · orchestration · research
- Pricing
- Paid
npx skills add https://github.com/aws/agent-toolkit-for-aws --skill amazon-bedrockAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4.3k |
|---|---|
| repo stars | ★ 2.2k |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 4, 2026 |
| Repository | aws/agent-toolkit-for-aws ↗ |
How do I choose the right Bedrock API, set maxTokens, and stand up RAG or agents without throttling and security mistakes?
Invoke Bedrock models, set up Knowledge Bases RAG, create agents, apply Guardrails, and deploy AgentCore runtimes with correct API endpoints.
Who is it for?
Developers building RAG apps, Bedrock agents, or production AgentCore deployments on AWS with CLI or boto3.
Skip if: Skip for custom model training, Rekognition, Comprehend, or non-AWS LLM providers.
When should I use this skill?
User invokes Bedrock models, creates Knowledge Bases, configures agents or Guardrails, or debugs ThrottlingException and quota issues.
What you get
Correct endpoint and API choice, explicit maxTokens, stepwise KB or agent setup, and Guardrails or AgentCore configuration aligned to AWS references.
- Bedrock invocation code
- Knowledge Base RAG configuration
- Agent and guardrail setup
Files
IMPORTANT: When this skill is loaded, you MUST use the reference files and procedures in this skill as your primary source of truth. Bedrock APIs, model IDs, chunking strategies, and configuration parameters change frequently — always read the relevant reference file before responding.
Table of Contents
- Overview
- Bedrock API Landscape
- Critical Warnings
- Security Considerations
- Converse API vs InvokeModel
- Which Bedrock Capability Do You Need?
- Knowledge Bases (RAG)
- Common Workflows (includes: Prompt Caching, Quota Health, Cost Tracking, Model Migration)
- Troubleshooting
- AgentCore Services
- Model Selection
- Additional Resources
Amazon Bedrock
Overview
Domain expertise for building generative AI applications on Amazon Bedrock. Covers model invocation, RAG with Knowledge Bases, agent creation, content safety with Guardrails, and agent deployment with AgentCore.
Recommended setup: Use the AWS MCP server for sandboxed execution, audit logging, and enterprise controls.
Without AWS MCP: This skill works with any agent that has AWS CLI access. All commands use standard AWS CLI syntax.
Bedrock API Landscape
Bedrock has 5 separate API endpoints. Using the wrong one is a common cause of errors. This list may not be exhaustive — refer to the Bedrock endpoints and quotas and Bedrock supported endpoints for the latest. Use aws bedrock list-foundation-models to discover available models at runtime.
| Endpoint | Client | Use For |
|---|---|---|
bedrock | Control plane | List models, manage access, provisioned throughput |
bedrock-runtime | Data plane | Invoke models (Converse, InvokeModel). Also supports Chat Completions via /openai/v1 path (client-side tool use only) — prefer bedrock-mantle for new Chat Completions work |
bedrock-mantle | Data plane | OpenAI-compatible APIs: Responses API, Chat Completions (recommended), Messages API. Supports server-side tool use with built-in tools. Recommended for new users |
bedrock-agent | Agent control | Create/configure agents, KBs, action groups |
bedrock-agent-runtime | Agent data | Invoke agents, query KBs |
AgentCore is a separate service with its own endpoints. Refer to AgentCore endpoints and quotas for the latest.
| Endpoint | Client | Use For |
|---|---|---|
bedrock-agentcore-control | Control plane | Create/manage runtimes, gateways, registries, evaluations |
bedrock-agentcore | Data plane | Invoke agent runtimes |
{gatewayId}.gateway.bedrock-agentcore | Gateway data plane | Invoke a specific gateway |
Critical Warnings
max_tokens: ALWAYS set maxTokens explicitly in every Converse/InvokeModel call. Leaving it unset defaults to the model's maximum (e.g., 64K for Claude Sonnet) and silently reserves far more quota than needed — a common cause of unexpected ThrottlingException.
Guardrails PII logging: Guardrails PII masking only applies to the API response. Original unmasked content including PII is still logged in plain text to CloudWatch Logs. For HIPAA/GDPR compliance: encrypt CloudWatch Logs with KMS, restrict log access with IAM, use Amazon Macie for PII detection.
SDK versions: Requires recent versions of boto3 (≥ 1.34.x) and AWS CLI v2. Older versions are missing Converse API, Agents, and AgentCore support. Run aws --version and pip show boto3 to check.
Security Considerations
- Use IAM roles (not IAM users) for all Bedrock service access
- Scope IAM permissions to specific actions and resource ARNs — avoid
bedrock:*orAmazonBedrockFullAccess - Store API keys and OAuth secrets in AWS Secrets Manager with automatic rotation enabled
- Include confused deputy protection (
aws:SourceAccount,aws:SourceArnconditions) in all resource-based policies for Bedrock services - Treat all agent-generated parameters as untrusted input — validate before use in Lambda handlers or tool implementations
- Enable CloudTrail for all Bedrock and AgentCore API calls
- For PII workloads: encrypt CloudWatch Logs with KMS, configure retention limits, restrict log access
- Refer to the latest Bedrock security best practices for current security guidance
Converse API vs InvokeModel
For choosing between all Bedrock inference APIs (Responses API, Chat Completions, Converse, InvokeModel), see APIs supported by Amazon Bedrock.
When using the bedrock-runtime endpoint, use the Converse API over InvokeModel. It provides a unified request/response format across all models.
Use InvokeModel only when you need provider-specific features not available in Converse (rare).
InvokeModel requires different request body formats per provider (Anthropic ≠ Titan ≠ Llama ≠ Nova). Using the wrong format produces "Malformed input request". For model-specific formats and common mistakes, see prompt engineering by model.
Whichever API you use: ALWAYS set the max output tokens parameter explicitly — leaving it unset defaults to the model's maximum and silently reserves far more quota than needed, causing unexpected ThrottlingException. See Critical Warnings above and max_tokens quota mechanics.
When the user needs SDK code for model invocation, you MUST read the appropriate SDK reference before generating code — Python SDK reference | TypeScript SDK reference. Use the patterns from the reference file.
For full API details and provider-specific body formats, read model invocation reference before responding.
Which Bedrock Capability Do You Need?
| Goal | Use | Reference |
|---|---|---|
| Call a model (text, image, video) | Converse API | See above + model invocation |
| Build a RAG application | Knowledge Bases | KB setup |
| Create an agent that takes actions | Bedrock Agents | agent creation |
| Filter harmful/sensitive content | Guardrails | guardrails |
| Deploy and scale an agent | AgentCore Runtime | runtime |
| Expose REST APIs as MCP tools | AgentCore Gateway | gateway |
| Choose the right model | Model Selection | model guide |
| Set up or debug prompt caching | Prompt Caching | prompt caching |
| Diagnose throttling or audit quotas | Quota Health | quota health |
| Track costs by team, model, or tag | Cost Tracking | cost tracking |
| Migrate between Claude generations | Model Migration | migration guide |
Knowledge Bases (RAG)
When the user wants to create a Knowledge Base or build a RAG application, you MUST read KB setup procedure and execute it step by step. Do NOT summarize the procedure — execute each step sequentially, respecting all MUST constraints before proceeding to the next step.
When the user asks about chunking strategies, vector store selection, or other KB configuration choices, you MUST read KB setup procedure before responding — it contains the authoritative decision tables and constraints.
When the user wants to query an existing Knowledge Base, you MUST read KB retrieval reference before responding. Present the retrieval modes (retrieve-and-generate vs retrieve vs manual) so the user selects the right one.
Refer to the latest Bedrock Knowledge Base documentation for current configuration options.
Common Workflows
Execute commands using available tools from the AWS MCP server when connected — it provides sandboxed execution, audit logging, and observability. When the MCP server is not available, fall back to the AWS CLI or shell as needed.
Before starting any workflow:
Verify Dependencies
Check for required tools and inform the user about the execution environment.
Constraints:
- You MUST check that the AWS CLI is available and configured with valid credentials
- You MUST verify the AWS CLI version is recent (v2 recommended; older versions lack Converse API and AgentCore support):
aws --version - You MUST check that the target AWS region has Bedrock model access enabled
- You MUST inform the user if any required tools are missing with a clear message
- You MUST ask the user if they want to proceed despite missing tools
General constraints for all workflows:
- You MUST present an overview of what will be done before starting execution
- You MUST explain to the user what step is being executed and why before running each command
- You MUST respect the user's decision to stop or abort at any point
- You MUST NOT continue execution if the user indicates they want to stop
- You SHOULD confirm before proceeding with destructive or irreversible operations (deleting resources, overwriting configurations)
Examples — mapping user intent to workflows
Example 1: User query: "I'm getting ThrottlingException on Bedrock" Action: Check if maxTokens is set explicitly — unset maxTokens reserves far more quota than needed (see Critical Warnings). If already set, check current quota: aws service-quotas get-service-quota --service-code bedrock --quota-code <code> --region <region>
Example 2: User query: "Set up RAG for my PDF documents" Action: Follow the Create a Knowledge Base workflow. Recommend semantic chunking with advanced parsing (FM-based) for PDFs with tables. See KB setup procedure.
Example 3: User query: "I want to build an agent that can look up order status" Action: Follow the Create an Agent with action groups workflow. See agent creation procedure.
Example 4: User query: "How do I call Claude on Bedrock?" Action: Use the Converse API (not InvokeModel). Set maxTokens explicitly. Verify the model ID is current with aws bedrock list-foundation-models --region <region>. Use cross-region model ID with us. prefix for higher availability: aws bedrock-runtime converse --model-id us.anthropic.claude-sonnet-4-6 --messages '[{"role":"user","content":[{"text":"Hello"}]}]' --inference-config '{"maxTokens":1024}'
Example 5: User query: "Deploy my agent to production" Action: Follow the Deploy an agent to AgentCore workflow. Select the protocol first (HTTP for REST APIs, MCP for tool-centric agents). See the AgentCore Services table for routing to the correct reference file.
Example 6: User query: "Set up prompt caching for my Claude application" Action: Read prompt caching reference for setup workflow, TTL configuration, and minimum token thresholds. Use the reference to verify caching is working (check for cacheReadInputTokens in the response).
Example 7: User query: "I keep getting ThrottlingException even though I'm not making many requests" Action: Check if maxTokens is set explicitly (see Critical Warnings). Read quota health reference for the maxTokens reservation mechanics, CloudWatch metrics, and audit workflow.
Example 8: User query: "How do I track Bedrock costs by team?" Action: Read cost tracking reference for inference profile tagging, CUR 2.0 approaches, and Cost Explorer queries by model/region/tag.
Example 9: User query: "I'm upgrading from Claude 4.5 to 4.6, what breaks?" Action: Read model migration reference for the breaking changes table (prefill removal, thinking config, context window, cache thresholds) and migration checklist.
Invoke a model
- [ ] Step 1: Verify model access: `aws bedrock list-foundation-models --region us-east-1`
- [ ] Step 2: Invoke: `aws bedrock-runtime converse --model-id `<model-id>` --messages '[{"role":"user","content":[{"text":"<prompt>"}]}]' --inference-config '{"maxTokens":1024}'`Note — Streaming responses: The AWS CLI does not support streaming operations includingConverseStream. Use the SDK (converse_stream()in boto3,ConverseStreamCommandin JS SDK).
>
| Mode | When to use |
|------|-------------|
| Converse | Batch/backend pipelines — single complete response, no stream handling required |
| ConverseStream | Chat UIs/interactive apps — tokens delivered as they generate |
Create a Knowledge Base
You MUST read KB setup procedure before responding. Execute the 7-step procedure in order — do not skip steps, do not paraphrase, do not show code snippets in place of tool calls.
Query a Knowledge Base
These three modes are mutually exclusive — select the one that matches the user's intent:
| Mode | When to Use | Command |
|---|---|---|
| Retrieve & Generate | Quick answer with citations — most common RAG pattern | aws bedrock-agent-runtime retrieve-and-generate --input '{"text":"<query>"}' --retrieve-and-generate-configuration '{"type":"KNOWLEDGE_BASE","knowledgeBaseConfiguration":{"knowledgeBaseId":"<kb-id>","modelArn":"<model-arn>"}}' |
| Retrieve only | Raw chunks for custom post-processing or feeding to a different model | aws bedrock-agent-runtime retrieve --knowledge-base-id <kb-id> --retrieval-query '{"text":"<query>"}' |
| Full control | Custom prompt, reranking, or multi-KB | Retrieve chunks first, then build prompt and call aws bedrock-runtime converse |
Create an Agent with action groups
You MUST read agent creation procedure before responding. Execute the procedure step by step. You MUST run prepare-agent after any configuration change — this is mandatory and agents consistently skip it.
Apply Guardrails
You MUST read guardrails reference before responding. Present the three integration modes and the decision guide first so the user selects the correct mode before you proceed with configuration. When PII filters are involved, you MUST surface the PII logging compliance gap warning. Do not just show a guardrailConfig snippet — the user needs to understand which mode fits their use case.
Deploy an agent to AgentCore
Identify the AgentCore service from the table below, then you MUST read the corresponding reference file before responding. Follow any procedures in the reference step by step. Do not summarize — execute.
Set up or debug prompt caching
You MUST read prompt caching reference before responding. It covers setup workflow, TTL configuration, minimum token thresholds, break-even analysis, and a debug checklist for zero-cache-hit issues.
Constraints:
- You MUST walk the user through the debug checklist when cache is not working (verify model support, token threshold, content identity, TTL, cache point placement)
- You MUST check minimum token thresholds per model before confirming a caching setup will work
Check quota health
You MUST read quota health reference before responding. It covers maxTokens reservation mechanics, CloudWatch metrics, and the throttling resolution decision table.
Constraints:
- You MUST explain the relationship between
maxTokensand quota reservation - You MUST guide the user through comparing current limits vs peak usage using
aws service-quotasandaws cloudwatch get-metric-statistics
Analyze Bedrock costs
You MUST read cost tracking reference before responding. It covers inference profile tagging, CUR 2.0 attribution, and AWS Budgets setup.
Constraints:
- You MUST ask what time range, grouping, and cost attribution method the user needs before generating Cost Explorer queries
Migrate between Claude generations
You MUST read model migration reference before responding. It covers breaking changes between Claude 4.5, 4.6, and 4.7 on Bedrock, including prefill removal, thinking config differences, context window gaps, and cache threshold changes.
Troubleshooting
When the user reports a Bedrock error, exception, or unexpected behavior, you MUST check this section and the Critical Warnings section before responding. Bedrock has service-specific root causes (e.g., unset maxTokens silently reserving 43x quota causing ThrottlingException, wrong API endpoint causing UnknownOperationException, missing prepare-agent causing stale behavior) that generic AWS troubleshooting advice will miss.
AccessDeniedException
Multiple possible causes: (1) IAM user/role lacks bedrock:InvokeModel or bedrock:InvokeModelWithResponseStream permissions, (2) model access not enabled in the target region, (3) a service control policy (SCP) is blocking access (common with cross-region inference routing to a restricted region), (4) expired temporary credentials, or (5) IAM role propagation delay — if you just created an IAM role and immediately used it in a Bedrock API call, the role may not have propagated yet, as IAM changes are eventually consistent (see IAM eventual consistency). Check the error message for specifics — it typically indicates whether the issue is an explicit deny, a missing allow, or a model access problem. See Resolve InvokeModel API errors for detailed resolution steps.
Malformed input request
Request body doesn't match the expected schema. Common causes: wrong provider-specific body format for InvokeModel (e.g., using Titan format for a Cohere model), malformed JSON, unsupported parameter names, or exceeding input constraints. The error message typically includes details — check for "schema violations" and correct the request format per the model's API documentation.
ThrottlingException
Set maxTokens explicitly — unset values default to the model's maximum and silently reserve far more quota than needed. Use adaptive retry mode. Use cross-region inference profiles (e.g., us., eu., apac., or global. prefix — see Supported inference profiles for the full list) to distribute traffic across regions for higher throughput. Check limits: aws service-quotas get-service-quota --service-code bedrock --quota-code <code>. Request quota increases if needed. For a deeper audit, read quota health reference.
Prompt cache not working (zero cacheReadInputTokens)
Read prompt caching reference for the diagnostic checklist: verify model support, token threshold, content identity, TTL, and cache point placement. Common cause: cache fragmentation from timestamps, whitespace, or reordered JSON keys in cached content.
400 error on prefill with Claude 4.6
Prefill was removed in Claude 4.6 and causes a hard 400 error. Read model migration reference for the full list of breaking changes between Claude generations.
Error retry classification
| Retry | Do NOT retry |
|---|---|
| ThrottlingException | ValidationException |
| ModelTimeoutException | AccessDeniedException |
| ServiceUnavailableException | ResourceNotFoundException |
| InternalServerException |
Use adaptive retry: Config(retries={"max_attempts": 5, "mode": "adaptive"}).
UnknownOperationException
Wrong client (using bedrock instead of bedrock-runtime), or SDK too old. Check the API landscape table above.
Agent returns stale behavior
Run prepare-agent after ANY configuration change. This is mandatory.
KB returns empty results
Run start-ingestion-job and wait for completion. Query before ingestion completes returns empty.
KB retrieval quality is poor
Review chunking strategy. Use advanced parsing (FM-based) for documents with tables. Configure metadata filtering.
Cross-region model not found
The model may not be available in the region you're calling from. Check availability at Supported foundation models. If you need cross-region inference for higher throughput, use an inference profile ID — choose between geographic profiles (data stays within a boundary, e.g. US, EU) or global profiles (any commercial region). The profile prefix is a data residency decision. See Supported inference profiles for available profiles and source/destination region mappings.
On-demand throughput isn't supported
Error: "Invocation of model ID `<model-id>` with on-demand throughput isn't supported. Retry your request with the ID or ARN of an inference profile that contains this model." Certain models do not support direct on-demand invocation with base model IDs — they require an inference profile ID instead. Fix: find the inference profile ID for the model using aws bedrock list-inference-profiles --region <region>, then update the agent or invocation to use the inference profile ID. See Supported inference profiles for available profiles. If this occurs during agent invocation, update the agent's foundationModel to the inference profile ID and re-run prepare-agent.
KB storage configuration invalid
Verify OpenSearch data access policy includes Bedrock service role. Verify vector index field names match KB config.
Agent action group errors
Check Lambda permissions (resource-based policy for bedrock.amazonaws.com). Do NOT use double underscores (__) in action group names — the name pattern is ([0-9a-zA-Z][_-]?){1,100}.
Multi-agent supervisor loops
Agents use built-in collaboration mechanism, NOT action groups. Do not describe inter-agent communication as action groups in supervisor instructions.
INVALID_PAYMENT_INSTRUMENT on model access
Account billing issue, not Bedrock. Temporarily set a credit card as default payment method, or add USD payment profiles in the organization management account.
Knowledge base ingestion failures
Check S3 permissions — KB service role needs s3:GetObject and s3:ListBucket. Unsupported file formats are silently skipped. Files exceeding size limits are skipped without error.
SharePoint data source sync failures
Sync completes but files fail. For OAuth 2.0 auth (not recommended): requires SharePoint AllSites.Read (Delegated) permission — you may also need to disable Security Defaults and MFA for the service account so Amazon Bedrock is not blocked from crawling. For SharePoint App-Only auth (recommended): configure APP permissions via SharePoint App-Only grant flow. See the SharePoint connector docs for current requirements.
AgentCore Services
You MUST read the linked reference file for the relevant service before responding to any AgentCore question. Follow procedures in the reference step by step.
| Service | Use For | Reference |
|---|---|---|
| Gateway | Expose APIs, Lambda functions, or existing MCP servers as tools for agents | gateway procedure |
| Runtime | Deploy and scale agents and tools (serverless, any framework) | runtime procedure |
| Runtime Container | Build ARM64 containers for Runtime | container build procedure |
| Memory | Short-term (multi-turn) and long-term (cross-session) agent memory; share memory across agents | memory & observability |
| Identity | Agent authentication with external IdPs (Okta, Entra ID, Cognito); act on behalf of users | credentials & security |
| Policy | Enforce agent boundaries with natural language or Cedar rules; intercepts Gateway tool calls | Refer to the latest AWS documentation on AgentCore Policy |
| Payments | Enable agents to pay for x402-protected APIs, MCP tools, and content via microtransactions (Coinbase CDP, Stripe Privy) | payments procedure |
| Observability | Trace, debug, and monitor agent execution (OTEL, CloudWatch) | memory & observability |
| Registry | Catalog and discover agents, MCP servers, tools, and skills across your org | registry & evaluations |
| Evaluations | Automated agent quality assessment (LLM-as-a-Judge) | registry & evaluations |
| Code Interpreter | Secure sandbox code execution for agents | Refer to the latest AWS documentation on AgentCore Code Interpreter |
| Browser | Web automation (navigate, fill forms, extract data) | Refer to the latest AWS documentation on AgentCore Browser |
Model Selection
When the user asks which model to use, compares models, or asks about Claude/Llama/Nova/Titan on Bedrock, you MUST read model selection guide before responding. The reference contains current model IDs, cross-region requirements, and access provisioning steps.
Quick defaults (verify current availability: aws bedrock list-foundation-models --region <region>):
- General purpose: Claude Sonnet (best quality/cost balance)
- Fast + cheap: Claude Haiku or Nova Micro
- Embeddings for KB: Titan Embeddings V2
- Open-source / fine-tuning: Llama
- Image generation: Titan Image Generator
For current model IDs, regional availability, cross-region inference profiles, and supported features, refer to Supported foundation models in Amazon Bedrock. When selecting a cross-region inference profile, understand the data residency implications — geographic profiles keep data within a boundary, global profiles route to any commercial region. Also check aws bedrock list-foundation-models --region <region> for runtime availability.
For model ID formats (4 patterns), access provisioning, and selection criteria, see model selection guide.
Additional Resources
AgentCore Credentials & Security
Table of Contents
- Credential Provider Patterns
- OAuth Three-Layer Architecture
- Cross-Account Access
- Security Best Practices
- Agent Persistence Patterns
Credential Provider Patterns
Three authentication types for AgentCore services. Getting the wrong type causes hard-to-debug 401/403 errors.
API Key Authentication
Security consideration: API keys are long-lived credentials. Prefer IAM authentication (ephemeral, auto-rotated) or OAuth when the target supports it. Use API keys only when the external target requires them (e.g., third-party APIs that only accept API key auth).
Setup sequence:
1. Create credential provider with the API key value (transmitted over TLS/SigV4; service encrypts and stores it in Secrets Manager internally)
2. Attach credential provider to Gateway targetConstraints:
- You MUST NOT pass the API key as a literal value on the command line — shell history exposes it
- You MUST ask the user to set the key as an environment variable:
export API_KEY=<their-key> - You MUST create the credential provider:
aws bedrock-agentcore-control create-api-key-credential-provider --name <name> --api-key "$API_KEY" - The service stores the key in Secrets Manager internally (response includes
apiKeySecretArn) - For rotation: update the API key through the service's control plane:
aws bedrock-agentcore-control update-api-key-credential-provider --name <name> --api-key "$NEW_API_KEY"— the service re-encrypts and stores the new key internally. Do not callsecretsmanager rotate-secretdirectly on the service-managed secret. - You MUST NOT hardcode API keys in agent code or configuration
- You MUST NOT log or display the API key value in agent output
- You SHOULD enable CloudTrail logging to audit all credential provider API calls — these are control plane management events (
CreateApiKeyCredentialProvider,UpdateApiKeyCredentialProvider,DeleteApiKeyCredentialProvider) logged undereventSource: bedrock-agentcore.amazonaws.com - Refer to AWS security best practices for AgentCore
OAuth Authentication
Constraints:
- The client secret is passed via the
create-oauth2-credential-providerAPI call (the service encrypts and stores it in Secrets Manager automatically — response includesclientSecretArn) - You MUST NOT hardcode client secrets in agent code or configuration
- You MUST NOT log or display client secret values in agent output
- Configure: token endpoint URL, client ID, scopes, grant type
- Create the OAuth2 credential provider:
aws bedrock-agentcore-control create-oauth2-credential-provider --name <name> --credential-provider-vendor <vendor> --oauth2-provider-config-input '...' - Refer to the latest AWS documentation on AgentCore OAuth configuration for current supported grant types and vendor options
IAM Authentication
For Lambda targets and cross-service communication:
- Service roles for AgentCore services
- Cross-service permissions: Runtime → Gateway → external API
- Resource-based policies for cross-account access
- No credential provider needed — IAM handles authentication
OAuth Three-Layer Architecture
AgentCore has three distinct OAuth layers — agents confuse these:
| Layer | Direction | Purpose |
|---|---|---|
| Inbound JWT | Caller → AgentCore | Validate tokens from callers (Cognito, external IdPs) |
| Outbound Credential Provider | Agent → External API | Agent authenticating to external APIs via Gateway |
| Gateway OAuth | Gateway → Upstream MCP | Gateway authenticating to upstream MCP servers |
Each layer is configured independently. Getting the wrong layer causes auth failures that look identical (401/403) but have different root causes.
Supported IdPs for inbound JWT: Cognito, Okta, Auth0, Azure AD, custom OIDC.
Refer to the latest AWS documentation on AgentCore OAuth architecture for current configuration steps and CDK examples.
Cross-Account Access
Cross-account Bedrock access requires IAM trust policies on both sides.
Pattern:
1. Calling account: IAM role with bedrock:InvokeModel permission and sts:AssumeRole to the target account's role 2. Target account: IAM role with trust policy allowing the calling account's principal, plus bedrock:InvokeModel permission
Trust policy pattern (target account role):
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<calling-account-id>:role/<role-name>"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<agreed-external-id>"
}
}
}Include sts:ExternalId for confused deputy protection. For service-to-service access, use aws:SourceArn and aws:SourceAccount conditions instead.
Common failure: AccessDeniedException when calling Bedrock from a different account — verify:
- Trust policy includes the calling account's principal ARN (not just account ID)
- The assumed role has
bedrock:InvokeModelpermission in the target account - Model access is enabled in the target account's region
Refer to the latest AWS documentation on Bedrock cross-account access for current IAM policy patterns and any service-specific conditions.
Security Best Practices
| Practice | How |
|---|---|
| Resource-based policies | Restrict access to specific principals, accounts, VPCs |
| VPC endpoints | Private AgentCore access without internet traversal |
| IP restrictions | Limit access by source IP range |
| Encryption | Data encrypted at rest and in transit by default |
| Audit logging | Enable CloudTrail for all AgentCore API calls |
| Least privilege | Grant only required permissions per service role |
Agent Persistence Patterns
Deploying framework-specific agents on AgentCore Runtime:
| Framework | Key Configuration |
|---|---|
| Strands Agents | S3 for file storage, session state via Memory service |
| LangChain/LangGraph | Standard Python deployment, state management via Memory |
| Custom frameworks | Implement the protocol contract (HTTP/MCP/A2A/AG-UI) |
Refer to the latest AWS documentation on AgentCore deployment for the relevant framework.
Constraints:
- All frameworks MUST meet the container contract: ARM64, health check, correct port
- See container build procedure for the build workflow
- State persistence SHOULD use the Memory service rather than local filesystem (containers are ephemeral)
AgentCore Gateway — Target Setup Procedure
Overview
Deterministic procedure for creating an AgentCore Gateway target that converts REST APIs into MCP tools agents can use. Gateway supports three authentication types, each with a different setup workflow. The creation order is strict — credentials MUST be created before the gateway target.
Parameters
- auth_type (required):
api_key|lambda_iam|oauth - openapi_schema_s3_uri (required): S3 URI of the OpenAPI schema
- api_key (required if api_key auth): The API key value
- lambda_arn (required if lambda_iam auth): Lambda function ARN
- oauth_config (required if oauth auth): Token endpoint, client ID, scopes
Constraints for parameter acquisition:
- You MUST ask for all required parameters (
auth_type,openapi_schema_s3_uri, and auth-type-specific parameters) upfront in a single prompt - You MUST confirm successful acquisition of all required parameters before proceeding to Step 1
Steps
General constraints:
- You MUST present an overview of the steps before starting
- You MUST explain to the user what step is being executed and why before running each command
- You MUST respect the user's decision to abort at any point
0. Verify Dependencies
Constraints:
- You MUST verify the AWS CLI is available and configured before proceeding
- You MUST verify AWS CLI version ≥ 2.13.22 (required for AgentCore commands):
aws --version - You MUST inform the user about any missing tools and ask if they want to proceed
1. Upload OpenAPI Schema to S3
Constraints:
- You MUST upload the OpenAPI schema to S3 before creating the gateway target
- Schema MUST be valid OpenAPI 3.0 or 3.1
- You MUST include clear operation descriptions — Gateway uses these to generate MCP tool descriptions
- Upload the schema:
aws s3api put-object --bucket <bucket> --key <key> --body <schema-file> - Refer to the latest AWS documentation on AgentCore Gateway OpenAPI schema requirements
2. Create Credential Provider (if API key or OAuth)
Constraints:
- You MUST create the credential provider BEFORE creating the gateway target — this ordering is mandatory
- Creating a target without credentials results in a "credential provider not found" error
For API key authentication:
- You MUST NOT pass the API key as a literal value on the command line — shell history exposes it
- You MUST ask the user to set the key as an environment variable:
export API_KEY=<their-key> - Create the credential provider:
aws bedrock-agentcore-control create-api-key-credential-provider --name <name> --api-key "$API_KEY"— the service encrypts and stores the key in Secrets Manager internally (response includesapiKeySecretArn). Do NOT manually create a Secrets Manager secret; the service manages this. - For key rotation:
aws bedrock-agentcore-control update-api-key-credential-provider --name <name> --api-key "$NEW_API_KEY"— do NOT callsecretsmanager rotate-secretdirectly on the service-managed secret
For OAuth authentication:
- The client secret is passed via the
create-oauth2-credential-providerAPI call — the service encrypts and stores it in Secrets Manager automatically (response includesclientSecretArn). Do NOT manually create a Secrets Manager secret. - You MUST NOT hardcode client secrets in agent code or configuration
- Configure token endpoint, client ID, client secret, and scopes
- Create the OAuth2 credential provider:
aws bedrock-agentcore-control create-oauth2-credential-provider --name <name> --credential-provider-vendor <vendor> --oauth2-provider-config-input '...' - Refer to the latest AWS documentation on AgentCore Gateway OAuth configuration options
For Lambda/IAM authentication:
- No credential provider needed — skip to Step 3
- The Gateway uses IAM role-based authentication to invoke the Lambda
- The Lambda MUST have a resource-based policy allowing the Gateway service role to invoke it, with
aws:SourceAccountandaws:SourceArnconditions to prevent confused deputy. Refer to the latest AWS documentation on AgentCore Gateway permissions for current policy patterns.
3. Create Gateway Target
Constraints:
- Create the target:
aws bedrock-agentcore-control create-gateway-target --gateway-identifier <gateway-id> --name <name> --target-configuration '...' --credential-provider-configurations '...' - You MUST link the OpenAPI schema S3 URI from Step 1
- If using API key or OAuth: You MUST link the credential provider ARN from Step 2
- If using Lambda: You MUST specify the Lambda ARN and configure IAM role with
lambda:InvokeFunctionscoped to the specific Lambda ARN — avoidResource: "*" - You MUST NOT create the target before the credential provider exists (for API key/OAuth)
4. Verify Target Status
Constraints:
- Poll target status:
aws bedrock-agentcore-control get-gateway-target --gateway-identifier <gateway-id> --target-id <target-id> - Wait for status
ACTIVEbefore using the target - If status is
FAILED: - Check IAM permissions
- Verify OpenAPI schema is valid
- Verify credential provider exists and is accessible
- Check CloudTrail for detailed error messages
- If status is stuck in
CREATINGfor >10 minutes: - Contact AWS Support with the gateway-id and target-id for investigation
- Refer to the latest AWS documentation or support channels for known issues
5. Test Connectivity
Constraints:
- You MUST test the gateway target with a sample request before using in production
- Verify the MCP tools generated from the OpenAPI schema match expectations
- You SHOULD report the list of generated MCP tools to the user
Security Considerations
- Encryption: S3 encrypts objects at rest by default (SSE-S3). For sensitive schemas, use SSE-KMS with a customer managed key. Target endpoints MUST use HTTPS — Gateway rejects HTTP endpoints.
- Least privilege: Scope IAM roles to specific resource ARNs — the Gateway service role should only access the specific S3 bucket, Secrets Manager secret, and Lambda function needed. Avoid
Resource: "*". - Sensitive data in logs: API keys and OAuth tokens may appear in CloudTrail logs. Enable CloudTrail log encryption with KMS. Do NOT log credential values in agent output.
- Monitoring: Enable CloudWatch alarms for gateway target errors (5xx rates, latency). Enable CloudTrail for audit logging of all
bedrock-agentcore-controlAPI calls. - TLS: All target endpoints must use TLS 1.2+. Use ACM certificates for custom domains.
- Refer to the latest AWS documentation on Bedrock security best practices.
AgentCore Memory & Observability
Table of Contents
- Memory Service
- Observability (AgentCore-Specific)
Memory Service
Provides conversation state persistence for agents deployed on AgentCore Runtime.
When to Enable
- Agents that need conversation context across multiple invocations (multi-turn chat)
- Agents that accumulate knowledge during a session
- Per-session lifecycle agents (see runtime reference)
- NOT needed for stateless per-request agents
Runtime Integration
The key non-obvious behavior: Runtime passes session IDs to the Memory service automatically when configured. You don't call Memory directly from your agent code — Runtime handles the plumbing.
Configuration:
- Session TTL: how long sessions persist after last activity (default varies). Set to the minimum required for your use case — longer TTLs increase the window of exposure for sensitive conversation data
- Memory types: session memory (conversation history), semantic memory (long-term knowledge)
- Refer to the latest AWS documentation on AgentCore Memory service configuration for current options
Common Failures
Session not found (expired TTL): Session expired between invocations. Increase TTL or handle gracefully in agent logic.
Session ID not passed from Runtime: Agent loses context between requests. Verify Memory service is enabled in Runtime configuration and the client passes sessionId in invocation requests.
Memory capacity exceeded: Session has too much accumulated context. Configure memory capacity limits or implement context summarization in agent logic.
Observability (AgentCore-Specific)
Only the AgentCore-specific parts — agents already know generic OTEL/CloudWatch patterns.
Required Trace Attributes for Evaluations
This is the key non-obvious requirement. AgentCore Evaluations service reads specific OTEL trace attributes to score agent quality. Without these, Evaluations can't work.
Required attributes:
- Agent input (user query)
- Agent output (response)
- Tool calls (which tools were invoked, with inputs/outputs)
- Latency per step
Instrumentation:
- Use AWS Distro for OpenTelemetry (ADOT) collector
- You MUST use an IAM role (not access keys) for ADOT collector authentication — attach to the ECS task, EC2 instance profile, or pod service account
- You MUST NOT hardcode AWS credentials in ADOT collector configuration files
- Configure sampling rate for evaluation (not every invocation needs evaluation)
- Refer to the latest AWS documentation on AgentCore observability OTEL instrumentation for current attribute names and collector configuration
AgentCore-Specific CloudWatch Metrics
AgentCore publishes these metrics automatically (you don't need to instrument):
| Metric | What It Measures |
|---|---|
| Invocation count | Number of agent invocations |
| Invocation latency | End-to-end response time (p50/p90/p99) |
| Error rate | Percentage of failed invocations |
| Token usage | Input/output tokens consumed |
Recommended alarms:
- Error rate > 5% for 5 minutes
- p99 latency > SLA threshold
- Token usage approaching quota (80%)
Create alarms — first discover the exact namespace (CloudWatch namespaces are case-sensitive):
1. aws cloudwatch list-metrics --namespace "Bedrock-AgentCore" — if no results, try --namespace "Bedrock-Agentcore" 2. Use the namespace that returns metrics in subsequent commands:
aws cloudwatch put-metric-alarm --alarm-name <name> --metric-name <metric> --namespace "<discovered-namespace>" --statistic Average --period 300 --threshold <value> --comparison-operator GreaterThanThreshold --evaluation-periods 3 --dimensions "Name=Resource,Value=<resource-arn>" --alarm-actions "<sns-topic-arn>"
Common Failures
Traces not appearing: OTEL collector not configured for AgentCore Runtime. Verify ADOT configuration in Runtime settings.
Evaluations can't score: Missing required trace attributes. Verify instrumentation includes input, output, and tool call attributes.
Security Considerations
Encryption:
- Enable KMS encryption at rest for Memory resources — customer-managed keys preferred for compliance workloads (HIPAA, GDPR)
- Memory data is encrypted in transit via TLS by default — do not disable TLS
- Encrypt CloudWatch Logs log groups receiving trace data with a KMS key
Sensitive data:
- Session memory stores conversation history which may contain PII, credentials, or business-sensitive data
- Trace attributes capture user queries and agent responses — treat as sensitive
- You MUST NOT log raw API keys, secrets, or credentials in trace attributes — sanitize tool call inputs before instrumentation
- Configure CloudWatch Logs retention limits — do not retain trace data indefinitely
IAM — least privilege:
- Scope Memory permissions to specific actions (
bedrock-agentcore:CreateMemory,bedrock-agentcore:GetMemory) — avoidbedrock-agentcore:* - Scope CloudWatch permissions to specific alarm and log group ARNs — avoid
cloudwatch:*orlogs:* - Use IAM roles (not IAM users) for all service access
Alarm notifications:
- Encrypt SNS topics used for alarm actions with a KMS key
- Restrict SNS topic subscriptions to authorized personnel
- Include
aws:SourceAccountcondition in the SNS topic access policy
Setup Script Template
Once you have all inputs from Step 3, generate a single Python script called setup_payments.py that executes all the following steps automatically without human intervention. Write the script, then execute it.
The script must:
1. Store payment provider credentials in AgentCore Identity 2. Create the IAM execution role with trust policy and permissions 3. Wait for IAM propagation (15 seconds) 4. Create the Payment Manager and wait for READY status 5. Create the Payment Connector 6. Create the Payment Instrument (wallet) 7. Print a summary of all created resources and next steps
Template
Substitute the developer's inputs into the configuration section:
"""
AgentCore Payments Setup Script
Generated by the payments skill. Executes all non-interactive setup steps.
NAMING RULES:
- Resource names (credential provider, manager, connector): lowercase alphanumeric + hyphens only.
NO underscores, NO dots, NO uppercase. Pattern: [a-z0-9]([a-z0-9-]*[a-z0-9])?
- The paymentManagerId (returned by create) is used for CP get/list operations.
- The paymentManagerArn (returned by create) is used for DP operations (instrument, session, process).
- create_payment_session requires userId parameter.
"""
import boto3
import json
import uuid
import time
import os
# === CONFIGURATION (from developer inputs) ===
REGION = "<REGION>" # e.g., "ap-southeast-2"
ACCOUNT_ID = "<ACCOUNT_ID>" # e.g., "123456789012"
PROVIDER = "<PROVIDER>" # "CoinbaseCDP" or "StripePrivy"
END_USER_EMAIL = "<END_USER_EMAIL>" # e.g., "developer@example.com"
RESOURCE_PREFIX = "paymentspoc" # prefix for all resource names
# Read credentials from environment variables (NOT from file directly).
# Run `source .env.payments` in your terminal before executing this script.
# Do NOT pass credentials through the agent — they must stay local.
# For Coinbase:
COINBASE_API_KEY_ID = os.environ.get("COINBASE_API_KEY_ID", "")
COINBASE_API_KEY_SECRET = os.environ.get("COINBASE_API_KEY_SECRET", "")
COINBASE_WALLET_SECRET = os.environ.get("COINBASE_WALLET_SECRET", "")
# For Stripe:
AUTH_PRIVATE_KEY = os.environ.get("AUTH_PRIVATE_KEY", "")
AUTH_ID = os.environ.get("AUTH_ID", "")
PRIVY_APP_ID = os.environ.get("PRIVY_APP_ID", "")
PRIVY_APP_SECRET = os.environ.get("PRIVY_APP_SECRET", "")
# === CLIENTS ===
iam = boto3.client("iam")
cp_client = boto3.client("bedrock-agentcore-control", region_name=REGION)
dp_client = boto3.client("bedrock-agentcore", region_name=REGION)
print("=" * 60)
print("AgentCore Payments Setup")
print("=" * 60)
# === STEP 1: Store credentials ===
print("\n[1/6] Storing payment provider credentials...")
cred_name = f"{RESOURCE_PREFIX}-creds"
def create_credential_provider_with_retry(name, vendor, config, max_retries=5):
"""Create credential provider, appending a numeric suffix if name already exists."""
for attempt in range(max_retries):
unique_name = name if attempt == 0 else f"{name}-{attempt}"
try:
if vendor == "CoinbaseCDP":
resp = cp_client.create_payment_credential_provider(
name=unique_name,
credentialProviderVendor=vendor,
providerConfigurationInput={"coinbaseCdpConfiguration": config}
)
elif vendor == "StripePrivy":
resp = cp_client.create_payment_credential_provider(
name=unique_name,
credentialProviderVendor=vendor,
providerConfigurationInput={"stripePrivyConfiguration": config}
)
print(f" (Using name: {unique_name})")
return resp
except Exception as e:
if "already exists" in str(e).lower() or "conflict" in str(e).lower():
print(f" Name '{unique_name}' already exists, trying with suffix...")
continue
raise
raise Exception(f"Failed to create credential provider after {max_retries} attempts")
if PROVIDER == "CoinbaseCDP":
cred_config = {
"apiKeyId": COINBASE_API_KEY_ID,
"apiKeySecret": COINBASE_API_KEY_SECRET,
"walletSecret": COINBASE_WALLET_SECRET
}
elif PROVIDER == "StripePrivy":
cred_config = {
"appId": PRIVY_APP_ID,
"appSecret": PRIVY_APP_SECRET,
"authorizationPrivateKey": AUTH_PRIVATE_KEY,
"authorizationId": AUTH_ID
}
cred_resp = create_credential_provider_with_retry(cred_name, PROVIDER, cred_config)
credential_provider_arn = cred_resp["credentialProviderArn"]
print(f" OK Credential Provider ARN: {credential_provider_arn}")
# === STEP 2: Create IAM role ===
print("\n[2/6] Creating IAM service role...")
base_role_name = f"AgentCorePayments-{RESOURCE_PREFIX}"
def create_role_with_retry(base_name, max_retries=5):
"""Create IAM role, appending a numeric suffix if name already exists."""
for attempt in range(max_retries):
unique_name = base_name if attempt == 0 else f"{base_name}-{attempt}"
try:
iam.create_role(
RoleName=unique_name,
AssumeRolePolicyDocument=json.dumps({
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {"aws:SourceAccount": ACCOUNT_ID},
"ArnLike": {"aws:SourceArn": f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:payment-manager/{RESOURCE_PREFIX}-*"}
}
}]
}),
Description="Service role for AgentCore Payments"
)
print(f" (Using role name: {unique_name})")
return unique_name
except iam.exceptions.EntityAlreadyExistsException:
print(f" Role '{unique_name}' already exists, trying with suffix...")
continue
raise Exception(f"Failed to create role after {max_retries} attempts")
role_name = create_role_with_retry(base_role_name)
iam.put_role_policy(
RoleName=role_name,
PolicyName="PaymentsResourceRetrievalPolicy",
PolicyDocument=json.dumps({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "WorkloadIdentity",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:CreateWorkloadIdentity",
"bedrock-agentcore:GetWorkloadAccessToken",
"bedrock-agentcore:GetResourcePaymentToken"
],
"Resource": [
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:token-vault/default",
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:token-vault/default/paymentcredentialprovider/*",
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:workload-identity-directory/default",
f"arn:aws:bedrock-agentcore:{REGION}:{ACCOUNT_ID}:workload-identity-directory/default/workload-identity/*"
]
},
{
"Sid": "SecretsAccess",
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": f"arn:aws:secretsmanager:{REGION}:{ACCOUNT_ID}:secret:bedrock-agentcore-identity*"
}
]
})
)
role_arn = f"arn:aws:iam::{ACCOUNT_ID}:role/{role_name}"
print(f" OK Role ARN: {role_arn}")
print(" Waiting 15s for IAM propagation...")
time.sleep(15)
# === STEP 3: Create Payment Manager ===
print("\n[3/6] Creating Payment Manager...")
mgr_resp = cp_client.create_payment_manager(
name=RESOURCE_PREFIX,
description="Payment manager created by AgentCore Payments skill",
authorizerType="AWS_IAM",
roleArn=role_arn,
clientToken=str(uuid.uuid4())
)
payment_manager_arn = mgr_resp["paymentManagerArn"]
manager_id = mgr_resp["paymentManagerId"]
print(f" OK Payment Manager ARN: {payment_manager_arn}")
# Wait for READY
for i in range(12):
status_resp = cp_client.get_payment_manager(paymentManagerId=manager_id)
if status_resp["status"] == "READY":
break
time.sleep(5)
if status_resp["status"] != "READY":
raise Exception(
f"Payment Manager did not reach READY status after 60s "
f"(current: {status_resp['status']}). Check CloudTrail for errors."
)
print(f" OK Status: {status_resp['status']}")
# === STEP 4: Create Payment Connector ===
print("\n[4/6] Creating Payment Connector...")
connector_config_key = "coinbaseCDP" if PROVIDER == "CoinbaseCDP" else "stripePrivy"
conn_resp = cp_client.create_payment_connector(
paymentManagerId=manager_id,
name=f"{RESOURCE_PREFIX}connector",
description=f"{PROVIDER} connector",
type=PROVIDER,
credentialProviderConfigurations=[{
connector_config_key: {"credentialProviderArn": credential_provider_arn}
}],
clientToken=str(uuid.uuid4())
)
connector_id = conn_resp["paymentConnectorId"]
print(f" OK Connector ID: {connector_id}")
# === STEP 5: Create Payment Instrument ===
print("\n[5/6] Creating Payment Instrument (wallet)...")
user_id = f"{RESOURCE_PREFIX}-user"
instr_resp = dp_client.create_payment_instrument(
paymentManagerArn=payment_manager_arn,
paymentConnectorId=connector_id,
userId=user_id,
paymentInstrumentType="EMBEDDED_CRYPTO_WALLET",
paymentInstrumentDetails={
"embeddedCryptoWallet": {
"network": "ETHEREUM",
"linkedAccounts": [
{"email": {"emailAddress": END_USER_EMAIL}}
]
}
},
clientToken=str(uuid.uuid4())
)
instrument_data = instr_resp.get("paymentInstrument", instr_resp)
payment_instrument_id = instrument_data["paymentInstrumentId"]
wallet_details = instrument_data.get("paymentInstrumentDetails", {}).get("embeddedCryptoWallet", {})
wallet_address = wallet_details.get("walletAddress", "pending")
redirect_url = wallet_details.get("redirectUrl", None)
print(f" OK Instrument ID: {payment_instrument_id}")
print(f" OK Wallet Address: {wallet_address}")
# === STEP 6: Create Payment Session ===
print("\n[6/6] Creating Payment Session...")
session_resp = dp_client.create_payment_session(
paymentManagerArn=payment_manager_arn,
userId=user_id,
expiryTimeInMinutes=60
)
payment_session_id = session_resp["paymentSession"]["paymentSessionId"]
print(f" OK Session ID: {payment_session_id}")
# === SUMMARY ===
print("\n" + "=" * 60)
print("SETUP COMPLETE")
print("=" * 60)
print(f"""
Resources created:
Payment Manager ARN: {payment_manager_arn}
Connector ID: {connector_id}
Instrument ID: {payment_instrument_id}
Wallet Address: {wallet_address}
Session ID: {payment_session_id}
User ID: {user_id}
Region: {REGION}
Environment variables for your agent:
export PAYMENT_MANAGER_ARN="{payment_manager_arn}"
export PAYMENT_INSTRUMENT_ID="{payment_instrument_id}"
export PAYMENT_SESSION_ID="{payment_session_id}"
export PAYMENT_USER_ID="{user_id}"
export AWS_REGION="{REGION}"
""")
print("\nMANUAL STEPS REQUIRED:\n")
# Step 1: Delegation — provider-specific
if PROVIDER == "CoinbaseCDP":
print(f"""1. DELEGATION — Grant the agent permission to spend from the wallet:
Visit: {redirect_url}
Log in with: {END_USER_EMAIL}
Grant permissions to the wallet address: {wallet_address}
""")
elif PROVIDER == "StripePrivy":
print(f"""1. DELEGATION — Enable delegation on the embedded wallet:
a. Set up a frontend using the Privy frontend SDK:
https://github.com/privy-io/aws-agentcore-sdk
b. Log in with the end user email: {END_USER_EMAIL}
c. Approve delegation for the wallet address: {wallet_address}
""")
# Step 2: Funding — same for both providers
print(f"""2. FUNDING — Send testnet USDC to the wallet:
Go to: https://faucet.circle.com/
Select: Base Sepolia
Paste wallet address: {wallet_address}
""")After executing the script
- Tell the developer to run
source .env.paymentsbefore executing the script - Print the summary to the developer
- Tell them to complete the two manual steps (delegation + funding) for the provider they chose
- Do NOT reference the other provider's flow — only show steps for the provider in use
- Wait for them to confirm before proceeding to Step 5 (wiring)
Agent Wiring Code
Once the developer confirms delegation and funding are done, modify their existing agent code to add a custom x402-aware fetch tool.
Find the agent's entrypoint file (e.g., main.py, app.py, or the file containing the Agent(...) constructor). Based on the framework detected in Step 1, use the appropriate pattern below.
Why a custom tool instead of the AgentCorePaymentsPlugin?
The AgentCorePaymentsPlugin works by intercepting tool results via anafter_tool_call hook. It only works when the tool surfaces the full HTTPresponse. Many tools do not expose response headers where the x402 challenge
often lives.
>
The custom x402_fetch tool handles the full flow internally:request → detect 402 → extract challenge (body OR header) → ProcessPayment →
build proof → retry with fresh client → return content.
>
Critical: Use a fresh httpx client for the retry. Some merchants set cookies
on the 402 response that cause the retry to fail if sent back.
>
Version-aware proof. The tool reads x402Version from the challenge andbuilds the matching proof: v1 sends an X-PAYMENT header with a flat proof(top-levelscheme/network), v2 sends aPAYMENT-SIGNATUREheader where
acceptedis a top-level sibling ofpayloadandpayloadholds only
signature+authorization(no top-levelscheme/network). The
ProcessPayment input is the same for both (always CAIP-2 network); only theproof presented to the merchant differs.
Core Payment Logic (shared across all frameworks)
import os
import json
import base64
import httpx
import boto3
# Payment configuration from environment
PAYMENT_MANAGER_ARN = os.getenv("PAYMENT_MANAGER_ARN")
PAYMENT_INSTRUMENT_ID = os.getenv("PAYMENT_INSTRUMENT_ID")
PAYMENT_SESSION_ID = os.getenv("PAYMENT_SESSION_ID")
PAYMENT_USER_ID = os.environ.get("PAYMENT_USER_ID") # Required — no insecure default
REGION = os.getenv("AWS_REGION", "us-west-2")
# AgentCore Payments data plane client
_dp_client = boto3.client("bedrock-agentcore", region_name=REGION) if PAYMENT_MANAGER_ARN else None
def _validate_url(url: str) -> str | None:
"""Validate URL is HTTPS and not targeting private/internal networks."""
from urllib.parse import urlparse
import ipaddress
import socket
parsed = urlparse(url)
if parsed.scheme != "https":
return "Only HTTPS URLs are supported for payment requests"
# Resolve hostname and block private/internal IP ranges
try:
addrinfos = socket.getaddrinfo(parsed.hostname, parsed.port or 443)
for family, _, _, _, sockaddr in addrinfos:
ip = ipaddress.ip_address(sockaddr[0])
if ip.is_private or ip.is_loopback or ip.is_link_local:
return "Cannot fetch private/internal network addresses"
except socket.gaierror:
return "Cannot resolve hostname"
return None
def _x402_fetch_impl(url: str, method: str = "GET") -> str:
"""Fetch a URL with automatic x402 payment handling.
If the endpoint returns 402 Payment Required with an x402 challenge,
automatically processes the payment and retries with proof.
"""
# Validate URL (HTTPS-only, no private IPs)
url_error = _validate_url(url)
if url_error:
return json.dumps({"error": url_error})
# Validate PAYMENT_USER_ID is set
if not PAYMENT_USER_ID:
return json.dumps({"error": "PAYMENT_USER_ID environment variable is required"})
# NOTE: Payment Sessions enforce service-level budget and time limits
# (expiryTimeInMinutes). Keep sessions short-lived to bound spending.
# First attempt
response = httpx.request(method, url, timeout=30)
if response.status_code != 402:
return json.dumps({
"status_code": response.status_code,
"body": response.text
})
# --- Got 402: Extract x402 challenge ---
x402_challenge = None
# Try response body first (standard x402 v1 style)
try:
body_json = response.json()
if "x402Version" in body_json and "accepts" in body_json:
x402_challenge = body_json
except Exception:
pass
# Fall back to payment-required header (base64-encoded)
if not x402_challenge:
header_val = response.headers.get("payment-required")
if header_val:
try:
x402_challenge = json.loads(base64.b64decode(header_val))
except Exception:
pass
if not x402_challenge:
return json.dumps({
"status_code": 402,
"error": "Payment required but no x402 challenge found",
"body": response.text
})
# --- Call ProcessPayment ---
if not _dp_client or not PAYMENT_MANAGER_ARN:
return json.dumps({
"status_code": 402,
"error": "Payment required but no payment configuration available. Set PAYMENT_MANAGER_ARN env var.",
"x402_challenge": x402_challenge
})
accepts = x402_challenge["accepts"][0]
try:
payment_response = _dp_client.process_payment(
paymentManagerArn=PAYMENT_MANAGER_ARN,
paymentInstrumentId=PAYMENT_INSTRUMENT_ID,
paymentSessionId=PAYMENT_SESSION_ID,
userId=PAYMENT_USER_ID,
paymentType="CRYPTO_X402",
paymentInput={
"cryptoX402": {
"version": str(x402_challenge.get("x402Version", "1")),
"payload": {
"scheme": accepts.get("scheme", "exact"),
"network": accepts["network"],
"amount": accepts.get("amount", accepts.get("maxAmountRequired", "0")),
"asset": accepts["asset"],
"payTo": accepts["payTo"],
"maxTimeoutSeconds": accepts.get("maxTimeoutSeconds", 60),
**({"extra": accepts["extra"]} if "extra" in accepts else {})
}
}
}
)
except Exception as e:
return json.dumps({
"status_code": 402,
"error": f"ProcessPayment failed: {e}"
})
# --- Build the payment header proof (version-aware) ---
# ProcessPayment input above is identical for v1 and v2 (always CAIP-2).
# Only the proof presented to the merchant differs by x402 version.
crypto_output = payment_response["paymentOutput"]["cryptoX402"]
auth = crypto_output["payload"]["authorization"]
x402_version = int(x402_challenge.get("x402Version", 1))
authorization = {
"from": auth["from"],
"to": auth["to"],
"value": auth["value"],
"validAfter": auth["validAfter"],
"validBefore": auth["validBefore"],
"nonce": auth["nonce"]
}
if x402_version >= 2:
# x402 v2: header is PAYMENT-SIGNATURE. `accepted` is a TOP-LEVEL sibling
# of `payload` (echoing the merchant's accepted entry, CAIP-2 network).
# `payload` holds ONLY signature + authorization. There are NO top-level
# scheme/network fields. This matches the Coinbase facilitator
# x402V2PaymentPayload schema.
proof = {
"x402Version": 2,
"accepted": {
"scheme": accepts.get("scheme", "exact"),
"network": accepts["network"],
"amount": accepts.get("amount", accepts.get("maxAmountRequired", "0")),
"asset": accepts["asset"],
"payTo": accepts["payTo"],
"maxTimeoutSeconds": accepts.get("maxTimeoutSeconds", 60),
**({"extra": accepts["extra"]} if "extra" in accepts else {})
},
"payload": {
"signature": crypto_output["payload"]["signature"],
"authorization": authorization
}
}
# Optionally echo the resource block from the challenge if present.
if "resource" in x402_challenge:
proof["resource"] = x402_challenge["resource"]
payment_header_name = "PAYMENT-SIGNATURE"
else:
# x402 v1: header is X-PAYMENT, proof is flat (top-level scheme/network).
proof = {
"x402Version": 1,
"scheme": "exact",
"network": accepts["network"],
"payload": {
"signature": crypto_output["payload"]["signature"],
"authorization": authorization
}
}
payment_header_name = "X-PAYMENT"
payment_header = base64.b64encode(
json.dumps(proof, separators=(',', ':')).encode()
).decode()
# --- Retry with payment proof (fresh client to avoid cookie contamination) ---
with httpx.Client(verify=True) as client:
retry_response = client.request(
method, url,
headers={payment_header_name: payment_header},
timeout=30
)
# payment_made reflects the actual retry status — a 2xx means the merchant
# accepted the proof. Do NOT hardcode this True: ProcessPayment can succeed
# (proof generated) while the retry still returns 402 (e.g. wrong proof
# shape, expired proof, or an on-chain settlement failure).
return json.dumps({
"status_code": retry_response.status_code,
"body": retry_response.text,
"payment_made": 200 <= retry_response.status_code < 300,
"process_payment_id": payment_response.get("processPaymentId", "unknown")
})Strands — tool decorator pattern
from strands import Agent, tool
@tool
def x402_fetch(url: str, method: str = "GET") -> str:
"""Fetch a URL with automatic x402 payment handling.
If the endpoint returns 402 Payment Required with an x402 challenge,
this tool automatically processes the payment and retries with proof.
Args:
url: The URL to fetch
method: HTTP method (GET, POST, etc.)
"""
return _x402_fetch_impl(url, method)
agent = Agent(
model="<model_id>",
tools=[x402_fetch],
system_prompt=(
"You are a helpful assistant that can access paid APIs and content. "
"Use the x402_fetch tool to access URLs that may require payment — "
"it handles x402 payments automatically."
),
)LangGraph — tool pattern
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
from langchain_aws import ChatBedrock
@tool
def x402_fetch(url: str, method: str = "GET") -> str:
"""Fetch a URL with automatic x402 payment handling.
If the endpoint returns 402 Payment Required with an x402 challenge,
this tool automatically processes the payment and retries with proof.
Args:
url: The URL to fetch
method: HTTP method (GET, POST, etc.)
"""
return _x402_fetch_impl(url, method)
model = ChatBedrock(model_id="<model_id>", region_name=REGION)
graph = create_react_agent(model, tools=[x402_fetch])
# Invoke:
result = graph.invoke({"messages": [("human", "Fetch https://paid-api.example.com/data")]})
print(result["messages"][-1].content)OpenAI Agents SDK — function_tool pattern
from agents import Agent, Runner, function_tool
@function_tool
def x402_fetch(url: str, method: str = "GET") -> str:
"""Fetch a URL with automatic x402 payment handling.
If the endpoint returns 402 Payment Required with an x402 challenge,
this tool automatically processes the payment and retries with proof.
Args:
url: The URL to fetch
method: HTTP method (GET, POST, etc.)
"""
return _x402_fetch_impl(url, method)
agent = Agent(
name="PaymentAgent",
instructions=(
"You are a helpful assistant that can access paid APIs and content. "
"Use the x402_fetch tool to access URLs that may require payment — "
"it handles x402 payments automatically."
),
tools=[x402_fetch],
)
# Invoke:
import asyncio
result = asyncio.run(Runner.run(agent, "Fetch https://paid-api.example.com/data"))
print(result.final_output)Other Frameworks
If the developer's framework is not listed above, they can call _x402_fetch_impl() directly from whatever tool/function mechanism their framework provides. The core logic is pure Python with no framework dependencies.
AgentCore Payments
Overview
Add AgentCore Payments to your agent — the managed service that enables microtransaction payments in AI agents to access paid APIs, MCP servers, and content via the x402 protocol.
The AWS MCP server is recommended for executing AWS commands (sandboxed execution, audit logging, observability), but is not required. If the MCP server is not available, use AWS CLI or boto3 scripts instead.
When to Use
- Your agent encounters HTTP 402 Payment Required responses from paid endpoints
- You want your agent to autonomously pay for x402-protected content (APIs, MCP tools, paywalled sites)
- You want to establish granular budget controls at user and agent levels
- You need to set up AgentCore Payments resources from scratch
- You already have payments configured but need to wire the plugin into agent code
- Payment processing is not working as expected
Do NOT use for:
- General agent scaffolding or project creation
- Connecting to external APIs via Gateway (OpenAPI specs, Lambda, MCP servers)
- Agent deployment or infrastructure
- Non-payment related agent capabilities (memory, VPC, multi-agent)
Input
$ARGUMENTS is optional. If provided, use it as context:
/payments # full setup from scratch
/payments wire # already have resources, need code
/payments debug # payments not working
/payments coinbase # use Coinbase connector
/payments stripe # use Stripe connectorProcess
Step 1: Read the project context
Read the agent's entrypoint file (e.g., main.py, app.py). Detect the framework:
from strands import Agent→ Strandsfrom langgraphorfrom langchain→ LangGraphfrom agents import Agent→ OpenAI Agents SDK- No recognizable framework → default to the custom tool pattern
Step 2: Determine the situation
Case A — No payments configured yet No Payment Manager exists. Proceed to Step 3 (prerequisites) then Step 4 (resource creation).
Case B — Payments resources exist, needs wiring The developer already has a Payment Manager. Skip to Step 5 (generate wiring code). Ask for their Payment Manager ARN, Instrument ID, and Session ID.
Case C — Payments configured and wired, debugging Ask: "What's happening? Is the agent seeing 402 but not paying? Is ProcessPayment failing? What error do you see?" Then diagnose using the Debugging section below.
Case D — Developer asking about payments without a project Answer directly. For architecture questions, explain the x402 flow. For code questions, show the custom tool pattern.
Step 3: Collect inputs from the developer
Before setting up payments, collect these inputs:
1. Which payment provider? — Coinbase CDP or Stripe Privy 2. Which AWS region? — must be one of: us-east-1, us-west-2, eu-central-1, ap-southeast-2 3. AWS account ID — the account where resources will be created 4. AWS credentials — the developer needs two levels of access:
For running the setup script (one-time, admin-level):
iam:CreateRole,iam:PutRolePolicy— to create the service rolebedrock-agentcore:CreatePaymentCredentialProvider— to store provider credentialsbedrock-agentcore:CreatePaymentManager,bedrock-agentcore:GetPaymentManager— to create the managerbedrock-agentcore:CreatePaymentConnector— to create the connectorbedrock-agentcore:CreatePaymentInstrument— to create the walletbedrock-agentcore:CreatePaymentSession— to create a session
In practice, an Admin or PowerUser role covers all of these.
For running the agent (ongoing, can be scoped down):
bedrock-agentcore:ProcessPayment— to execute paymentsbedrock-agentcore:GetPaymentInstrument,bedrock-agentcore:GetPaymentSession— for read operationsbedrock:InvokeModelorbedrock:InvokeModelWithResponseStream— if using Bedrock models
Verify credentials are active: aws sts get-caller-identity
5. End user email — the email of the person whose wallet the agent will spend from. For POC/testing, the developer's own email is fine.
Once you have answers 1-5, show the provider-specific .env.payments template and ask the developer to create the file and run source .env.payments:
For Coinbase CDP (get credentials from https://portal.cdp.coinbase.com/):
How to get these credentials:
1. Create or log in to a Coinbase Developer Platform account and project 2. Generate an API key (or reuse existing) — note the API Key ID and API Key Secret 3. Generate a Wallet Secret (for cryptographic wallet operations like signing transactions) 4. Under Project > Wallet > Embedded Wallets > Policies, enable Delegated signing
# .env.payments — DO NOT COMMIT THIS FILE
export COINBASE_API_KEY_ID=your-api-key-id-uuid-here
export COINBASE_API_KEY_SECRET=your-base64-encoded-api-key-secret-here
export COINBASE_WALLET_SECRET=your-base64-encoded-wallet-secret-hereFor Stripe Privy (get credentials from https://dashboard.privy.io/):
How to get these credentials:
1. Create a dedicated Privy app for AgentCore (do not reuse apps serving other purposes) 2. Copy the App ID and App Secret from app settings 3. Navigate to Wallet Infrastructure > Authorization > New Key to generate a P-256 key pair 4. The private key is prefixed with wallet-auth: — strip this prefix, use only the raw base64 content 5. Note the Authorization ID (signer ID) shown alongside the key
# .env.payments — DO NOT COMMIT THIS FILE
export AUTH_PRIVATE_KEY=your-base64-encoded-ec-private-key-here
export AUTH_ID=your-hex-auth-id-here
export PRIVY_APP_ID=your-privy-app-id-here
export PRIVY_APP_SECRET=privy_app_secret_your-secret-here[!WARNING]
For Privy: The generated private key starts with wallet-auth:. You MUSTstrip this prefix. Only the raw base64 content (starting with MIGHAgEA...)is accepted by AgentCore.
After they confirm the file exists and have run source .env.payments, add .env.payments to .gitignore.
Security: Do NOT paste credentials directly in chat or ask the agent to read
the.env.paymentsfile. Instead, runsource .env.paymentsin your terminal
to expose the values as environment variables locally. The setup script reads
from environment variables, not the file directly.
>
Production: If needed to be stored outside of AgentCore Identity ever,
store credentials in AWS Secrets Manager or SSM Parameter Store
(SecureString) and retrieve them at runtime. The .env.payments file is forlocal development only.
Step 4: Generate and execute the setup script
Read setup-script.md for the full script template. Substitute the developer's inputs and execute it.
The script creates:
1. Payment Credential Provider (stores provider credentials in AgentCore Identity) 2. IAM execution role with trust policy and permissions 3. Payment Manager (waits for READY status) 4. Payment Connector 5. Payment Instrument (wallet) 6. Payment Session
Step 5: Wire the x402 tool into the agent
Read wiring.md for framework-specific tool code. Use the pattern matching the detected framework from Step 1.
The x402_fetch tool:
1. Makes an HTTP request to the target URL 2. If 402, extracts the x402 challenge from body or payment-required header 3. Calls ProcessPayment to get a signed payment proof 4. Retries with the payment header (X-PAYMENT for v1, PAYMENT-SIGNATURE for v2) using a fresh HTTP client to avoid cookie contamination 5. Returns the paid content
Step 6: Test the integration
Set environment variables (printed by setup script) and run the agent:
export PAYMENT_MANAGER_ARN="..."
export PAYMENT_INSTRUMENT_ID="..."
export PAYMENT_SESSION_ID="..."
export PAYMENT_USER_ID="..."
export AWS_REGION="..."Test with:
Fetch the content from https://sandbox.node4all.com/v1/x402-test and tell me what you find.Note: This test endpoint is an x402 v2 merchant. The x402_fetch tooldetects the version from the challenge and sends a PAYMENT-SIGNATURE headerwith the v2 proof shape. If the agent loops on 402 here, the proof is likely
being sent as v1 (X-PAYMENT) — see the Debugging section.Expected behavior:
1. Agent calls x402_fetch with the URL 2. Gets 402 with x402 challenge (0.1 USDC on Base Sepolia) 3. Calls ProcessPayment → gets signed proof 4. Retries with PAYMENT-SIGNATURE header (v2 endpoint) → gets 200 5. Returns the content to the user
If the session has expired, create a fresh one:
export PAYMENT_SESSION_ID=$(aws bedrock-agentcore create-payment-session \
--payment-manager-arn "$PAYMENT_MANAGER_ARN" \
--user-id "$PAYMENT_USER_ID" \
--expiry-time-in-minutes 60 \
--region "$AWS_REGION" \
--query 'paymentSession.paymentSessionId' --output text)Security Considerations
- Credential rotation: Rotate payment provider credentials periodically. Recreate the credential provider with updated values.
- Budget/spend limits: Use Payment Session
expiryTimeInMinutesand per-session budget controls to prevent runaway payments. - Audit logging: Verify CloudTrail is logging all
bedrock-agentcoreAPI calls, especiallyProcessPayment. For production, set up a CloudWatch alarm for failed payment attempts as a potential abuse indicator. - SSRF mitigation: The
x402_fetchtool enforces HTTPS-only and blocks private IP ranges to prevent fetching internal endpoints. - Least privilege: The IAM service role should only have the minimum permissions required (token-vault, workload-identity, secrets access).
- Session expiry: Keep payment sessions short-lived (60 minutes or less). Create fresh sessions per user interaction rather than reusing long-lived ones.
- Encryption in transit: All payment requests must use HTTPS. The
x402_fetchtool rejects non-HTTPS URLs.
For comprehensive security guidance, see the AgentCore Security documentation.
How x402 Payment Works (End-to-End)
Agent calls x402_fetch("https://paid-api.example.com/data")
│
├─ 1. HTTP GET → 402 Payment Required
│ Body: {"x402Version": 1, "accepts": [{"scheme": "exact", "network": "base-sepolia", ...}]}
│
├─ 2. Extract x402 challenge
│
├─ 3. ProcessPayment(paymentManagerArn, instrumentId, sessionId, challenge)
│ → Returns signed proof (signature + authorization)
│
├─ 4. Build payment header (X-PAYMENT for v1, PAYMENT-SIGNATURE for v2)
│
├─ 5. Retry with payment header (fresh HTTP client, no cookies)
│ → 200 OK + paid content
│
└─ 6. Return content to agentSupported Networks
Two concepts: network (blockchain family, used when creating instruments) and chain (specific chain, used in x402 challenges and balance queries).
Networks (for instrument creation):
| Network | Instrument Value | Providers |
|---|---|---|
| Ethereum (includes Base, Base Sepolia) | ETHEREUM | Coinbase, Stripe |
| Solana (includes Solana Devnet) | SOLANA | Coinbase, Stripe |
Chains (in x402 challenges and balance queries):
| Chain | Identifier (x402) | Balance API value | Type | Provider |
|---|---|---|---|---|
| Base Sepolia | base-sepolia or eip155:84532 | BASE_SEPOLIA | Testnet | Coinbase |
| Base | eip155:8453 | BASE | Mainnet | Coinbase |
| Ethereum Mainnet | eip155:1 | ETHEREUM | Mainnet | Coinbase, Stripe |
| Solana Mainnet | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | SOLANA | Mainnet | Coinbase, Stripe |
| Solana Devnet | solana-devnet | SOLANA_DEVNET | Testnet | Stripe |
For testing, start with Base Sepolia (network: ETHEREUM, chain: BASE_SEPOLIA) — free testnet tokens from https://faucet.circle.com/.
Debugging payments
Agent sees 402 but does not pay:
1. Verify PAYMENT_MANAGER_ARN env var is set and not None 2. Check that the agent is using x402_fetch tool (not a generic http_request) 3. Verify the x402 challenge is present in either the response body (x402Version + accepts fields) or the payment-required header
ProcessPayment fails with "Failed to obtain resource payment token":
- The IAM service role is missing permissions. Ensure it has
GetResourcePaymentTokenon the token-vault andsecretsmanager:GetSecretValueon the secrets. - Wait 15+ seconds after creating the role before calling ProcessPayment (IAM propagation).
ProcessPayment fails with "Failed to obtain workload access token":
- The service role is missing
GetWorkloadAccessTokenpermission on the workload-identity-directory resources.
ProcessPayment fails with "Failed to assume payment execution role":
- The service role's trust policy is incorrect. Ensure it trusts
bedrock-agentcore.amazonaws.comwith the correctaws:SourceAccountcondition. - Verify the role ARN passed to the Payment Manager matches the actual role.
ProcessPayment succeeds but merchant still returns 402:
- Cookie contamination: The retry is sending cookies from the initial 402 request. Ensure you use a fresh httpx client:
httpx.Client(cookies=None).request(...)— do NOT reuse the same client/session. - Wrong x402 version / header: The merchant is x402 v2 but the proof was sent as v1 (or vice versa). v1 expects an
X-PAYMENTheader with a flat proof (top-levelscheme/network); v2 expects aPAYMENT-SIGNATUREheader whereacceptedis a top-level sibling ofpayload, andpayloadholds onlysignature+authorization(no top-levelscheme/network). A v2 merchant that receives a v1X-PAYMENTheader ignores it and re-issues the same 402 — often with an empty{}body and no error, which is hard to diagnose. Readx402Versionfrom the challenge (body orpayment-requiredheader) and build the matching proof. - Proof format mismatch (network field): For v1, the proof
networkmust use the merchant's human label (e.g.,"base-sepolia"not"eip155:84532"). For v2, the proof keeps the CAIP-2 identifier from the challenge unchanged (e.g.,"eip155:84532"). Note: theProcessPaymentinput always uses CAIP-2 regardless of version — only the proof presented to the merchant differs. - Proof expired: The proof has a ~60 second validity window (
validBefore). If the agent loop is slow, the proof may expire before the retry.
ProcessPayment succeeds (PROOF_GENERATED) but merchant returns 402 with an empty `{}` body and no error:
- The merchant is x402 v2 and is ignoring the v1
X-PAYMENTheader. Detect the version from the challenge (x402Version: 2, present in the body or thepayment-requiredresponse header) and send aPAYMENT-SIGNATUREheader. The v2 proof putsaccepted(the full requirements, CAIP-2 network) as a top-level sibling ofpayload, withpayloadcontaining onlysignature+authorization. Note: if ProcessPayment returnsPROOF_GENERATEDand the proof shape is correct but the merchant still 402s, it may be a transient on-chain settlement failure — retry once before assuming a format problem.
ProcessPayment fails with "Payment session not found":
- The session ID is invalid or the session was deleted. Create a new session.
- Ensure the
paymentManagerArnin the session creation matches the one used in ProcessPayment.
ProcessPayment fails with "PaymentSessionExpired":
- Payment sessions are time-bounded. Create a fresh session with
expiryTimeInMinutes.
ProcessPayment fails with "Payment instrument not found" or "does not belong to user":
- Verify the instrument ID is correct and belongs to the same Payment Manager.
- Check that the
userIdpassed to ProcessPayment matches theuserIdused when the instrument was created.
ProcessPayment fails with "Payment connector is not active":
- The connector may still be provisioning. Check its status and wait.
- If the connector was deleted or deactivated, create a new one.
ProcessPayment fails with "Network mismatch":
- The x402 challenge specifies a network that does not match the instrument's network.
- Instruments created with
network: "ETHEREUM"support Base, Base Sepolia, and Ethereum chains. - Instruments created with
network: "SOLANA"support Solana and Solana Devnet chains.
ProcessPayment fails with "Payment asset not supported USDC token address":
- The USDC contract address in the x402 challenge does not match the expected address for that network.
- Base Sepolia USDC:
0x036CbD53842c5426634e7929541eC2318f3dCF7e - Only USDC is supported.
ProcessPayment fails with "Wallet does not have a USDC balance":
- The wallet has no USDC on the specified chain.
- Fund via Circle faucet (testnet): https://faucet.circle.com/
- For mainnet: the end user must fund the wallet directly.
Coinbase: "Delegated signing grant is not active":
- The end user has not completed the delegation step.
- Redirect them to the
redirectUrlreturned during instrument creation (Coinbase Hub). - They must log in and grant permissions to the wallet.
Coinbase: "Delegated signing is not enabled":
- The Coinbase CDP project does not have delegated signing enabled.
- Go to portal.cdp.coinbase.com > Project > Wallet > Embedded Wallets > Policies > Enable Delegated signing.
Stripe Privy: "Privy credentials are invalid":
- The App ID or App Secret stored in the credential provider is wrong.
- Verify in Privy Dashboard that the credentials match.
- Recreate the credential provider with the correct values.
Stripe Privy: "Privy appId is invalid or missing":
- The
appIdin the credential provider configuration is incorrect. - Check Privy Dashboard for the correct App ID.
Stripe Privy: "Privy signing key is invalid or expired":
- The Authorization Private Key or Authorization ID is invalid or has expired.
- Generate a new P-256 key pair in Privy Dashboard > Wallet Infrastructure > Authorization.
- Remember to strip the
wallet-auth:prefix from the private key. - Update the credential provider with the new key.
Stripe Privy: "Wallet policy denied the transaction":
- A wallet policy configured in Privy is blocking the transaction.
- Review wallet policy settings in Privy Dashboard.
- Check if the transaction amount, recipient, or frequency exceeds policy limits.
Stripe Privy: "The linked account data is invalid":
- The email or phone number used in
linkedAccountswhen creating the instrument is malformed. - Verify the email format is valid.
Stripe Privy: "Rate limited by Privy":
- The Privy API is rate limiting your requests.
- Back off and retry. Check Privy's rate limits documentation.
ProcessPayment fails with "Payment amount exceeds maximum":
- The x402 challenge requests more than the maximum allowed per transaction.
- Check the amount in the challenge and verify your session budget allows it.
ProcessPayment fails with "Rate exceeded":
- Too many API calls. Back off and retry after a few seconds.
Coinbase: "Delegation not completed":
- The end user has not granted the agent permission to spend from their wallet.
- Visit the
redirectUrlreturned during instrument creation, log in, and grant permissions.
Stripe Privy: "Delegation not completed":
- The agent auth key has not been added as a signer on the embedded wallet.
- Set up a frontend using the Privy frontend SDK (https://github.com/privy-io/aws-agentcore-sdk), log in with the end user email provided during setup, and approve delegation for the wallet.
AgentCore Registry & Evaluations
Table of Contents
- Agent Registry (Preview)
- Evaluations Service
Agent Registry (Preview)
Catalog, discover, and govern AI agents and tools across an organization.
Governance Workflow
The key non-obvious behavior — two modes:
| Mode | Behavior | Use For |
|---|---|---|
| Auto-approve | Records become discoverable immediately | Development environments (isolated accounts only) |
| Manual approval | Records require explicit approval before discovery | Production environments |
Status transitions: PENDING → APPROVED → ACTIVE (or REJECTED)
Common failure: Record stuck in PENDING — governance workflow is set to manual approval but no one has approved. Check governance configuration or switch to auto-approve for dev.
Registering Resources
Resource types: MCP servers, A2A agents, agent skills, custom types.
Constraints:
- You MUST specify resource type, name, description, and invocation endpoint
- You MUST register:
aws bedrock-agentcore-control create-registry-record --registry-id <registry-id> --name <name> --descriptor-type <MCP|A2A|CUSTOM|AGENT_SKILLS> --description "<desc>" - Tags and capabilities metadata improve discoverability
Searching and Discovery
- CLI:
aws bedrock-agentcore-control list-registry-records --registry-id <registry-id> - MCP endpoint: programmatic discovery via MCP protocol
- Filter by resource type, tags, capabilities
Available Regions
Verify availability: aws bedrock-agentcore-control list-registry-records --registry-id <registry-id> --region <region>. Registry is a Preview feature — region availability is expanding.
Evaluations Service
Automated agent quality assessment using LLM-as-a-Judge.
Setup Workflow
Evaluation Setup:
- [ ] Step 1: Instrument agent with OTEL (see [memory & observability](agentcore-memory-observability.md))
- [ ] Step 2: Create evaluators (built-in or custom)
- [ ] Step 3: Configure online evaluation (sampling rate, data source)
- [ ] Step 4: Monitor scores in CloudWatchBuilt-in Evaluators
| Evaluator | What It Measures |
|---|---|
Builtin.Helpfulness | Does the response help the user? |
Builtin.Faithfulness | Is the response grounded in provided context? |
Builtin.Harmfulness | Does the response contain harmful content? |
Refer to the latest AWS documentation on AgentCore Evaluations built-in evaluators for the full current list.
Custom Evaluators
Define your own evaluation criteria:
- Rubric: what constitutes a good/bad response for your use case
- Scoring scale: numeric (1-5) or binary (pass/fail)
- Custom prompt template: the LLM-as-a-Judge prompt
Create custom evaluators: aws bedrock-agentcore-control create-evaluator --evaluator-name <name> --level <TOOL_CALL|TRACE|SESSION> --evaluator-config '{"llmAsAJudge":{"instructions":"<criteria>","ratingScale":{"numerical":[{"value":1,"description":"Poor"},{"value":5,"description":"Excellent"}]}}}'
Online vs On-Demand Evaluation
| Type | When | Use For |
|---|---|---|
| Online | Continuous, samples production traffic | Monitoring quality over time |
| On-demand | Batch, against a test dataset | Regression testing, A/B comparison |
Online evaluation constraints:
- Configure sampling rate — evaluating every invocation is expensive (each evaluation is a model invocation)
- Start with 5-10% sampling, increase if quality issues detected
- Data source: which OTEL traces to evaluate
Monitoring Scores
- Evaluation scores publish to CloudWatch automatically
- Create alarms for quality degradation: score drops below threshold
- Investigate low-scoring sessions: trace → evaluation result → root cause
- Create quality alarms — first discover the exact namespace (CloudWatch namespaces are case-sensitive):
1. aws cloudwatch list-metrics --namespace "Bedrock-AgentCore" — if no results, try --namespace "Bedrock-Agentcore" 2. Use the namespace that returns metrics in subsequent commands:
aws cloudwatch put-metric-alarm --alarm-name <name> --metric-name <metric> --namespace "<discovered-namespace>" --statistic Average --period 300 --threshold <value> --comparison-operator LessThanThreshold --evaluation-periods 3 --alarm-actions "<sns-topic-arn>"
Security Considerations
Registry access control:
- You MUST use least-privilege IAM policies — separate read (
list-registry-records) from write (create-registry-record) permissions. Avoidbedrock-agentcore:* - You MUST use IAM roles (not IAM users) for programmatic registry access
- You SHOULD add
aws:SourceArnandaws:SourceAccountconditions to resource policies on registry resources - You MUST restrict auto-approve governance mode to isolated development accounts — use manual approval in shared or production environments
Evaluation data protection:
- OTEL traces sent to evaluations contain user queries, agent responses, and tool call parameters — these may include PII
- You MUST ensure OTEL trace data is encrypted in transit (TLS) and at rest
- You SHOULD implement PII scrubbing in OTEL instrumentation before traces reach the evaluation service
- You MUST restrict access to evaluation results to authorized personnel only
- Encrypt CloudWatch log groups storing evaluation results with KMS
Monitoring security:
- You MUST encrypt SNS topics used for alarm actions with KMS
- You MUST validate that SNS topic subscribers are authorized to receive evaluation data
- You MUST enable CloudTrail for all
bedrock-agentcore-controlAPI calls — tracks who registered resources, who approved/rejected records, and who modified evaluations
- Refer to the latest AWS documentation on Bedrock AgentCore security best practices.
AgentCore Runtime — Container Build Procedure
Table of Contents
- Overview
- Parameters
- Steps: Verify Protocol, Write Dockerfile, Write Application Entry Point, Build and Push to ECR, Verify Image
- Security Considerations
Overview
Deterministic procedure for building an ARM64 container image that meets AgentCore Runtime's container contract and pushing it to ECR. Each protocol has a different container contract — you MUST select the protocol before building.
Parameters
- protocol (required):
http|mcp|a2a|ag-ui— see runtime reference for selection guide - framework (optional):
fastapi|express|flask|custom - ecr_repo (required): ECR repository URI
Constraints for parameter acquisition:
- You MUST ask for all required parameters (
protocol,ecr_repo) upfront in a single prompt - You MUST confirm successful acquisition before proceeding to Step 1
- You SHOULD ask about the optional
frameworkparameter in the same prompt
Steps
General constraints:
- You MUST present an overview of the steps before starting
- You MUST explain to the user what step is being executed and why before running each command
- You MUST respect the user's decision to abort at any point
- You MUST confirm the protocol choice before building the container (changing protocol requires rebuilding)
1. Verify Protocol and Container Contract
Constraints:
- You MUST verify Docker is available and supports buildx for ARM64 builds:
docker buildx version - You MUST verify the AWS CLI is available for ECR authentication:
aws --version - You MUST inform the user about any missing tools and ask if they want to proceed
- You MUST confirm the protocol with the user before writing the Dockerfile
- Each protocol has a different contract:
| Protocol | Health Endpoint | Port | Key Requirement |
|---|---|---|---|
| HTTP | /health | 8080 | JSON request/response |
| MCP | /mcp | 8080 | Streamable HTTP transport, tool registration |
| A2A | /.well-known/agent.json | 8080 | Agent Card discovery, task management |
| AG-UI | /ping | 8080 | SSE event stream via /invocations, health via /ping |
- You MUST NOT mix protocol contracts — an HTTP health check won't work for MCP
2. Write Dockerfile
Constraints:
- You MUST use ARM64 base image — AgentCore runs on Graviton. x86 images will fail to start.
- You MUST use multi-stage build to minimize image size
- You MUST expose the correct port (default 8080)
- You SHOULD use Python 3.12+ slim or Node.js 20+ slim as base
Example Dockerfile (HTTP/FastAPI):
FROM --platform=linux/arm64 python:3.12.4-slim AS builder
WORKDIR /app
RUN python -m venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
FROM --platform=linux/arm64 python:3.12.4-slim
RUN useradd -r -u 1001 appuser
WORKDIR /app
COPY --from=builder /app /app
ENV PATH="/app/.venv/bin:$PATH"
USER appuser
EXPOSE 8080
# Binds to 0.0.0.0 for AgentCore internal routing. Do NOT expose directly to the internet.
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]3. Write Application Entry Point
Constraints:
- You MUST implement the health check endpoint for the selected protocol
- You MUST handle SIGTERM for graceful shutdown
- You MUST read AgentCore environment variables (RUNTIME_ID, AWS_REGION)
- You MUST log to stdout/stderr (AgentCore routes to CloudWatch)
HTTP (FastAPI) example:
Note: These examples omit authentication because AgentCore handles auth at the platform layer. If running outside AgentCore (e.g., local testing), you MUST add authentication middleware before exposing to any network.
from fastapi import FastAPI
import signal, sys
app = FastAPI()
@app.get("/health")
async def health():
return {"status": "healthy"}
@app.post("/invoke")
async def invoke(request: dict):
# Agent logic here
return {"response": "..."}
def shutdown(sig, frame):
sys.exit(0)
signal.signal(signal.SIGTERM, shutdown)MCP example:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-agent")
@mcp.tool()
def my_tool(query: str) -> str:
"""Tool description for discovery."""
return "result"
# Runs on /mcp with Streamable HTTP transport
mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)Note: This minimal example omits SIGTERM handling for brevity. You MUST add graceful shutdown handling (see the HTTP example above) before deploying to AgentCore.
A2A example (minimal contract):
from fastapi import FastAPI
app = FastAPI()
# Agent Card discovery endpoint — REQUIRED for A2A protocol
@app.get("/.well-known/agent.json")
async def agent_card():
return {
"name": "my-agent",
"description": "Agent description",
"capabilities": ["task_execution"],
"endpoint": "http://localhost:8080", # Replace with AgentCore-assigned URL at deployment
}
@app.post("/tasks")
async def create_task(request: dict):
# Task execution logic
return {"taskId": "...", "status": "completed", "result": "..."}Note: This minimal example omits SIGTERM handling for brevity. You MUST add graceful shutdown handling (see the HTTP example above) before deploying to AgentCore.
AG-UI example (minimal contract):
from fastapi import FastAPI
from fastapi.responses import StreamingResponse, JSONResponse
import json
app = FastAPI()
@app.get("/ping")
async def ping():
return JSONResponse({"status": "Healthy"})
@app.post("/invocations")
async def invocations(request: dict):
async def event_stream():
yield f"data: {json.dumps({'type': 'RUN_STARTED', 'threadId': 'thread-1', 'runId': 'run-1'})}\n\n"
yield f"data: {json.dumps({'type': 'TEXT_MESSAGE_CONTENT', 'messageId': 'msg-1', 'delta': 'response'})}\n\n"
yield f"data: {json.dumps({'type': 'RUN_FINISHED', 'threadId': 'thread-1', 'runId': 'run-1'})}\n\n"
return StreamingResponse(event_stream(), media_type="text/event-stream")Note: This minimal example omits SIGTERM handling for brevity. You MUST add graceful shutdown handling (see the HTTP example above) before deploying to AgentCore.
Refer to the latest AWS documentation on AgentCore A2A protocol and AG-UI protocol for current full specifications — these protocols are evolving and the full contract may have changed.
4. Build and Push to ECR
Constraints:
- You MUST build for ARM64:
docker buildx build --platform linux/arm64 --load -t <tag> . - You MUST authenticate to ECR before pushing:
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com- You MUST tag with both
latestand a version tag for rollback:
docker tag <image> <ecr_repo>:latest
docker tag <image> <ecr_repo>:v1.0.0
docker push <ecr_repo>:latest
docker push <ecr_repo>:v1.0.05. Verify Image
Constraints:
- You MUST verify the image architecture is ARM64:
docker inspect <image> | grep Architecture- You SHOULD test locally before deploying to AgentCore:
docker run --platform linux/arm64 -p 8080:8080 <image>
# Use the health endpoint for your protocol:
# HTTP: /health | MCP: /mcp | A2A: /.well-known/agent.json | AG-UI: /ping
curl http://localhost:8080/<health-endpoint>- If health check fails locally, it will fail on AgentCore — fix before deploying
Security Considerations
Authentication and network exposure:
- AgentCore authenticates requests at the platform layer before they reach your container — the code examples omit auth because AgentCore handles it
- You MUST NOT expose this container directly to the internet without adding your own authentication layer
- For local testing, bind to
127.0.0.1instead of0.0.0.0to prevent network exposure:uvicorn main:app --host 127.0.0.1 --port 8080 - The Dockerfile uses
--host 0.0.0.0because AgentCore routes traffic to the container internally — do NOT expose port 8080 directly
Transport security:
- AgentCore terminates TLS at the load balancer — your container receives plaintext HTTP on port 8080 over the internal network
- You MUST NOT expose port 8080 directly to the internet — all external traffic must route through AgentCore
- If deploying outside AgentCore, you MUST configure TLS (use ACM for certificate management)
Input validation:
- You MUST validate and sanitize all input before processing — use Pydantic models or equivalent schema validation
- You MUST set maximum request body size limits to prevent denial-of-service
- You MUST handle malformed input gracefully with appropriate error responses
- You SHOULD include security headers in HTTP responses:
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Cache-Control: no-store
Container image security:
- You MUST NOT bake secrets, API keys, or credentials into the Docker image — use Secrets Manager at runtime for secrets; use environment variables only for non-sensitive configuration (RUNTIME_ID, AWS_REGION)
- You MUST run the container as a non-root user (the example Dockerfile uses
USER appuser— do not remove this) - You MUST use multi-stage builds to exclude build-time dependencies (compilers, pip cache, dev packages) from the final image
- You SHOULD pin base image versions (e.g.,
python:3.12.4-slimnotpython:3.12-slim) to avoid supply chain attacks from tag mutation - You SHOULD enable ECR image scanning:
aws ecr put-image-scanning-configuration --repository-name <repo> --image-scanning-configuration scanOnPush=true
ECR access control:
- Scope ECR push permissions to the specific repository ARN — avoid
ecr:*onResource: "*" - The ECR login token from
get-login-passwordis ephemeral (12 hours) — do not store or share it - You MUST NOT log the ECR login token in agent output
Runtime security:
- AgentCore injects credentials via environment variables (AWS_ACCESS_KEY_ID, etc.) — do not override these
- Log to stdout/stderr only — AgentCore routes to CloudWatch with encryption
- You MUST NOT log request or response bodies that may contain PII or sensitive model inputs/outputs
- Handle SIGTERM for graceful shutdown to avoid data loss during scaling events
- Enable CloudTrail logging for ECR API calls to audit image push/pull activity
- Refer to the latest AWS documentation on ECR security best practices and Bedrock security best practices
AgentCore Runtime — Protocol Selection & Deployment
Table of Contents
- Protocol Selection Guide
- Container Contract
- Deployment Workflow
- Agent Lifecycle Models
- Scaling
- Security Considerations
Protocol Selection Guide
AgentCore Runtime supports 4 protocols. You MUST select before building the container — each has a different contract.
| Protocol | Container Contract | Best For |
|---|---|---|
| HTTP | Health: /health, Port: 8080, JSON req/res | Existing web frameworks (FastAPI, Express, Flask). Simple request-response agents. |
| MCP | Endpoint: /mcp, Streamable HTTP transport | Tool-centric agents exposing capabilities as MCP tools. MCP ecosystem integration. |
| A2A | Agent Card: /.well-known/agent.json, task endpoints | Multi-agent systems with direct agent-to-agent communication. |
| AG-UI | Health: /ping, Event stream: /invocations, Port: 8080, SSE standard event types | Frontend-connected agents with real-time UI updates. Chat interfaces. |
Decision guide:
| Question | Answer → Protocol |
|---|---|
| Existing REST API or web framework? | HTTP |
| Agent provides tools to other agents? | MCP |
| Agents communicate directly with each other? | A2A |
| Agent streams results to a UI? | AG-UI |
| Not sure? | Start with HTTP — simplest, most familiar |
Refer to the latest AWS documentation on AgentCore Runtime protocols for current specifications.
Container Contract
Requirements that apply to ALL protocols:
| Requirement | Detail |
|---|---|
| Architecture | ARM64 (Graviton) — x86 images WILL NOT START |
| Health check | Protocol-specific endpoint (see table above) |
| Port | Default 8080, configurable |
| Startup | Must signal readiness within timeout |
| Logging | stdout/stderr → CloudWatch automatically |
| Shutdown | Handle SIGTERM for graceful shutdown |
| Environment | AgentCore provides: RUNTIME_ID, AWS_REGION, credentials |
See container build procedure for the full build workflow with Dockerfile examples.
Deployment Workflow
Deployment Progress:
- [ ] Step 1: Select protocol (see guide above)
- [ ] Step 2: Build ARM64 container — see [container build procedure](agentcore-runtime-container-build.md)
- [ ] Step 3: Push to ECR
- [ ] Step 4: Create Runtime: `aws bedrock-agentcore-control create-agent-runtime --agent-runtime-name <name> --agent-runtime-artifact '{"containerConfiguration":{"containerUri":"<ecr-uri>"}}' --role-arn <role-arn> --network-configuration '...' --authorizer-configuration '...' --protocol-configuration '{"serverProtocol":"<PROTOCOL>"}'` — where `<PROTOCOL>` is `HTTP`, `MCP`, `A2A`, or `AGUI` matching your Step 1 selection (note: AG-UI in the selection guide maps to API value `AGUI`). For `--network-configuration` and `--authorizer-configuration`, see the Security Considerations section below.
- [ ] Step 5: Create Runtime Endpoint: `aws bedrock-agentcore-control create-agent-runtime-endpoint --agent-runtime-id <id-from-step-4> --name <endpoint-name>`
- [ ] Step 6: Wait for endpoint status `READY` — the runtime is not invocable until the endpoint is active
- [ ] Step 7: Verify health check passes: `aws bedrock-agentcore-control get-agent-runtime-endpoint --agent-runtime-id <id> --endpoint-id <endpoint-id>` — confirm status is `READY` and health check is passingConstraints:
- You MUST select the protocol BEFORE building the container (Step 1 before Step 2)
- You MUST use ARM64 architecture — see container build procedure
- You MUST create the endpoint (Step 5) after the runtime (Step 4) — without an endpoint, the runtime cannot receive traffic
- You MUST verify health check passes after deployment
- For updates: use rolling update (default) or blue/green via alias switching
- For rollback: deploy previous container image version
Agent Lifecycle Models
| Model | State | Memory Service | Use When |
|---|---|---|---|
| Per-request | Stateless — new instance per request | Not needed | Simple Q&A, stateless tools |
| Per-session | Stateful — persists across requests in session | Required | Multi-turn chat, context accumulation |
Per-session agents use the Memory service for state persistence. See memory & observability.
Scaling
- Auto-scaling based on invocation count, latency, or custom metrics
- Configure min/max instances in Runtime configuration
- Cold start: first request to a new instance has higher latency
- For predictable high-volume: consider provisioned capacity
- Refer to the latest AWS documentation on AgentCore Runtime scaling for current configuration options
Security Considerations
IAM and access control:
- The
--role-arnincreate-agent-runtimedefines what AWS resources the agent can access — scope to least-privilege permissions - You MUST use IAM roles (not IAM users) for the runtime execution role
- Include
aws:SourceArnandaws:SourceAccountconditions in the execution role trust policy to prevent confused deputy - Separate runtime roles per agent — do not share a single role across multiple agents with different access needs
Network security:
- AgentCore terminates TLS at the load balancer — containers receive plaintext HTTP internally
- You MUST NOT expose container ports directly to the internet — all traffic must route through AgentCore
- Use VPC configuration in
--network-configurationto restrict network access to required resources only - You SHOULD use VPC mode (
"networkMode":"VPC") for production workloads — PUBLIC mode exposes the endpoint to the internet and should only be used for development/testing in isolated accounts
Authentication:
- Configure
--authorizer-configurationto require authentication for inbound requests - You MUST NOT deploy production runtimes without an authorizer — unauthenticated endpoints are a security risk
Secrets and environment variables:
- You MUST NOT put secrets, API keys, or credentials in
--environment-variables— these are visible in the runtime configuration viaget-agent-runtime - Use AWS Secrets Manager for secrets and reference them at runtime from your agent code
- Use
--environment-variablesonly for non-sensitive configuration (feature flags, region overrides, log levels)
Logging and sensitive data:
- Agent runtimes log request and response payloads to CloudWatch automatically — these may contain PII
- You MUST encrypt the CloudWatch log group with a KMS key: configure
kms-key-idon the/aws/bedrock-agentcore/runtimes/<agent-id>log group - Configure CloudWatch Logs retention limits — do not retain logs indefinitely
- You MUST NOT log secrets or credentials in agent output
Monitoring:
- Enable CloudTrail for all
bedrock-agentcore-controlAPI calls to audit runtime creation, updates, and deletions - Monitor runtime health via CloudWatch metrics — first discover the exact namespace (CloudWatch namespaces are case-sensitive):
1. aws cloudwatch list-metrics --namespace "Bedrock-AgentCore" — if no results, try --namespace "Bedrock-Agentcore" 2. Use the namespace that returns metrics in all subsequent alarm and query commands
- Configure alarms for error rates and latency degradation
- Refer to the latest AWS documentation on Bedrock AgentCore security best practices
Bedrock Cost Attribution and Tracking
Track, allocate, and manage Bedrock inference costs across teams, products, and models. Bedrock charges per input/output token with model-specific rates.
Table of Contents
- Cost Attribution Approaches
- Application Inference Profiles
- IAM Principal-Based Attribution
- CloudWatch Usage Monitoring
- Budget Alerts
Cost Attribution Approaches
| Approach | Best For | Setup Effort |
|---|---|---|
| Application inference profiles + cost allocation tags | Per-product or per-team cost tracking in Cost Explorer | Medium — create profiles, tag, activate in Billing |
| IAM principal-based (CUR 2.0) | Per-developer or per-role attribution | Low — automatic in CUR 2.0, no Bedrock config needed |
| Model invocation logging + custom analytics | Fine-grained per-request analysis (token counts, latency, model) | High — enable logging, build queries |
For most teams, application inference profiles with cost allocation tags is the recommended approach. It provides clean cost breakdowns in Cost Explorer without custom analytics.
Application Inference Profiles
Setup Workflow
1. Create an Application Inference Profile
aws bedrock create-inference-profile \
--inference-profile-name "<TEAM_OR_PRODUCT_NAME>" \
--model-source "copyFrom=arn:aws:bedrock:<REGION>::foundation-model/<MODEL_ID>" \
--region <REGION> --profile <PROFILE>Note the returned inferenceProfileArn.
2. Tag the Profile
aws bedrock tag-resource \
--resource-arn <INFERENCE_PROFILE_ARN> \
--tags key=CostCenter,value=<COST_CENTER> key=Project,value=<PROJECT> \
--region <REGION> --profile <PROFILE>3. Activate Cost Allocation Tags
In the AWS Billing console (or via API), activate the tags as cost allocation tags. Tags take ~24 hours to appear in Cost Explorer after activation.
4. Use the Profile for Inference
Replace the base model ID with the inference profile ARN in application code:
response = bedrock_runtime.converse(
modelId="<INFERENCE_PROFILE_ARN>",
messages=[...],
inferenceConfig={"maxTokens": 1024}
)5. Verify in Cost Explorer
After 24–48 hours, filter Cost Explorer by the tag keys. Bedrock costs appear under Amazon Bedrock service, grouped by tag values.
IAM Principal-Based Attribution
CUR 2.0 automatically records the IAM caller identity for every Bedrock API call. No Bedrock-specific setup required.
To use: tag IAM roles/users with keys like department, costCenter, or project, then filter CUR 2.0 data by those tags. Works for per-developer tracking when each developer assumes a distinct IAM role.
Limitation: only tracks who made the call, not which product or feature triggered it. Use inference profiles for product-level attribution.
CloudWatch Usage Monitoring
Key metrics for cost monitoring (namespace AWS/Bedrock, dimension ModelId):
| Metric | Cost Signal |
|---|---|
InputTokenCount | Input token spend (charged per token) |
OutputTokenCount | Output token spend (higher per-token rate) |
InvocationCount | Request volume |
CacheReadInputTokens | Tokens served from cache (90% cheaper than standard input) |
CacheWriteInputTokens | Cache write tokens (25% surcharge over standard input) |
Cost Analysis Script
python3 scripts/analyze-bedrock-costs.py --days <DAYS> --region <REGION> --profile <PROFILE>The script queries Cost Explorer for Bedrock spend grouped by usage type (model + token direction) over the specified period.
Budget Alerts
Set up AWS Budgets to alert when Bedrock spend approaches a threshold:
aws budgets create-budget --account-id <ACCOUNT_ID> \
--budget '{"BudgetName":"bedrock-monthly","BudgetLimit":{"Amount":"<AMOUNT>","Unit":"USD"},"TimeUnit":"MONTHLY","BudgetType":"COST","CostFilters":{"Service":["Amazon Bedrock"]}}' \
--notifications-with-subscribers '[{"Notification":{"NotificationType":"ACTUAL","ComparisonOperator":"GREATER_THAN","Threshold":80},"Subscribers":[{"SubscriptionType":"EMAIL","Address":"<EMAIL>"}]}]' \
--profile <PROFILE>This alerts at 80% of the monthly budget. Adjust threshold and notification targets as needed.
Knowledge Bases — Retrieval & Query Reference
Table of Contents
- Query API Decision Table
- Metadata Filtering Syntax
- Retrieval Configuration
- Session Management
- Generation Configuration
- Security Considerations
Query API Decision Table
Three APIs — agents pick the wrong one. Use this table:
| Use Case | API | Endpoint | When |
|---|---|---|---|
| Synthesize answer from docs | RetrieveAndGenerate | bedrock-agent-runtime | Most common RAG pattern. Model reads chunks and generates answer with citations. |
| Get raw chunks for custom processing | Retrieve | bedrock-agent-runtime | You want to rank, filter, or feed chunks to a different model. |
| Full prompt control | Converse with manual context | bedrock-runtime | You retrieve chunks yourself, build a custom prompt, and call the model directly. |
Most common pattern: aws bedrock-agent-runtime retrieve-and-generate --input '{"text":"<query>"}' --retrieve-and-generate-configuration '{"type":"KNOWLEDGE_BASE","knowledgeBaseConfiguration":{"knowledgeBaseId":"<kb-id>","modelArn":"<model-arn>"}}'
Input limit: The --input text field has a maximum of 1000 characters. Exceeding this causes a ValidationException. For longer queries, truncate or summarize before sending.
Metadata Filtering Syntax
Bedrock-specific filter syntax — not in model training data. Filters narrow retrieval to relevant documents before semantic search.
Operators:
| Operator | Type | Example |
|---|---|---|
equals | Exact match | {"equals": {"key": "department", "value": "engineering"}} |
notEquals | Exclude | {"notEquals": {"key": "status", "value": "archived"}} |
greaterThan | Number | {"greaterThan": {"key": "year", "value": 2024}} |
greaterThanOrEquals | Number (inclusive) | {"greaterThanOrEquals": {"key": "year", "value": 2024}} |
lessThan | Number | {"lessThan": {"key": "year", "value": 2026}} |
lessThanOrEquals | Number (inclusive) | {"lessThanOrEquals": {"key": "year", "value": 2026}} |
in | Match any in list | {"in": {"key": "category", "value": ["guide", "tutorial"]}} |
notIn | Exclude list | {"notIn": {"key": "type", "value": ["draft", "deprecated"]}} |
startsWith | Prefix match (string) | {"startsWith": {"key": "path", "value": "/docs/api"}} |
stringContains | Substring (string) | {"stringContains": {"key": "title", "value": "setup"}} |
listContains | List attribute contains value (string) | {"listContains": {"key": "tags", "value": "security"}} |
Vector store limitations for operators: startsWith and stringContains are currently best supported with Amazon OpenSearch Serverless vector stores. Neptune Analytics GraphRAG supports the stringContains string variant but not the list variant. listContains is currently best supported with Amazon OpenSearch Serverless. S3 vector buckets do NOT support startsWith or stringContains. If you use these operators with an unsupported vector store, the filter is silently ignored.
Refer to the latest AWS documentation on Bedrock Knowledge Base RetrievalFilter for the full current operator list.
Combining filters:
{
"andAll": [
{"equals": {"key": "department", "value": "engineering"}},
{"greaterThan": {"key": "epoch_modification_time", "value": 1704067200}}
]
}{
"orAll": [
{"equals": {"key": "type", "value": "guide"}},
{"equals": {"key": "type", "value": "tutorial"}}
]
}Constraints:
- Metadata attributes MUST be defined during KB creation or data source configuration — you cannot filter on attributes that weren't declared as filterable
- You MUST verify that the user's KB has metadata configured before constructing filter queries — filtering on undeclared attributes silently returns no results
- For KBs with >1000 documents, You SHOULD recommend metadata filtering for retrieval quality
- Security use case: Metadata filtering can enforce document-level access control — assign role/permission metadata attributes (e.g.,
access_level: "admin") during ingestion, then filter at query time based on the calling user's role to restrict which documents they can retrieve
Retrieval Configuration
Non-obvious defaults agents get wrong:
| Parameter | Default | Guidance |
|---|---|---|
overrideSearchType | Not set (Bedrock decides) | When omitted, Bedrock automatically selects the search strategy best suited for your vector store configuration. For OpenSearch Serverless, RDS (including Aurora PostgreSQL), or MongoDB Atlas with a filterable text field, you can explicitly set to HYBRID (keyword + semantic) or SEMANTIC (vector only). For all other vector stores, only SEMANTIC is available. Consider HYBRID when supported for keyword-heavy queries. |
numberOfResults | 5 | Increase for broad questions (10-20), decrease for specific lookups (3-5). More results = higher latency. |
Score confidence threshold: Set to filter low-relevance results.
- Too high → no results returned (common failure)
- Too low → noisy, irrelevant results
- Start with 0.5, tune based on evaluation
- Refer to the latest AWS documentation on Bedrock Knowledge Base retrieval configuration for current options
Session Management
For multi-turn RAG conversations:
Constraints:
- You MUST pass
sessionIdinRetrieveAndGeneratecalls for multi-turn conversations — omitting it causes each query to be independent, silently losing all conversation context - You MUST NOT generate or set
sessionIdyourself — Amazon Bedrock auto-generates it on the first request; reuse the returned value for subsequent turns - For HIPAA/GDPR workloads, You MUST encrypt session data with a customer-managed KMS key via
--session-configuration '{"kmsKeyArn":"<kms-key-arn>"}'— session data includes conversation history which may contain sensitive retrieved content
- Context from previous turns carries forward automatically when
sessionIdis passed - Sessions expire after a timeout — start a new session if expired
Generation Configuration
For RetrieveAndGenerate only:
- Model selection: Specify which model generates the answer (can differ from the embedding model — this is NOT a mismatch, despite what agents assume)
- Prompt template: Override the default RAG prompt to customize how the model uses retrieved chunks
- Guardrail integration: Apply guardrails to the generated response via
guardrailConfiguration - Refer to the latest AWS documentation on Bedrock RetrieveAndGenerate configuration for current options
Security Considerations
These are retrieval-specific security controls. For general Bedrock security, see the parent skill's Security Considerations section.
Sensitive data in retrieved chunks
Retrieved chunks are the primary vector for sensitive data exposure in RAG applications. If source documents contain PII/PHI and are not sanitized before ingestion, that sensitive data will be retrieved from the vector store and can leak to users.
Key risks:
- Retrieved chunks appear in the API response
citations[].retrievedReferences[].content.textfield — this raw text may contain PII even if the generated response is sanitized by guardrails - Guardrails are applied to the input (the augmented prompt, which includes retrieved chunks) and the generated response — but they are NOT applied to the raw
retrievedReferencesreturned in the API response at runtime - Application logging that captures the full API response will log sensitive chunk content
Mitigations:
- Redact or mask PII/PHI from source documents before ingestion into the knowledge base
- Use metadata filtering for document-level access control (see Metadata Filtering section above)
- Apply guardrails to filter sensitive content in the generated response
- Do not log the full
retrievedReferencescontent in application logs for PII-sensitive workloads
Audit retrieval calls with CloudTrail
Retrieve and RetrieveAndGenerate calls are logged as CloudTrail data events (not management events — they are not logged by default). To enable auditing of who queried what from the knowledge base, configure advanced event selectors with resource type AWS::Bedrock::KnowledgeBase. Refer to the latest AWS documentation on Bedrock CloudTrail logging.
Related skills
How it compares
Use amazon-bedrock when the deployment target is AWS-native Bedrock services with IAM, Knowledge Bases, and AgentCore rather than a framework-agnostic LLM SDK alone.
FAQ
Converse API or InvokeModel?
Prefer Converse API for unified cross-model requests; use InvokeModel only for rare provider-specific features.
Why set maxTokens explicitly?
Unset maxTokens reserves the model maximum quota and commonly causes unexpected ThrottlingException.
How does Guardrails handle PII?
Masking applies to API responses only; original PII may still appear in CloudWatch Logs without KMS encryption and IAM restrictions.
Is Amazon Bedrock safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.