agentic-x402 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SKILL.md CHANGED
@@ -4,7 +4,7 @@ description: Make x402 payments to access gated APIs and content. Fetch paid res
4
4
  license: MIT
5
5
  compatibility: Requires Node.js 20+, network access to x402 facilitators and EVM chains
6
6
  homepage: https://www.npmjs.com/package/agentic-x402
7
- metadata: {"author": "monemetrics", "version": "0.3.6", "openclaw": {"requires": {"bins": ["x402"], "env": ["EVM_PRIVATE_KEY"]}, "primaryEnv": "EVM_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "agentic-x402", "bins": ["x402"], "label": "Install agentic-x402 (npm)"}], "plugin": true}}
7
+ metadata: {"author": "monemetrics", "version": "0.3.7", "openclaw": {"requires": {"bins": ["x402"], "env": ["EVM_PRIVATE_KEY"]}, "primaryEnv": "EVM_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "agentic-x402", "bins": ["x402"], "label": "Install agentic-x402 (npm)"}], "plugin": true}}
8
8
  allowed-tools: Bash(x402:*) Bash(npm:*) Read
9
9
  ---
10
10
 
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "id": "agentic-x402",
3
3
  "name": "x402 Payments",
4
- "version": "0.3.6",
5
- "description": "Agent skill for x402 payments — pay for and sell gated content with background payment watching",
6
- "skills": ["./skills/x402"],
4
+ "version": "0.3.8",
5
+ "description": "Agent plugin for x402 payments — pay for and sell gated content with background payment watching",
7
6
  "configSchema": {
8
7
  "type": "object",
9
8
  "properties": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-x402",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Agent skill for x402 payments - pay for and sell gated content",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,7 +12,6 @@
12
12
  "files": [
13
13
  "bin/",
14
14
  "scripts/",
15
- "skills/",
16
15
  "config/example.env",
17
16
  "openclaw.plugin.json",
18
17
  "README.md",
@@ -4,20 +4,28 @@
4
4
  import type { PluginTool } from './types.js';
5
5
  import type { PaymentWatcher } from './watcher.js';
6
6
 
7
+ /** Helper to wrap a result as OpenClaw tool response */
8
+ function json(payload: unknown) {
9
+ return {
10
+ content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }],
11
+ details: payload,
12
+ };
13
+ }
14
+
7
15
  export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
8
16
  return [
9
17
  // 1. x402_balance
10
18
  {
11
19
  name: 'x402_balance',
12
20
  description: 'Check wallet USDC and ETH balances on Base',
13
- inputSchema: { type: 'object', properties: {}, required: [] },
14
- async execute() {
21
+ parameters: { type: 'object', properties: {} },
22
+ async execute(_toolCallId, _params) {
15
23
  const { getClient, getWalletAddress, getUsdcBalance, getEthBalance } = await import('../core/client.js');
16
24
  const client = getClient();
17
25
  const address = getWalletAddress();
18
26
  const [usdc, eth] = await Promise.all([getUsdcBalance(), getEthBalance()]);
19
27
 
20
- return {
28
+ return json({
21
29
  address,
22
30
  network: client.config.network,
23
31
  chainId: client.config.chainId,
@@ -25,7 +33,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
25
33
  usdc: { raw: usdc.raw.toString(), formatted: usdc.formatted },
26
34
  eth: { raw: eth.raw.toString(), formatted: eth.formatted },
27
35
  },
28
- };
36
+ });
29
37
  },
30
38
  },
31
39
 
@@ -33,7 +41,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
33
41
  {
34
42
  name: 'x402_pay',
35
43
  description: 'Pay for an x402-gated resource. Returns the response body after payment.',
36
- inputSchema: {
44
+ parameters: {
37
45
  type: 'object',
38
46
  properties: {
39
47
  url: { type: 'string', description: 'URL of the x402-gated resource' },
@@ -44,7 +52,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
44
52
  },
45
53
  required: ['url'],
46
54
  },
47
- async execute(params) {
55
+ async execute(_toolCallId, params) {
48
56
  const url = params.url as string;
49
57
  const method = (params.method as string) || 'GET';
50
58
  const body = params.body as string | undefined;
@@ -65,7 +73,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
65
73
  const responseBody = contentType?.includes('json')
66
74
  ? await probe.json()
67
75
  : await probe.text();
68
- return { paid: false, status: probe.status, response: responseBody };
76
+ return json({ paid: false, status: probe.status, response: responseBody });
69
77
  }
70
78
 
71
79
  // Parse payment info
@@ -84,18 +92,18 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
84
92
  const effectiveMax = maxPaymentUsd ?? client.config.maxPaymentUsd;
85
93
 
86
94
  if (priceNum > effectiveMax) {
87
- return { paid: false, error: `Price $${priceNum} exceeds max $${effectiveMax}`, paymentInfo };
95
+ return json({ paid: false, error: `Price $${priceNum} exceeds max $${effectiveMax}`, paymentInfo });
88
96
  }
89
97
 
90
98
  if (dryRun) {
91
- return { paid: false, dryRun: true, price: priceNum, paymentInfo };
99
+ return json({ paid: false, dryRun: true, price: priceNum, paymentInfo });
92
100
  }
93
101
 
94
102
  // Execute payment
95
103
  const response = await client.fetchWithPayment(url, { method, body, headers });
96
104
 
97
105
  if (!response.ok) {
98
- return { paid: false, error: `${response.status} ${response.statusText}` };
106
+ return json({ paid: false, error: `${response.status} ${response.statusText}` });
99
107
  }
100
108
 
101
109
  const contentType = response.headers.get('content-type');
@@ -112,7 +120,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
112
120
  } catch { /* */ }
113
121
  }
114
122
 
115
- return { paid: true, price: priceNum, transactionHash: txHash, response: responseBody };
123
+ return json({ paid: true, price: priceNum, transactionHash: txHash, response: responseBody });
116
124
  },
117
125
  },
118
126
 
@@ -120,7 +128,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
120
128
  {
121
129
  name: 'x402_fetch',
122
130
  description: 'Fetch a URL with automatic x402 payment handling. Simpler than x402_pay — just returns the content.',
123
- inputSchema: {
131
+ parameters: {
124
132
  type: 'object',
125
133
  properties: {
126
134
  url: { type: 'string', description: 'URL to fetch' },
@@ -129,7 +137,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
129
137
  },
130
138
  required: ['url'],
131
139
  },
132
- async execute(params) {
140
+ async execute(_toolCallId, params) {
133
141
  const url = params.url as string;
134
142
  const method = (params.method as string) || 'GET';
135
143
  const body = params.body as string | undefined;
@@ -143,7 +151,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
143
151
  const response = await client.fetchWithPayment(url, { method, body, headers });
144
152
 
145
153
  if (!response.ok) {
146
- return { success: false, status: response.status, error: response.statusText };
154
+ return json({ success: false, status: response.status, error: response.statusText });
147
155
  }
148
156
 
149
157
  const contentType = response.headers.get('content-type');
@@ -151,7 +159,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
151
159
  ? await response.json()
152
160
  : await response.text();
153
161
 
154
- return { success: true, status: response.status, response: responseBody };
162
+ return json({ success: true, status: response.status, response: responseBody });
155
163
  },
156
164
  },
157
165
 
@@ -159,7 +167,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
159
167
  {
160
168
  name: 'x402_create_link',
161
169
  description: 'Create a payment link via 21.cash to sell gated content',
162
- inputSchema: {
170
+ parameters: {
163
171
  type: 'object',
164
172
  properties: {
165
173
  name: { type: 'string', description: 'Name of the payment link' },
@@ -171,7 +179,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
171
179
  },
172
180
  required: ['name', 'price'],
173
181
  },
174
- async execute(params) {
182
+ async execute(_toolCallId, params) {
175
183
  const name = params.name as string;
176
184
  const price = params.price as string;
177
185
  const gatedUrl = params.url as string | undefined;
@@ -180,7 +188,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
180
188
  const webhookUrl = params.webhookUrl as string | undefined;
181
189
 
182
190
  if (!gatedUrl && !gatedText) {
183
- return { success: false, error: 'Either url or text is required' };
191
+ return json({ success: false, error: 'Either url or text is required' });
184
192
  }
185
193
 
186
194
  const { getClient, getWalletAddress } = await import('../core/client.js');
@@ -210,10 +218,10 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
210
218
 
211
219
  const data = await response.json();
212
220
  if (!response.ok || !(data as Record<string, unknown>).success) {
213
- return { success: false, error: (data as Record<string, unknown>).error ?? 'Unknown error' };
221
+ return json({ success: false, error: (data as Record<string, unknown>).error ?? 'Unknown error' });
214
222
  }
215
223
 
216
- return data;
224
+ return json(data);
217
225
  },
218
226
  },
219
227
 
@@ -221,14 +229,14 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
221
229
  {
222
230
  name: 'x402_link_info',
223
231
  description: 'Get details about a payment link by router address',
224
- inputSchema: {
232
+ parameters: {
225
233
  type: 'object',
226
234
  properties: {
227
235
  routerAddress: { type: 'string', description: 'Router contract address or payment URL' },
228
236
  },
229
237
  required: ['routerAddress'],
230
238
  },
231
- async execute(params) {
239
+ async execute(_toolCallId, params) {
232
240
  let routerAddress = params.routerAddress as string;
233
241
 
234
242
  // Extract address from URL if needed
@@ -239,7 +247,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
239
247
  }
240
248
 
241
249
  if (!routerAddress.startsWith('0x') || routerAddress.length !== 42) {
242
- return { success: false, error: 'Invalid router address' };
250
+ return json({ success: false, error: 'Invalid router address' });
243
251
  }
244
252
 
245
253
  const { getConfig } = await import('../core/config.js');
@@ -250,10 +258,10 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
250
258
  const data = await response.json();
251
259
 
252
260
  if (!response.ok || !(data as Record<string, unknown>).success) {
253
- return { success: false, error: (data as Record<string, unknown>).error ?? 'Link not found' };
261
+ return json({ success: false, error: (data as Record<string, unknown>).error ?? 'Link not found' });
254
262
  }
255
263
 
256
- return data;
264
+ return json(data);
257
265
  },
258
266
  },
259
267
 
@@ -261,14 +269,13 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
261
269
  {
262
270
  name: 'x402_routers',
263
271
  description: 'List payment routers where your wallet is a beneficiary',
264
- inputSchema: {
272
+ parameters: {
265
273
  type: 'object',
266
274
  properties: {
267
275
  withBalances: { type: 'boolean', description: 'Fetch on-chain USDC balance for each router' },
268
276
  },
269
- required: [],
270
277
  },
271
- async execute(params) {
278
+ async execute(_toolCallId, params) {
272
279
  const withBalances = params.withBalances as boolean | undefined;
273
280
 
274
281
  const { getWalletAddress } = await import('../core/client.js');
@@ -282,13 +289,13 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
282
289
  const data = await response.json() as { success: boolean; links?: Array<Record<string, unknown>>; error?: string };
283
290
 
284
291
  if (!response.ok || !data.success) {
285
- return { success: false, error: data.error ?? 'Failed to fetch routers' };
292
+ return json({ success: false, error: data.error ?? 'Failed to fetch routers' });
286
293
  }
287
294
 
288
295
  const links = data.links ?? [];
289
296
 
290
297
  if (!withBalances) {
291
- return {
298
+ return json({
292
299
  success: true,
293
300
  address,
294
301
  routers: links.map((l) => ({
@@ -298,7 +305,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
298
305
  sharePercent: l.beneficiary_percentage,
299
306
  createdAt: l.created_at,
300
307
  })),
301
- };
308
+ });
302
309
  }
303
310
 
304
311
  // Fetch balances
@@ -340,7 +347,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
340
347
  };
341
348
  }));
342
349
 
343
- return { success: true, address, routers };
350
+ return json({ success: true, address, routers });
344
351
  },
345
352
  },
346
353
 
@@ -348,7 +355,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
348
355
  {
349
356
  name: 'x402_distribute',
350
357
  description: 'Distribute (withdraw) USDC from a PaymentRouter contract',
351
- inputSchema: {
358
+ parameters: {
352
359
  type: 'object',
353
360
  properties: {
354
361
  routerAddress: { type: 'string', description: 'PaymentRouter contract address' },
@@ -356,12 +363,12 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
356
363
  },
357
364
  required: ['routerAddress'],
358
365
  },
359
- async execute(params) {
366
+ async execute(_toolCallId, params) {
360
367
  const routerAddress = params.routerAddress as string;
361
368
  const specifiedAmount = params.amount as string | undefined;
362
369
 
363
370
  if (!routerAddress.startsWith('0x') || routerAddress.length !== 42) {
364
- return { success: false, error: 'Invalid router address' };
371
+ return json({ success: false, error: 'Invalid router address' });
365
372
  }
366
373
 
367
374
  const { getClient } = await import('../core/client.js');
@@ -400,17 +407,17 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
400
407
  });
401
408
 
402
409
  if (routerBalance === 0n) {
403
- return { success: false, error: 'Router has no USDC balance to distribute' };
410
+ return json({ success: false, error: 'Router has no USDC balance to distribute' });
404
411
  }
405
412
 
406
413
  let distributeAmount: bigint;
407
414
  if (specifiedAmount) {
408
415
  distributeAmount = parseUnits(specifiedAmount, 6);
409
416
  if (distributeAmount > routerBalance) {
410
- return {
417
+ return json({
411
418
  success: false,
412
419
  error: `Requested ${specifiedAmount} USDC exceeds balance ${formatUnits(routerBalance, 6)} USDC`,
413
- };
420
+ });
414
421
  }
415
422
  } else {
416
423
  distributeAmount = routerBalance;
@@ -426,7 +433,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
426
433
 
427
434
  const receipt = await client.publicClient.waitForTransactionReceipt({ hash: txHash });
428
435
 
429
- return {
436
+ return json({
430
437
  success: receipt.status === 'success',
431
438
  routerAddress,
432
439
  amount: formatUnits(distributeAmount, 6),
@@ -434,7 +441,7 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
434
441
  transactionHash: txHash,
435
442
  blockNumber: receipt.blockNumber.toString(),
436
443
  status: receipt.status,
437
- };
444
+ });
438
445
  },
439
446
  },
440
447
 
@@ -442,12 +449,12 @@ export function createTools(watcher: PaymentWatcher | null): PluginTool[] {
442
449
  {
443
450
  name: 'x402_watcher_status',
444
451
  description: 'Get the status of the background payment watcher (tracked routers, payments detected)',
445
- inputSchema: { type: 'object', properties: {}, required: [] },
452
+ parameters: { type: 'object', properties: {} },
446
453
  async execute() {
447
454
  if (!watcher) {
448
- return { running: false, error: 'Watcher is not enabled' };
455
+ return json({ running: false, error: 'Watcher is not enabled' });
449
456
  }
450
- return watcher.getStatus();
457
+ return json(watcher.getStatus());
451
458
  },
452
459
  },
453
460
  ];
@@ -15,24 +15,33 @@ export interface PluginService {
15
15
  stop(): Promise<void>;
16
16
  }
17
17
 
18
- /** JSON Schema for tool input parameters */
19
- export interface ToolInputSchema {
18
+ /** JSON Schema for tool parameters */
19
+ export interface ToolParameters {
20
20
  type: 'object';
21
21
  properties: Record<string, {
22
22
  type: string;
23
- description: string;
23
+ description?: string;
24
24
  enum?: string[];
25
25
  default?: unknown;
26
26
  }>;
27
27
  required?: string[];
28
28
  }
29
29
 
30
+ /** Tool result content block */
31
+ export interface ToolResultContent {
32
+ type: 'text';
33
+ text: string;
34
+ }
35
+
30
36
  /** An agent tool registered by a plugin */
31
37
  export interface PluginTool {
32
38
  name: string;
33
39
  description: string;
34
- inputSchema: ToolInputSchema;
35
- execute(params: Record<string, unknown>): Promise<unknown>;
40
+ parameters: ToolParameters;
41
+ execute(toolCallId: string, params: Record<string, unknown>): Promise<{
42
+ content: ToolResultContent[];
43
+ details?: unknown;
44
+ }>;
36
45
  }
37
46
 
38
47
  /** Commander.js-style program for CLI registration */
@@ -1,486 +0,0 @@
1
- ---
2
- name: x402
3
- description: Make x402 payments to access gated APIs and content. Fetch paid resources, check wallet balance, create payment links, monitor routers for incoming payments, and distribute funds. Use when encountering 402 Payment Required responses or when the user wants to pay for web resources with crypto. Also available as an OpenClaw plugin with background payment watching and 8 agent tools.
4
- license: MIT
5
- compatibility: Requires Node.js 20+, network access to x402 facilitators and EVM chains
6
- homepage: https://www.npmjs.com/package/agentic-x402
7
- metadata: {"author": "monemetrics", "version": "0.3.6", "openclaw": {"requires": {"bins": ["x402"], "env": ["EVM_PRIVATE_KEY"]}, "primaryEnv": "EVM_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "agentic-x402", "bins": ["x402"], "label": "Install agentic-x402 (npm)"}], "plugin": true}}
8
- allowed-tools: Bash(x402:*) Bash(npm:*) Read
9
- ---
10
-
11
- # x402 Agent Skill
12
-
13
- Pay for x402-gated APIs and content using USDC on Base. This skill enables agents to autonomously make crypto payments when accessing paid web resources.
14
-
15
- ## Quick Reference
16
-
17
- | Command | Description |
18
- |---------|-------------|
19
- | `x402 setup` | Create or configure wallet |
20
- | `x402 balance` | Check USDC and ETH balances |
21
- | `x402 pay <url>` | Pay for a gated resource |
22
- | `x402 fetch <url>` | Fetch with auto-payment |
23
- | `x402 create-link` | Create payment link (seller) |
24
- | `x402 link-info <addr>` | Get payment link details |
25
- | `x402 routers` | List routers where your wallet is a beneficiary |
26
- | `x402 distribute <addr>` | Distribute USDC from a PaymentRouter |
27
-
28
- ## OpenClaw Plugin
29
-
30
- When installed as an OpenClaw plugin, agentic-x402 provides **8 agent tools** and a **background payment watcher** — no CLI required.
31
-
32
- ### Install as Plugin
33
-
34
- ```bash
35
- openclaw plugins install agentic-x402
36
- ```
37
-
38
- ### Agent Tools
39
-
40
- When running inside OpenClaw, the agent can call these tools directly (no shell commands needed):
41
-
42
- | Tool | Description |
43
- |------|-------------|
44
- | `x402_balance` | Check wallet USDC + ETH balances |
45
- | `x402_pay` | Pay for x402-gated resource (supports dry-run) |
46
- | `x402_fetch` | Fetch URL with automatic payment |
47
- | `x402_create_link` | Create payment link via 21.cash |
48
- | `x402_link_info` | Get link details by router address |
49
- | `x402_routers` | List beneficiary routers (optional balances) |
50
- | `x402_distribute` | Distribute USDC from a router |
51
- | `x402_watcher_status` | Get watcher state (tracked routers, payments detected) |
52
-
53
- All tools return structured JSON. Parameters use camelCase (e.g., `routerAddress`, `maxPaymentUsd`, `withBalances`).
54
-
55
- ### Background Payment Watcher
56
-
57
- The plugin includes a background service that monitors your payment routers for incoming USDC:
58
-
59
- - Fetches your routers from the 21.cash API
60
- - Polls USDC `balanceOf` for each router onchain (free view call, no gas)
61
- - Detects balance increases and triggers a hook at `/hooks/agent` with payment details
62
- - Configurable poll interval (default 30s)
63
- - First poll seeds state without triggering notifications (no false positives on restart)
64
-
65
- When a payment is detected, the watcher sends a hook with:
66
- ```json
67
- {
68
- "name": "x402-payment",
69
- "wakeMode": "now",
70
- "data": {
71
- "routerAddress": "0x...",
72
- "routerName": "My Link",
73
- "previousBalance": "10.00",
74
- "newBalance": "15.00",
75
- "increase": "5.00",
76
- "detectedAt": "2025-01-15T12:00:00.000Z"
77
- }
78
- }
79
- ```
80
-
81
- ### Plugin Configuration
82
-
83
- All config is optional — zero-config if `~/.x402/.env` exists. Config priority:
84
-
85
- 1. Environment variables (highest)
86
- 2. Plugin config (from OpenClaw)
87
- 3. `~/.x402/.env` (dotenv)
88
- 4. Hardcoded defaults
89
-
90
- Plugin config keys (set in OpenClaw):
91
-
92
- | Key | Description | Default |
93
- |-----|-------------|---------|
94
- | `evmPrivateKey` | EVM private key (0x-prefixed) | from env |
95
- | `network` | `mainnet` or `testnet` | `mainnet` |
96
- | `maxPaymentUsd` | Max payment limit in USD | `10` |
97
- | `x402LinksApiUrl` | 21.cash API URL | `https://21.cash` |
98
- | `watcher.enabled` | Enable background payment watcher | `true` |
99
- | `watcher.pollIntervalMs` | Poll interval in ms | `30000` |
100
- | `watcher.notifyOnPayment` | Send hook on payment detection | `true` |
101
-
102
- ### Plugin CLI Commands
103
-
104
- ```bash
105
- openclaw x402 watch # Start watcher in foreground (debugging)
106
- openclaw x402 watch --interval 10000 # Custom poll interval
107
- openclaw x402 status # Show watcher state, tracked routers, payment count
108
- ```
109
-
110
- ## Installation
111
-
112
- ```bash
113
- npm i -g agentic-x402
114
- ```
115
-
116
- Once installed, the `x402` command is available globally:
117
-
118
- ```bash
119
- x402 --help
120
- x402 --version
121
- ```
122
-
123
- ## Setup
124
-
125
- Run the interactive setup to create a new wallet:
126
-
127
- ```bash
128
- x402 setup
129
- ```
130
-
131
- This will:
132
- 1. Generate a new wallet (recommended) or accept an existing key
133
- 2. Save configuration to `~/.x402/.env`
134
- 3. Display your wallet address for funding
135
-
136
- **Important:** Back up your private key immediately after setup!
137
-
138
- ### Manual Configuration
139
-
140
- Alternatively, set the environment variable directly:
141
-
142
- ```bash
143
- export EVM_PRIVATE_KEY=0x...your_private_key...
144
- ```
145
-
146
- Or create a config file:
147
-
148
- ```bash
149
- mkdir -p ~/.x402
150
- echo "EVM_PRIVATE_KEY=0x..." > ~/.x402/.env
151
- chmod 600 ~/.x402/.env
152
- ```
153
-
154
- Verify setup:
155
-
156
- ```bash
157
- x402 balance
158
- ```
159
-
160
- ## Paying for Resources
161
-
162
- ### When you encounter HTTP 402 Payment Required
163
-
164
- Use `x402 pay` to make the payment and access the content:
165
-
166
- ```bash
167
- x402 pay https://api.example.com/paid-endpoint
168
- ```
169
-
170
- The command will:
171
- 1. Check payment requirements
172
- 2. Verify amount is within limits
173
- 3. Process the payment
174
- 4. Return the gated content
175
-
176
- ### Automatic payment with fetch
177
-
178
- Use `x402 fetch` for seamless payment handling:
179
-
180
- ```bash
181
- x402 fetch https://api.example.com/data --json
182
- ```
183
-
184
- This wraps fetch with x402 payment handling - if the resource requires payment, it's handled automatically.
185
-
186
- ### Payment limits
187
-
188
- By default, payments are limited to $10 USD. Override with `--max`:
189
-
190
- ```bash
191
- x402 pay https://expensive-api.com/data --max 50
192
- ```
193
-
194
- Or set globally:
195
- ```bash
196
- export X402_MAX_PAYMENT_USD=25
197
- ```
198
-
199
- ### Dry run
200
-
201
- Preview payment without executing:
202
-
203
- ```bash
204
- x402 pay https://api.example.com/data --dry-run
205
- ```
206
-
207
- ## Creating Payment Links (Seller)
208
-
209
- Create payment links to monetize your own content using x402-links-server:
210
-
211
- ### Setup for link creation
212
-
213
- Add to `.env`:
214
- ```bash
215
- X402_LINKS_API_URL=https://your-x402-links-server.com
216
- ```
217
-
218
- ### Create a link
219
-
220
- Gate a URL:
221
- ```bash
222
- x402 create-link --name "Premium API" --price 1.00 --url https://api.example.com/premium
223
- ```
224
-
225
- Gate text content:
226
- ```bash
227
- x402 create-link --name "Secret" --price 0.50 --text "The secret message..."
228
- ```
229
-
230
- With webhook notification:
231
- ```bash
232
- x402 create-link --name "Guide" --price 5.00 --url https://mysite.com/guide --webhook https://mysite.com/payment-hook
233
- ```
234
-
235
- ### Get link info
236
-
237
- ```bash
238
- x402 link-info 0x1234...5678
239
- x402 link-info https://21.cash/pay/0x1234...5678
240
- ```
241
-
242
- ## Managing Routers
243
-
244
- ### List your routers
245
-
246
- See all payment routers where your wallet is a beneficiary:
247
-
248
- ```bash
249
- x402 routers
250
- x402 routers --with-balance # Include on-chain USDC balances
251
- x402 routers --json
252
- ```
253
-
254
- | Flag | Description | Default |
255
- |------|-------------|---------|
256
- | `--with-balance` | Fetch on-chain USDC balance for each router | — |
257
- | `--json` | Output as JSON | — |
258
- | `-h, --help` | Show help | — |
259
-
260
- ### Distribute funds
261
-
262
- Withdraw accumulated USDC from a PaymentRouter contract:
263
-
264
- ```bash
265
- x402 distribute 0x1234...5678
266
- x402 distribute 0x1234...5678 --amount 5.00
267
- x402 distribute 0x1234...5678 --force --json
268
- ```
269
-
270
- | Flag | Description | Default |
271
- |------|-------------|---------|
272
- | `<router-address>` | PaymentRouter contract address (positional) | **required** |
273
- | `--amount` | Specific USDC amount to distribute (defaults to full balance) | full balance |
274
- | `--force` | Skip gas balance warning | — |
275
- | `--json` | Output as JSON | — |
276
- | `-h, --help` | Show help | — |
277
-
278
- ## Command Reference
279
-
280
- ### x402 balance
281
-
282
- Check wallet balances.
283
-
284
- ```bash
285
- x402 balance [--json] [--full]
286
- ```
287
-
288
- | Flag | Description | Default |
289
- |------|-------------|---------|
290
- | `--json` | Output as JSON (address, network, chainId, balances) | — |
291
- | `--full` | Show full wallet address instead of truncated | — |
292
- | `-h, --help` | Show help | — |
293
-
294
- ### x402 pay
295
-
296
- Pay for an x402-gated resource.
297
-
298
- ```bash
299
- x402 pay <url> [options]
300
- ```
301
-
302
- | Flag | Description | Default |
303
- |------|-------------|---------|
304
- | `<url>` | The URL of the x402-gated resource (positional) | **required** |
305
- | `--method` | HTTP method | `GET` |
306
- | `--body` | Request body (for POST/PUT requests) | — |
307
- | `--header` | Add custom header (can be used multiple times) | — |
308
- | `--max` | Maximum payment in USD (overrides config) | from config |
309
- | `--dry-run` | Show payment details without paying | — |
310
- | `-h, --help` | Show help | — |
311
-
312
- ### x402 fetch
313
-
314
- Fetch with automatic payment.
315
-
316
- ```bash
317
- x402 fetch <url> [options]
318
- ```
319
-
320
- | Flag | Description | Default |
321
- |------|-------------|---------|
322
- | `<url>` | The URL to fetch (positional) | **required** |
323
- | `--method` | HTTP method | `GET` |
324
- | `--body` | Request body (for POST/PUT) | — |
325
- | `--header` | Add header as `"Key: Value"` | — |
326
- | `--json` | Output as JSON only (for piping to other tools) | — |
327
- | `--raw` | Output raw response body only (no headers or status) | — |
328
- | `-h, --help` | Show help | — |
329
-
330
- ### x402 create-link
331
-
332
- Create a payment link.
333
-
334
- ```bash
335
- x402 create-link --name <name> --price <usd> [options]
336
- ```
337
-
338
- | Flag | Description | Default |
339
- |------|-------------|---------|
340
- | `--name` | Name of the payment link | **required** |
341
- | `--price` | Price in USD (e.g., `"5.00"` or `"0.10"`) | **required** |
342
- | `--url` | URL to gate behind payment | — |
343
- | `--text` | Text content to gate behind payment | — |
344
- | `--desc` | Description of the link | — |
345
- | `--webhook` | Webhook URL for payment notifications | — |
346
- | `--json` | Output as JSON | — |
347
- | `-h, --help` | Show help | — |
348
-
349
- > **Note:** Either `--url` or `--text` is required. The link is deployed as a smart contract on Base.
350
-
351
- ### x402 link-info
352
-
353
- Get payment link details.
354
-
355
- ```bash
356
- x402 link-info <router-address> [--json]
357
- ```
358
-
359
- | Flag | Description | Default |
360
- |------|-------------|---------|
361
- | `<address>` | Router contract address or full payment URL (positional) | **required** |
362
- | `--json` | Output as JSON | — |
363
- | `-h, --help` | Show help | — |
364
-
365
- ## Environment Variables
366
-
367
- | Variable | Description | Default |
368
- |----------|-------------|---------|
369
- | `EVM_PRIVATE_KEY` | Wallet private key (0x-prefixed) | **required** |
370
- | `X402_NETWORK` | `mainnet` (Base, chain 8453) or `testnet` (Base Sepolia, chain 84532) | `mainnet` |
371
- | `X402_MAX_PAYMENT_USD` | Safety limit — payments exceeding this are rejected unless `--max` is used | `10` |
372
- | `X402_FACILITATOR_URL` | Custom facilitator URL | Coinbase (mainnet) / x402.org (testnet) |
373
- | `X402_SLIPPAGE_BPS` | Slippage tolerance in basis points (100 bps = 1%) | `50` |
374
- | `X402_VERBOSE` | Enable verbose logging (`1` = on, `0` = off) | `0` |
375
- | `X402_LINKS_API_URL` | Base URL of x402-links-server (e.g., `https://21.cash`) | — |
376
-
377
- ## Supported Networks
378
-
379
- | Network | Chain ID | CAIP-2 ID |
380
- |---------|----------|-----------|
381
- | Base Mainnet | 8453 | eip155:8453 |
382
- | Base Sepolia | 84532 | eip155:84532 |
383
-
384
- ## Payment Token
385
-
386
- All payments use **USDC** (USD Coin) on the selected network.
387
-
388
- - Base Mainnet: `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`
389
- - Base Sepolia: `0x036CbD53842c5426634e7929541eC2318f3dCF7e`
390
-
391
- ## How x402 Works
392
-
393
- 1. Client requests a resource
394
- 2. Server responds with `402 Payment Required` + payment details
395
- 3. Client signs a payment authorization (USDC transfer)
396
- 4. Client retries request with payment signature
397
- 5. Server verifies payment via facilitator
398
- 6. Server settles payment on-chain
399
- 7. Server returns the gated content
400
-
401
- The x402 protocol is gasless for buyers - the facilitator sponsors gas fees.
402
-
403
- ## Troubleshooting
404
-
405
- ### "Missing required environment variable: EVM_PRIVATE_KEY"
406
-
407
- Set your wallet private key:
408
- ```bash
409
- export EVM_PRIVATE_KEY=0x...
410
- ```
411
-
412
- Or create a `.env` file in your working directory, or install globally and use `~/.x402/.env`.
413
-
414
- ### "Payment exceeds max limit"
415
-
416
- Increase the limit:
417
- ```bash
418
- x402 pay https://... --max 50
419
- ```
420
-
421
- ### Low balance warnings
422
-
423
- Fund your wallet with:
424
- - **USDC** for payments
425
- - **ETH** for gas (small amount, ~0.001 ETH)
426
-
427
- ### Network mismatch
428
-
429
- Ensure your wallet has funds on the correct network:
430
- - `X402_NETWORK=mainnet` → Base mainnet
431
- - `X402_NETWORK=testnet` → Base Sepolia
432
-
433
- ## Backup Your Private Key
434
-
435
- Your private key is stored in `~/.x402/.env`. If lost, your funds cannot be recovered.
436
-
437
- ### Recommended Backup Methods
438
-
439
- 1. **Password Manager** (Recommended)
440
- - Store in 1Password, Bitwarden, or similar
441
- - Create a secure note with your private key
442
- - Tag it for easy retrieval
443
-
444
- 2. **Encrypted File**
445
- ```bash
446
- # Encrypt with GPG
447
- gpg -c ~/.x402/.env
448
- # Creates ~/.x402/.env.gpg - store this backup securely
449
- ```
450
-
451
- 3. **Paper Backup** (for larger amounts)
452
- - Write down the private key
453
- - Store in a safe or safety deposit box
454
- - Never store digitally unencrypted
455
-
456
- ### View Your Private Key
457
-
458
- ```bash
459
- cat ~/.x402/.env | grep EVM_PRIVATE_KEY
460
- ```
461
-
462
- ### Recovery
463
-
464
- To restore from backup:
465
- ```bash
466
- mkdir -p ~/.x402
467
- echo "EVM_PRIVATE_KEY=0x...your_backed_up_key..." > ~/.x402/.env
468
- chmod 600 ~/.x402/.env
469
- x402 balance # verify
470
- ```
471
-
472
- ## Security Best Practices
473
-
474
- - **Use a dedicated wallet** — Never use your main wallet with automated agents
475
- - **Limit funds** — Only transfer what you need for payments
476
- - **Set payment limits** — Configure `X402_MAX_PAYMENT_USD` to cap exposure
477
- - **Test first** — Use `X402_NETWORK=testnet` with test tokens before mainnet
478
- - **Protect the config** — `~/.x402/.env` has 600 permissions; keep it that way
479
- - **Never share** — Your private key gives full access to your wallet
480
-
481
- ## Links
482
-
483
- - [x402 Protocol Docs](https://docs.x402.org/)
484
- - [x402 GitHub](https://github.com/coinbase/x402)
485
- - [npm: agentic-x402](https://www.npmjs.com/package/agentic-x402)
486
- - [Base Network](https://base.org/)