Skip to content

Commit 38d763c

Browse files
authored
feat: added x402ActionProvider to python and added to one example (#778)
* feat: added x402ActionProvider to python and added to one example * feat: unit tests * feat: added changelog and readme changes * feat: on retry, using original selected payment requirements for selection * chore: sync locks * fix: unit tests * chore: disabled 3 tests struggling to mock requests
1 parent 463d310 commit 38d763c

File tree

25 files changed

+7101
-189
lines changed

25 files changed

+7101
-189
lines changed

python/coinbase-agentkit/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,24 @@ This section provides a detailed list of all available action providers and thei
536536
</table>
537537
</details>
538538

539+
<details>
540+
<summary><strong>x402</strong></summary>
541+
<table width="100%">
542+
<tr>
543+
<td width="200"><code>make_http_request</code></td>
544+
<td width="768">Makes a basic HTTP request to an API endpoint. If the endpoint requires payment (returns 402), it will return payment details that can be used with retry_http_request_with_x402.</td>
545+
</tr>
546+
<tr>
547+
<td width="200"><code>retry_http_request_with_x402</code></td>
548+
<td width="768">Retries an HTTP request with x402 payment after receiving a 402 Payment Required response. This should be used after make_http_request returns a 402 response.</td>
549+
</tr>
550+
<tr>
551+
<td width="200"><code>make_http_request_with_x402</code></td>
552+
<td width="768">Makes an HTTP request with automatic x402 payment handling. Only use when explicitly told to skip the confirmation flow.</td>
553+
</tr>
554+
</table>
555+
</details>
556+
539557
## Wallet Providers
540558

541559
AgentKit supports the following wallet providers:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added x402ActionProvider

python/coinbase-agentkit/coinbase_agentkit/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
wallet_action_provider,
2222
weth_action_provider,
2323
wow_action_provider,
24+
x402_action_provider,
2425
)
2526
from .agentkit import AgentKit, AgentKitConfig
2627
from .wallet_providers import (
@@ -64,5 +65,6 @@
6465
"wallet_action_provider",
6566
"weth_action_provider",
6667
"wow_action_provider",
68+
"x402_action_provider",
6769
"__version__",
6870
]

python/coinbase-agentkit/coinbase_agentkit/action_providers/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from .wallet.wallet_action_provider import WalletActionProvider, wallet_action_provider
2828
from .weth.weth_action_provider import WethActionProvider, weth_action_provider
2929
from .wow.wow_action_provider import WowActionProvider, wow_action_provider
30+
from .x402.x402_action_provider import x402_action_provider, x402ActionProvider
3031

