Subchapter 23.7
references/agentcore-payments.mdMarkdown19 KBView on GitHub
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.
Assets
Kb Shim PyDo NOT use for:
$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 connectorRead the agent’s entrypoint file (e.g., main.py, app.py). Detect the framework:
from strands import Agent → Strandsfrom langgraph or from langchain → LangGraphfrom agents import Agent → OpenAI Agents SDKCase 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.
Before setting up payments, collect these inputs:
Which payment provider? — Coinbase CDP or Stripe Privy
Which AWS region? — must be one of: us-east-1, us-west-2, eu-central-1, ap-southeast-2
AWS account ID — the account where resources will be created
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 sessionIn 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:InvokeModel or bedrock:InvokeModelWithResponseStream — if using Bedrock modelsVerify credentials are active: aws sts get-caller-identity
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/ (opens in a new tab)):
How to get these credentials:
# .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/ (opens in a new tab)):
How to get these credentials:
wallet-auth: — strip this prefix, use only the raw base64 content# .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 MUST strip this prefix. Only the raw base64 content (starting withMIGHAgEA...) 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.paymentsfile is for local development only.
Read setup-script.md for the full script template. Substitute the developer’s inputs and execute it.
The script creates:
Read wiring.md for framework-specific tool code. Use the pattern matching the detected framework from Step 1.
The x402_fetch tool:
payment-required headerProcessPayment to get a signed payment proofX-PAYMENT for v1, PAYMENT-SIGNATURE for v2) using a fresh HTTP client to avoid cookie contaminationSet 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_fetchtool detects the version from the challenge and sends aPAYMENT-SIGNATUREheader with 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:
x402_fetch with the URLPAYMENT-SIGNATURE header (v2 endpoint) → gets 200If 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)expiryTimeInMinutes and per-session budget controls to prevent runaway payments.bedrock-agentcore API calls, especially ProcessPayment. For production, set up a CloudWatch alarm for failed payment attempts as a potential abuse indicator.x402_fetch tool enforces HTTPS-only and blocks private IP ranges to prevent fetching internal endpoints.x402_fetch tool rejects non-HTTPS URLs.For comprehensive security guidance, see the AgentCore Security documentation (opens in a new tab).
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 agentTwo 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/ (opens in a new tab).
Agent sees 402 but does not pay:
PAYMENT_MANAGER_ARN env var is set and not Nonex402_fetch tool (not a generic http_request)x402Version + accepts fields) or the payment-required headerProcessPayment fails with “Failed to obtain resource payment token”:
GetResourcePaymentToken on the token-vault and secretsmanager:GetSecretValue on the secrets.ProcessPayment fails with “Failed to obtain workload access token”:
GetWorkloadAccessToken permission on the workload-identity-directory resources.ProcessPayment fails with “Failed to assume payment execution role”:
bedrock-agentcore.amazonaws.com with the correct aws:SourceAccount condition.ProcessPayment succeeds but merchant still returns 402:
httpx.Client(cookies=None).request(...) — do NOT reuse the same client/session.X-PAYMENT header with a flat proof (top-level scheme/network); v2 expects a PAYMENT-SIGNATURE header where accepted is a top-level sibling of payload, and payload holds only signature + authorization (no top-level scheme/network). A v2 merchant that receives a v1 X-PAYMENT header ignores it and re-issues the same 402 — often with an empty {} body and no error, which is hard to diagnose. Read x402Version from the challenge (body or payment-required header) and build the matching proof.network must 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: the ProcessPayment input always uses CAIP-2 regardless of version — only the proof presented to the merchant differs.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:
X-PAYMENT header. Detect the version from the challenge (x402Version: 2, present in the body or the payment-required response header) and send a PAYMENT-SIGNATURE header. The v2 proof puts accepted (the full requirements, CAIP-2 network) as a top-level sibling of payload, with payload containing only signature + authorization. Note: if ProcessPayment returns PROOF_GENERATED and 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”:
paymentManagerArn in the session creation matches the one used in ProcessPayment.ProcessPayment fails with “PaymentSessionExpired”:
expiryTimeInMinutes.ProcessPayment fails with “Payment instrument not found” or “does not belong to user”:
userId passed to ProcessPayment matches the userId used when the instrument was created.ProcessPayment fails with “Payment connector is not active”:
ProcessPayment fails with “Network mismatch”:
network: "ETHEREUM" support Base, Base Sepolia, and Ethereum chains.network: "SOLANA" support Solana and Solana Devnet chains.ProcessPayment fails with “Payment asset not supported USDC token address”:
0x036CbD53842c5426634e7929541eC2318f3dCF7eProcessPayment fails with “Wallet does not have a USDC balance”:
Coinbase: “Delegated signing grant is not active”:
redirectUrl returned during instrument creation (Coinbase Hub).Coinbase: “Delegated signing is not enabled”:
Stripe Privy: “Privy credentials are invalid”:
Stripe Privy: “Privy appId is invalid or missing”:
appId in the credential provider configuration is incorrect.Stripe Privy: “Privy signing key is invalid or expired”:
wallet-auth: prefix from the private key.Stripe Privy: “Wallet policy denied the transaction”:
Stripe Privy: “The linked account data is invalid”:
linkedAccounts when creating the instrument is malformed.Stripe Privy: “Rate limited by Privy”:
ProcessPayment fails with “Payment amount exceeds maximum”:
ProcessPayment fails with “Rate exceeded”:
Coinbase: “Delegation not completed”:
redirectUrl returned during instrument creation, log in, and grant permissions.Stripe Privy: “Delegation not completed”: