> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Amazon Bedrock AgentCore

# Amazon Bedrock AgentCore + Privy

Enable AI agents to make autonomous stablecoin payments via AWS Bedrock AgentCore Payments using Privy embedded wallets.

This recipe covers how to use [Privy](https://dashboard.privy.io/) embedded wallets as the payment provider for [AWS Bedrock AgentCore Payments](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments.html), so agents can autonomously pay for APIs, MCP servers, and content using the x402 protocol.

## Overview

Amazon Bedrock AgentCore is AWS's managed platform for building and operating AI agents. **AgentCore Payments** is the service that lets an agent pay for paid endpoints (APIs, MCPs, and web content) over the x402 protocol. It owns the entire payment lifecycle: storing provider credentials, enforcing per-session spend limits, signing the payment, and recording the transaction.

Privy provides the **user-owned embedded wallet** that holds the stablecoins. AgentCore connects to Privy through a *PaymentConnector*, retrieves signing material through *AgentCore Identity*, and calls `ProcessPayment` to produce the signed x402 proof. The agent never talks to Privy's wallet APIs directly — it calls AgentCore, and AgentCore talks to Privy.

With this integration, agents can:

* Discover and call paid APIs, MCP tools, and paywalled content that return `402 Payment Required`
* Accept funds from the end user via fiat (cards, Apple Pay, Google Pay, ACH) or USDC stablecoin
* Pay autonomously to the paid endpoint over x402 (v1 and v2)
* Operate within spend limits enforced per **Payment Session** by AgentCore
* Settle on-chain in USDC

## How it works

AgentCore Payments is built from a small set of resources. Create them once, in order, and the agent uses them at runtime.

* **PaymentManager** — The top-level resource for your account. It defines how agents authenticate (`AWS_IAM` or `CUSTOM_JWT`) and references the IAM execution role AgentCore assumes to do payment work.
* **PaymentCredentialProvider** — Stores your Privy credentials (App ID, App Secret, authorization key) inside **AgentCore Identity**, backed by AWS Secrets Manager. The agent runtime never reads these directly.
* **PaymentConnector** — Binds the PaymentManager to a payment provider. For Privy, create a `StripePrivy` connector that references the credential provider above.
* **PaymentInstrument** — The end user's wallet. Create it through the AgentCore `CreatePaymentInstrument` API (type `EMBEDDED_CRYPTO_WALLET`), **not** through the Privy SDK. AgentCore provisions the Privy embedded wallet on your behalf and returns the wallet address.
* **PaymentSession** — A time-bounded spending context (`maxSpendAmount`, `currency`, `expiryTimeInMinutes`). When the session expires or the limit is reached, further payments in that session are denied.
* **ProcessPayment** — At runtime, when the agent hits a `402`, it calls `ProcessPayment`. AgentCore checks the session limit, retrieves the Privy signing key from Identity, signs the x402 proof, and returns it. The agent retries the request with the proof.

The runtime flow:

```
1. Agent calls a paid resource (x402)         -> 402 Payment Required
2. Agent calls AgentCore ProcessPayment       -> session limit checked
3. AgentCore retrieves the Privy signing key  -> signs the x402 proof
4. Agent retries the request with the proof   -> 200 OK + paid content

   Your agent  --(402)-->  Paid resource
   Your agent  --(ProcessPayment)-->  AgentCore Payments  --(via Identity)-->  Privy wallet
   Your agent  --(retry with signed proof)-->  Paid resource
```

## Prerequisites

* **AWS account with AgentCore Payments access** — Install and configure the AWS CLI v2 and Python 3.10+ with `boto3`. Verify your credentials with `aws sts get-caller-identity`. AgentCore Payments is available in `us-east-1`, `us-west-2`, `eu-central-1`, and `ap-southeast-2`.
* **A dedicated Privy app** — Create a developer account at [dashboard.privy.io](https://dashboard.privy.io/) and create a **dedicated** Privy app for AgentCore. Do not reuse an app that serves other purposes; this keeps credential scope and auditing clean. Copy the **App ID** and **App Secret** from the app settings.
* **A Privy authorization key** — In your Privy app, go to **Wallet Infrastructure > Authorization > New Key** to generate a P-256 key pair. This key is what AgentCore uses to sign wallet operations. Note the **Authorization ID** (signer ID) shown alongside the key.

<Warning>
  Privy prefixes the generated private key with `wallet-auth:`. AgentCore Payments does **not** accept this prefix. Strip it and store only the raw base64 content after the prefix.

  ```
  Privy gives you:  wallet-auth:MBMGByqGSM49AgEGCC...
  Store only:                   MBMGByqGSM49AgEGCC...
  ```
</Warning>

After this, there are four Privy values to hand to AgentCore: **App ID**, **App Secret**, **Authorization ID**, and the **Authorization Private Key** (prefix stripped). For full provider detail, see the AWS [Prerequisites for AgentCore payments](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-prerequisites.html).

## Step 1: Store Privy credentials in AgentCore Identity

Create a `PaymentCredentialProvider` so AgentCore can store your Privy credentials securely. Resource names must be lowercase alphanumeric with hyphens only.

```python theme={"system"}
import boto3

cp = boto3.client("bedrock-agentcore-control", region_name="us-west-2")

cred = cp.create_payment_credential_provider(
    name="agentcore-privy-creds",
    credentialProviderVendor="StripePrivy",
    providerConfigurationInput={
        "stripePrivyConfiguration": {
            "appId": PRIVY_APP_ID,
            "appSecret": PRIVY_APP_SECRET,
            "authorizationId": PRIVY_AUTH_ID,
            "authorizationPrivateKey": PRIVY_AUTH_PRIVATE_KEY,  # wallet-auth: prefix stripped
        }
    },
)
credential_provider_arn = cred["credentialProviderArn"]
```

<Warning>
  Never embed Privy credentials in agent source code or paste them in chat. Load them from
  environment variables (`source .env.payments`) at setup time. Once stored in AgentCore Identity,
  restrict the underlying Secrets Manager secret to the AgentCore Payments service role only.
</Warning>

## Step 2: Create the Payment Manager

The Payment Manager needs an IAM role that trusts `bedrock-agentcore.amazonaws.com` and grants `GetWorkloadAccessToken`, `GetResourcePaymentToken`, and `secretsmanager:GetSecretValue` (see the [IAM roles](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-iam-roles.html) page for the exact policy).

```python theme={"system"}
import uuid, time

mgr = cp.create_payment_manager(
    name="agentcoreprivy",
    description="Payment manager for Privy wallets",
    authorizerType="AWS_IAM",
    roleArn=role_arn,
    clientToken=str(uuid.uuid4()),
)
payment_manager_id = mgr["paymentManagerId"]    # used for control-plane ops
payment_manager_arn = mgr["paymentManagerArn"]  # used for data-plane ops

# Wait for READY before creating a connector
while cp.get_payment_manager(paymentManagerId=payment_manager_id)["status"] != "READY":
    time.sleep(5)
```

## Step 3: Create the Payment Connector

Bind the manager to Privy by referencing the credential provider from Step 1.

```python theme={"system"}
conn = cp.create_payment_connector(
    paymentManagerId=payment_manager_id,
    name="agentcoreprivyconnector",
    description="Privy connector",
    type="StripePrivy",
    credentialProviderConfigurations=[
        {"stripePrivy": {"credentialProviderArn": credential_provider_arn}}
    ],
    clientToken=str(uuid.uuid4()),
)
connector_id = conn["paymentConnectorId"]
```

## Step 4: Create the Payment Instrument (wallet)

Create the user's embedded wallet through AgentCore. The end user's email is linked here: it is the account they will log into when granting the agent permission to spend.

```python theme={"system"}
dp = boto3.client("bedrock-agentcore", region_name="us-west-2")

instr = dp.create_payment_instrument(
    paymentManagerArn=payment_manager_arn,
    paymentConnectorId=connector_id,
    userId="agentcore-user",
    paymentInstrumentType="EMBEDDED_CRYPTO_WALLET",
    paymentInstrumentDetails={
        "embeddedCryptoWallet": {
            "network": "ETHEREUM",  # ETHEREUM covers Base + Base Sepolia
            "linkedAccounts": [{"email": {"emailAddress": END_USER_EMAIL}}],
        }
    },
    clientToken=str(uuid.uuid4()),
)
instrument = instr.get("paymentInstrument", instr)
payment_instrument_id = instrument["paymentInstrumentId"]
wallet_address = instrument["paymentInstrumentDetails"]["embeddedCryptoWallet"]["walletAddress"]
```

A new instrument starts with **0 USDC** and the agent has **no permission to spend** until the end user grants it. Funding and delegation come next, in that order.

## Step 5: Grant the agent permission (delegation)

This is a required step. The agent's authorization key must be added as a signer on the end user's embedded wallet, and only the user can approve that.

1. Stand up a frontend using the [Privy AgentCore SDK](https://github.com/privy-io/aws-agentcore-sdk), which provides a reference wallet hub for login, agent connection, and on-ramping.
2. Have the end user log in with the email linked in Step 4 (`END_USER_EMAIL`).
3. The user approves delegation for the wallet, authorizing the agent to sign within AgentCore's controls.

<Note>
  If delegation is skipped, `ProcessPayment` fails with a "Delegation not completed" error. The
  agent acts as an authorized signer only: the user retains ownership and can revoke access at any
  time.
</Note>

## Step 6: Fund the wallet

Once delegation is approved, fund the wallet with USDC.

* **Testnet:** Get free testnet USDC on Base Sepolia from [Circle's faucet](https://faucet.circle.com/) and send it to the wallet address from Step 4.
* **Mainnet:** The end user funds the wallet through the Privy wallet hub: crypto-to-crypto transfer or supported fiat methods (cards, Apple Pay, Google Pay, ACH; availability varies by region).

## Step 7: Create a Payment Session and enable payments

Create a session to bound spending, then wire payment handling into your agent.

```python theme={"system"}
session = dp.create_payment_session(
    paymentManagerArn=payment_manager_arn,
    userId="agentcore-user",
    expiryTimeInMinutes=60,
    limits={"maxSpendAmount": {"value": "5.00", "currency": "USD"}},
)
payment_session_id = session["paymentSession"]["paymentSessionId"]
```

The agent uses an x402-aware fetch tool. When it hits a `402`, the tool reads the challenge, calls `ProcessPayment`, and retries with the signed proof. AgentCore checks the session limit, signs through Privy, and returns the proof.

```python theme={"system"}
import os, json, base64, httpx, boto3

dp = boto3.client("bedrock-agentcore", region_name=os.environ["AWS_REGION"])

def x402_fetch(url: str, method: str = "GET") -> str:
    """Fetch a URL, paying via AgentCore + Privy if it returns 402."""
    resp = httpx.request(method, url, timeout=30)
    if resp.status_code != 402:
        return json.dumps({"status_code": resp.status_code, "body": resp.text})

    # Extract the x402 challenge (body for v1, payment-required header otherwise)
    challenge = None
    try:
        body = resp.json()
        if "x402Version" in body and "accepts" in body:
            challenge = body
    except Exception:
        pass
    if not challenge and (h := resp.headers.get("payment-required")):
        challenge = json.loads(base64.b64decode(h))
    if not challenge:
        return json.dumps({"error": "402 without an x402 challenge"})

    # Pick the accept that matches your wallet's network family.
    # This recipe uses an ETHEREUM (EVM) wallet, so prefer eip155/base; fall back to the first.
    accepts_list = challenge["accepts"]
    accepts = next(
        (a for a in accepts_list
         if str(a.get("network", "")).lower().startswith(("eip155", "base", "ethereum"))),
        accepts_list[0],
    )

    # AgentCore signs the payment through the Privy wallet.
    # Forward the FULL accept object as the payload — it includes `extra`
    # (e.g. {"name": "USDC"}), which is required for EVM payments.
    pay = dp.process_payment(
        paymentManagerArn=os.environ["PAYMENT_MANAGER_ARN"],
        paymentInstrumentId=os.environ["PAYMENT_INSTRUMENT_ID"],
        paymentSessionId=os.environ["PAYMENT_SESSION_ID"],
        userId=os.environ["PAYMENT_USER_ID"],
        paymentType="CRYPTO_X402",
        paymentInput={"cryptoX402": {
            "version": str(challenge.get("x402Version", "1")),
            "payload": accepts,
        }},
    )

    out = pay["paymentOutput"]["cryptoX402"]
    version = int(challenge.get("x402Version", 1))

    # Build the retry proof to match the challenge version.
    if version >= 2:
        proof = {
            "x402Version": 2,
            "resource": challenge.get("resource"),
            "accepted": accepts,
            "extensions": challenge.get("extensions", {}),
            "payload": out["payload"],
        }
        header = "PAYMENT-SIGNATURE"
    else:
        proof = {
            "x402Version": 1,
            "scheme": accepts.get("scheme", "exact"),
            "network": accepts["network"],
            "payload": out["payload"],
        }
        header = "X-PAYMENT"

    token = base64.b64encode(json.dumps(proof, separators=(",", ":")).encode()).decode()

    # Retry with a FRESH client — reusing cookies from the 402 can break the retry
    with httpx.Client(verify=True) as client:
        retry = client.request(method, url, headers={header: token}, timeout=30)

    return json.dumps({
        "status_code": retry.status_code,
        "body": retry.text,
        "payment_made": 200 <= retry.status_code < 300,
    })
```

Register `x402_fetch` as a tool in your agent framework (Strands, LangGraph, OpenAI Agents SDK, etc.) and the agent will pay for `402` resources autonomously.

<Warning>
  Use a **fresh** HTTP client for the retry. Some merchants set cookies on the 402 response that
  cause the paid retry to fail if reused. Also build the proof to match the challenge's
  `x402Version`: a v2 merchant silently ignores a v1 `X-PAYMENT` header and re-issues the same 402.
</Warning>

## Spend controls

AgentCore enforces spending at the **Payment Session** level. There are no per-recipient allowlists or separate budget objects.

| Field                 | Description                                                                                                                      |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `maxSpendAmount`      | The ceiling for the session                                                                                                      |
| `currency`            | The session currency                                                                                                             |
| `expiryTimeInMinutes` | Keep sessions short-lived (60 minutes or less); create a fresh session per user interaction rather than reusing a long-lived one |

When a payment would exceed the limit or the session has expired, AgentCore denies it before signing.

## Observability

AgentCore Payments integrates with Amazon CloudWatch. Once enabled, every data plane API call (`ProcessPayment`, `CreatePaymentInstrument`, etc.) emits metrics, logs, and X-Ray spans automatically.

### Enable it

1. Create a CloudWatch log group (e.g. `/bedrock-agentcore/payments/my-logs`).
2. Grant your IAM principal vended-log and X-Ray delivery permissions (`logs:CreateDelivery`, `xray:PutTraceSegments`, `bedrock-agentcore:AllowVendedLogDeliveryForResource`, and related).
3. On the Payment Manager details page, under **Log deliveries and tracing**, point log delivery at your log group and enable traces.

### Vended metrics

Key payment metrics published to CloudWatch: `PaymentRequestCount`, `PaymentSuccessCount`, `PaymentFailureCount`, `PaymentLatency`, and `SpendAmount`, plus per-operation `OperationSuccess`, `OperationFailure`, `OperationLatency`, `Throttles`, `UserErrors`, and `ActiveSessions`. Dimensions: `Operation`, `PaymentManagerId`, `PaymentConnectorId`, `AgentName`, and `Currency`.

Alarm on `PaymentFailureCount` (misconfiguration/abuse signal) and `PaymentLatency` against your SLA.

### Vended spans

One span per API call, named `Bedrock.AgentCore.Payments.<Operation>`, viewable in X-Ray with payment-specific attributes: `spend_amount`, `spend_currency`, `merchant` (payTo), `session_remaining_budget`, `total_budget_amount`, and `token_fetch_latency_ms` — plus resource IDs and standard AWS attributes.

For the full reference, see the [AgentCore Payments observability docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-observability.html).

## Supported networks

AgentCore Payments with Privy settles in **USDC**. For instrument creation, choose a network family; the x402 challenge then specifies the exact chain.

| Network (instrument) | Chains                                         | Asset | Type    |
| -------------------- | ---------------------------------------------- | ----- | ------- |
| `ETHEREUM`           | Base Sepolia (`base-sepolia` / `eip155:84532`) | USDC  | Testnet |
| `ETHEREUM`           | Base (`eip155:8453`), Ethereum (`eip155:1`)    | USDC  | Mainnet |
| `SOLANA`             | Solana Devnet (`solana-devnet`)                | USDC  | Testnet |
| `SOLANA`             | Solana Mainnet                                 | USDC  | Mainnet |

For development, start with `ETHEREUM` / Base Sepolia and free testnet USDC from [Circle's faucet](https://faucet.circle.com/).

## Testing

* Use Base Sepolia for development.
* Fund the wallet with testnet USDC from [Circle's faucet](https://faucet.circle.com/).
* Point the agent at an x402-enabled test endpoint and confirm it pays and returns content. Browse live x402 services at [x402scan.com](https://x402scan.com/).

If the agent loops on `402` after a successful `ProcessPayment`, the most common causes are cookie contamination on the retry, an x402 version/header mismatch, or an expired proof (\~60s validity window).

## Security considerations

* **User ownership.** AgentCore is an authorized signer, not the wallet owner. The user grants delegation and can revoke it at any time. The user can also withdraw funds from the wallet at any time.
* **Credential isolation.** Privy credentials live in AgentCore Identity / Secrets Manager. Restrict the secret to the AgentCore Payments service role only.
* **Session limits.** Per-session `maxSpendAmount` and short expiry bound runaway spending.
* **HTTPS only.** Reject non-HTTPS targets and block private/internal IP ranges to prevent SSRF.
* **Audit.** AgentCore Observability and CloudTrail capture every `ProcessPayment` call; alarm on failed-payment spikes.
* **Rotate** the App Secret and authorization key on a regular schedule (e.g., every 90 days).

## Further reading

* [AWS Bedrock AgentCore Payments](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments.html)
* [AgentCore payments samples: tutorials and end-to-end use-case patterns](https://github.com/awslabs/agentcore-samples/tree/main/01-features/08-agents-that-transact)
* [AgentCore Payments launch blog](https://aws.amazon.com/blogs/machine-learning/agents-that-transact-introducing-amazon-bedrock-agentcore-payments-built-with-coinbase-and-stripe/)
* [Privy AgentCore SDK](https://github.com/privy-io/aws-agentcore-sdk)
* [Privy authorization keys](https://docs.privy.io/basics/get-started/organization)
