Subchapter 21.4
references/setup.mdMarkdown13 KBView on GitHub
Full provisioning walkthrough. A human runs every step here except the final wiring. See the tool inventory in SKILL.md for what the agent can call.
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
npm install -g @aws/agentcore
agentcore --version
python -c "from bedrock_agentcore.payments import PaymentManager; print('payments OK')"If that import fails, install a bedrock-agentcore release with payments
support before continuing. Keep the virtual environment active for the Python
commands below.
requirements.txt pins the complete Python runtime graph used by this skill. For
integrity pinning in your own release process, generate platform-specific hashes
from that exact graph:
python -m pip download -r requirements.txt -d wheels/
python -m pip hash wheels/*Do this before running the connector command. Never paste these into a chat, an agent prompt, or a command flag.
Coinbase CDP — https://portal.cdp.coinbase.com/ (opens in a new tab)
Stripe Privy — https://dashboard.privy.io/ (opens in a new tab)
wallet-auth: prefix from the private key; keep the raw base64 (starts MIGHAgEA...)Leave the LLM conversation and run this in a separate terminal. Do not paste credentials, command output, deployed state, or generated IDs back into chat. Use the bare command so the whole setup stays in the interactive terminal:
agentcore add payment-managerNames: start with a letter, alphanumeric plus underscores, ≤48 characters.
Optionally tag the project so the service can distinguish skill-onboarded
resources — add to the top-level tags object in agentcore/agentcore.json,
keeping existing entries:
"tags": { "agentcore:onboarding-source": "agent-toolkit-skill" }Run it with no flags. The wizard prompts for each secret, so nothing lands in shell history or the process list:
agentcore add payment-connectorPassing --manager, --name, or --provider switches the CLI to
non-interactive mode and then requires every secret as a flag — it will not
fall back to prompting. Use the bare command.
The CLI writes provider secrets in plaintext to agentcore/.env.local and
uploads them to AgentCore Identity on deploy. Before deploying:
grep -q '^\.env\.local$\|^\.env\.\*$' .gitignore || echo '.env.local' >> .gitignore
chmod 600 agentcore/.env.localThe Python scaffold’s default .gitignore lists only .env, so .env.local
must be added. The agent must never read this file.
agentcore deployProvisions the manager and connector, plus a
Payment<Name>ProcessPaymentRole. IDs land in
agentcore/.cli/deployed-state.json under
targets.<target>.resources.payments[] (managerArn,
connectors[].connectorId, processPaymentRoleArn).
Follow the official guide: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-iam-roles.html (opens in a new tab)
AgentCore Payments defines four roles. This skill’s design assumes them, and the security properties below depend on the separation being real in your account.
| Role | Purpose | Who assumes it |
|---|---|---|
| ControlPlaneRole | Payment managers, connectors, credential providers | Administrator |
| ManagementRole | Payment instruments and sessions. Explicitly denied ProcessPayment | The human running this admin CLI |
| ProcessPaymentRole | Executes payments. Cannot create sessions | The agent runtime |
| ResourceRetrievalRole | Service role AgentCore assumes to fetch credentials | bedrock-agentcore.amazonaws.com |
The AWS guide states the reason directly:
“Separating payment management from payment execution prevents a single compromised identity from both creating sessions with unlimited budgets and executing payments against those sessions.”
This separation keeps payment management and execution in different trusted roles.
| Step | Command | Role |
|---|---|---|
| 1 | agentcore add payment-manager / payment-connector / deploy | ControlPlaneRole |
| 2 | agents_pay_admin.py init-config | none — writes a local file only |
| 3 | agents_pay_admin.py create-instrument | ManagementRole |
| 4 | agents_pay_admin.py new-session | ManagementRole |
| 6 | the agent calling x402_fetch | ProcessPaymentRole |
Note the explicit Deny. It is not decoration: it is what stops this role from
being usable to both mint budget and spend it.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPaymentManagement",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:CreatePaymentInstrument",
"bedrock-agentcore:GetPaymentInstrument",
"bedrock-agentcore:ListPaymentInstruments",
"bedrock-agentcore:DeletePaymentInstrument",
"bedrock-agentcore:CreatePaymentSession",
"bedrock-agentcore:GetPaymentSession",
"bedrock-agentcore:ListPaymentSessions",
"bedrock-agentcore:DeletePaymentSession"
],
"Resource": [
"arn:aws:bedrock-agentcore:*:ACCOUNT:payment-manager/*/instrument/*",
"arn:aws:bedrock-agentcore:*:ACCOUNT:payment-manager/*/session/*"
]
},
{
"Sid": "DenyProcessPayment",
"Effect": "Deny",
"Action": "bedrock-agentcore:ProcessPayment",
"Resource": "*"
}
]
}ProcessPayment plus reads. No session-write actions.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowProcessPayment",
"Effect": "Allow",
"Action": "bedrock-agentcore:ProcessPayment",
"Resource": ["arn:aws:bedrock-agentcore:*:ACCOUNT:payment-manager/*/session/*"]
},
{
"Sid": "AllowPaymentReadOperations",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:GetPaymentInstrument",
"bedrock-agentcore:GetPaymentInstrumentBalance",
"bedrock-agentcore:GetPaymentSession"
],
"Resource": [
"arn:aws:bedrock-agentcore:*:ACCOUNT:payment-manager/*/instrument/*",
"arn:aws:bedrock-agentcore:*:ACCOUNT:payment-manager/*/session/*"
]
}
]
}The AWS guide is emphatic about the combination to avoid:
“Do not include PaymentSession write permissions (for example,
CreatePaymentSession) andProcessPaymentin the same role, or the caller can bypass payment limits by creating new sessions with elevated budgets.”
Verify after deploy:
aws iam get-role-policy --role-name <ProcessPaymentRole> --policy-name <Policy> \
| grep -c CreatePaymentSession # must be 0A runtime AccessDeniedException on CreatePaymentSession is the control
working. Do not “fix” it by granting the permission.
The service role AgentCore assumes at runtime. Created and wired by
agentcore deploy; it is not assigned to a human. Its trust policy must name
bedrock-agentcore.amazonaws.com with an aws:SourceAccount condition. See the
guide for the full base and per-connector permissions.
python3 scripts/agents_pay_admin.py init-config \
--max-per-payment-usd 0.05 \
--network eip155:84532 \
--recipient 0xMerchantWalletAddressTo find a merchant’s real payTo and amount before allowlisting, read its
challenge without paying:
curl -sD - https://<merchant-host>/<path> -o /dev/null \
| awk '/^payment-required:/{print $2}' | base64 -d | python3 -m json.toolInspect accepts[].payTo, .amount (integer base units; USDC has 6 decimals,
so 2000 = $0.002), .asset, and .network, then allowlist deliberately with
--recipient. Unknown recipients are refused before signing.
If the operator deliberately accepts publisher-selected beneficiaries, replace
all --recipient flags with --allow-any-recipient. These modes are mutually
exclusive. The flag does not relax scheme, network, asset, origin/resource,
per-payment, or cumulative session limits.
Add --origin https://host (repeatable) only if you want to pin the agent to a
known merchant set; omitted, it may fetch any public HTTPS site.
One wallet for this installation. The payer identity is read from the config, where
init-config generated it — this skill is single-tenant, so there is nothing to
invent or keep in sync:
python3 scripts/agents_pay_admin.py create-instrument --email you@example.comThe manager ARN and connector ID are read from agentcore/.cli/deployed-state.json
automatically, so run this from your AgentCore project directory. Otherwise pass
--manager-arn and --connector-id explicitly.
Add --network-family SOLANA for Solana; the default ETHEREUM covers Base and
Base Sepolia.
Run it under the setup role, not the runtime role. It prints the instrument ID,
the wallet address, the delegation URL, and the export lines for the runtime.
One-time per wallet, and do this before creating a session.
Both steps attach to the wallet, not to a session: create_payment_session takes
only a user, an expiry, and a budget. Sessions are time-bounded (60 minutes by
default), so minting one first and then going off to complete a browser flow and a
faucet transfer simply burns the clock — the budget can expire before the agent has
spent anything.
Delegation — authorize the agent to spend from the wallet:
redirectUrl, signs in, grants
permission to the wallet addressFunding — send testnet USDC to the wallet address via https://faucet.circle.com/ (opens in a new tab) (Base Sepolia). Fund only what the agent may plausibly spend: wallet balance is the backstop if every other control fails.
python3 scripts/agents_pay_admin.py new-session --budget 1.00 --expiry-minutes 60
export PAYMENT_MANAGER_ARN=... PAYMENT_INSTRUMENT_ID=...
export PAYMENT_SESSION_ID=... PAYMENT_USER_ID=alice
export AWS_REGION=us-west-2
python3 scripts/agents_pay_admin.py preflight
python3 scripts/test_x402_policy.pyKeep sessions short (≤60 minutes) and budgets small. When the budget is spent, a
human runs new-session again — the agent cannot.
Two distinct concepts, and mixing them up is a common setup failure:
--network-family)allowed_networksFamilies (instrument creation):
| Family | Value | Covers | Providers |
|---|---|---|---|
| Ethereum | ETHEREUM | Base, Base Sepolia, Ethereum mainnet | Coinbase, Stripe |
| Solana | SOLANA | Solana, Solana Devnet | Coinbase, Stripe |
Chains (x402 challenges, allowed_networks, balance queries):
| Chain | CAIP-2 identifier | Balance API value | Type | Providers |
|---|---|---|---|---|
| Base Sepolia | eip155:84532 (or base-sepolia) | BASE_SEPOLIA | Testnet | Coinbase |
| Base | eip155:8453 | BASE | Mainnet | Coinbase |
| Ethereum | eip155:1 | ETHEREUM | Mainnet | Coinbase, Stripe |
| Solana | solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp | SOLANA | Mainnet | Coinbase, Stripe |
| Solana Devnet | solana-devnet | SOLANA_DEVNET | Testnet | Stripe |
Start on Base Sepolia (family ETHEREUM, chain eip155:84532) — free testnet
USDC from https://faucet.circle.com/ (opens in a new tab). init-config only knows the USDC contract for
the Base chains; adding another chain means adding its exact contract to KNOWN_USDC
in the admin CLI, deliberately, rather than passing an address in.
bedrock-agentcore calls, especially ProcessPaymentCreatePaymentSession by the runtime principal — it should be
impossible, so any occurrence means the IAM split has regressedProcessPayment failures as an abuse signalshow-config output whenever payments start refusing unexpectedly