3132
__all__ = [
3233
"Action",
@@ -64,4 +65,6 @@
6465
"weth_action_provider",
6566
"WowActionProvider",
6667
"wow_action_provider",
68+
"x402ActionProvider",
69+
"x402_action_provider",
6770
]
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# X402 Action Provider
2+
3+
This directory contains the **X402ActionProvider** implementation, which provides actions to interact with **x402-protected APIs** that require payment to access.
4+
5+
## Directory Structure
6+
7+
```
8+
x402/
9+
├── x402_action_provider.py # Main provider with x402 payment functionality
10+
├── schemas.py # x402 action schemas
11+
├── __init__.py # Main exports
12+
└── README.md # This file
13+
```
14+
15+
## Actions
16+
17+
### Primary Actions (Recommended Flow)
18+
19+
1. `make_http_request`: Make initial HTTP request and handle 402 responses
20+
2. `retry_http_request_with_x402`: Retry a request with payment after receiving payment details
21+
22+
### Alternative Action
23+
24+
- `make_http_request_with_x402`: Direct payment-enabled requests (skips confirmation flow)
25+
26+
## Overview
27+
28+
The x402 protocol enables APIs to require micropayments for access. When a client makes a request to a protected endpoint, the server responds with a `402 Payment Required` status code along with payment instructions.
29+
30+
### Recommended Two-Step Flow
31+
32+
1. Initial Request:
33+
- Make request using `make_http_request`
34+
- If endpoint doesn't require payment, get response immediately
35+
- If 402 received, get payment options and instructions
36+
37+
2. Payment & Retry (if needed):
38+
- Review payment requirements
39+
- Use `retry_http_request_with_x402` with chosen payment option
40+
- Get response with payment proof
41+
42+
This flow provides better control and visibility into the payment process.
43+
44+
### Direct Payment Flow (Alternative)
45+
46+
For cases where immediate payment without confirmation is acceptable, use `make_http_request_with_x402` to handle everything in one step.
47+
48+
## Usage
49+
50+
### `make_http_request` Action
51+
52+
Makes initial request and handles 402 responses:
53+
54+
```python
55+
{
56+
"url": "https://api.example.com/data",
57+
"method": "GET", # Optional, defaults to GET
58+
"headers": { "Accept": "..." }, # Optional
59+
"body": { ... } # Optional
60+
}
61+
```
62+
63+
Response format for 402 status:
64+
```python
65+
{
66+
"status": "error_402_payment_required",
67+
"acceptablePaymentOptions": [
68+
{
69+
"scheme": "exact",
70+
"network": "base-sepolia",
71+
"maxAmountRequired": "1000",
72+
"resource": "https://api.example.com/data",
73+
"description": "Access to data",
74+
"mimeType": "application/json",
75+
"payTo": "0x...",
76+
"maxTimeoutSeconds": 300,
77+
"asset": "0x..."
78+
}
79+
],
80+
"nextSteps": [
81+
"Inform the user that the server replied with a 402 Payment Required response.",
82+
"The payment options are: [asset] [amount] [network]",
83+
"Ask the user if they want to retry the request with payment.",
84+
"Use retry_http_request_with_x402 to retry the request with payment."
85+
]
86+
}
87+
```
88+
89+
### `retry_http_request_with_x402` Action
90+
91+
Retries request with payment after 402:
92+
93+
```python
94+
{
95+
"url": "https://api.example.com/data",
96+
"method": "GET", # Optional, defaults to GET
97+
"headers": { "Accept": "..." }, # Optional
98+
"body": { ... }, # Optional
99+
# Payment details (all fields required)
100+
"scheme": "exact",
101+
"network": "base-sepolia",
102+
"max_amount_required": "1000",
103+
"resource": "https://api.example.com/data",
104+
"pay_to": "0x...",
105+
"max_timeout_seconds": 300,
106+
"asset": "0x...",
107+
# Optional payment details
108+
"description": "", # Optional
109+
"mime_type": "", # Optional
110+
"output_schema": null, # Optional
111+
"extra": null # Optional
112+
}
113+
```
114+
115+
### `make_http_request_with_x402` Action
116+
117+
Direct payment-enabled requests (use with caution):
118+
119+
```python
120+
{
121+
"url": "https://api.example.com/data",
122+
"method": "GET", # Optional, defaults to GET
123+
"headers": { "Accept": "..." }, # Optional
124+
"body": { ... } # Optional
125+
}
126+
```
127+
128+
## Response Format
129+
130+
Successful responses include payment proof when payment was made:
131+
132+
```python
133+
{
134+
"status": "success",
135+
"data": { ... }, # API response data
136+
"message": "Request completed successfully with payment",
137+
"details": {
138+
"url": "https://api.example.com/data",
139+
"method": "GET",
140+
"paymentUsed": {
141+
"network": "base-sepolia",
142+
"asset": "0x...",
143+
"amount": "1000"
144+
},
145+
"paymentProof": { # Only present if payment was made
146+
"transaction": "0x...", # Transaction hash
147+
"network": "base-sepolia",
148+
"payer": "0x..." # Payer address
149+
}
150+
}
151+
}
152+
```
153+
154+
Error responses include helpful details and suggestions:
155+
```python
156+
{
157+
"error": true,
158+
"message": "Error description",
159+
"details": "Detailed error information",
160+
"suggestion": "Helpful suggestion for resolving the error"
161+
}
162+
```
163+
164+
## Network Support
165+
166+
The x402 provider currently supports the following networks:
167+
- `base-mainnet`
168+
- `base-sepolia`
169+
170+
The provider requires EVM-compatible networks where the wallet can sign payment transactions.
171+
172+
## Dependencies
173+
174+
This action provider requires:
175+
- `requests` - For making HTTP requests
176+
- `x402` - For payment requirement types and validation
177+
- An EVM-compatible wallet provider for signing transactions
178+
179+
## Notes
180+
181+
For more information on the **x402 protocol**, visit the [x402 documentation](https://x402.gitbook.io/x402/).
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""CDP EVM Server action provider for CDP protocol interactions."""
2+
3+
from .x402_action_provider import x402_action_provider
4+
5+
__all__ = ["x402_action_provider"]
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""Schemas for x402 action providers."""
2+
3+
from typing import Any, Literal
4+
5+
from pydantic import BaseModel, Field
6+
7+
8+
class HttpRequestSchema(BaseModel):
9+
"""Schema for making basic HTTP requests."""
10+
11+
url: str = Field(
12+
..., description="The URL of the API endpoint (can be localhost for development)"
13+
)
14+
method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"] = Field(
15+
default="GET", description="The HTTP method to use for the request"
16+
)
17+
headers: dict[str, str] | None = Field(
18+
default=None, description="Optional headers to include in the request"
19+
)
20+
body: Any | None = Field(
21+
default=None, description="Optional request body for POST/PUT/PATCH requests"
22+
)
23+
24+
class Config:
25+
"""Pydantic config."""
26+
27+
title = "Instructions for making a basic HTTP request"
28+
29+
30+
class RetryWithX402Schema(BaseModel):
31+
"""Schema for retrying requests with x402 payment."""
32+
33+
url: str = Field(
34+
..., description="The URL of the API endpoint (can be localhost for development)"
35+
)
36+
method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"] = Field(
37+
default="GET", description="The HTTP method to use for the request"
38+
)
39+
headers: dict[str, str] | None = Field(
40+
default=None, description="Optional headers to include in the request"
41+
)
42+
body: Any | None = Field(
43+
default=None, description="Optional request body for POST/PUT/PATCH requests"
44+
)
45+
scheme: str = Field(..., description="The payment scheme to use")
46+
network: str = Field(..., description="The network to use for payment")
47+
max_amount_required: str = Field(..., description="The maximum amount required for payment")
48+
resource: str = Field(..., description="The resource URL that requires payment")
49+
description: str = Field(default="", description="Description of the payment requirement")
50+
mime_type: str = Field(default="", description="MIME type of the response")
51+
output_schema: dict[str, Any] | None = Field(
52+
default=None, description="Schema of the expected output"
53+
)
54+
pay_to: str = Field(..., description="Address to send payment to")
55+
max_timeout_seconds: int = Field(..., description="Maximum timeout in seconds")
56+
asset: str = Field(..., description="Asset contract address to use for payment")
57+
extra: dict[str, Any] | None = Field(default=None, description="Additional payment metadata")
58+
59+
class Config:
60+
"""Pydantic config."""
61+
62+
title = (
63+
"Instructions for retrying a request with x402 payment after receiving a 402 response"
64+
)
65+
66+
67+
class DirectX402RequestSchema(BaseModel):
68+
"""Schema for direct x402 payment requests."""
69+
70+
url: str = Field(
71+
..., description="The URL of the API endpoint (can be localhost for development)"
72+
)
73+
method: Literal["GET", "POST", "PUT", "DELETE", "PATCH"] = Field(
74+
default="GET", description="The HTTP method to use for the request"
75+
)
76+
headers: dict[str, str] | None = Field(
77+
default=None, description="Optional headers to include in the request"
78+
)
79+
body: Any | None = Field(
80+
default=None, description="Optional request body for POST/PUT/PATCH requests"
81+
)
82+
83+
class Config:
84+
"""Pydantic config."""
85+
86+
title = "Instructions for making an HTTP request with automatic x402 payment handling. WARNING: This bypasses user confirmation - only use when explicitly told to skip confirmation!"

0 commit comments

Comments
 (0)