v3.2: x402 discovery endpoint, demo client, hackathon prep
- Added /x402 endpoint: machine-readable service catalog for agent discovery - Added x402-demo-client.mjs: demonstrates full x402 payment flow - Updated README with three distribution layers and discovery docs - Service catalog includes pricing, input/output schemas, MCP info, Bazaar reference
This commit is contained in:
parent
0ddd7f2774
commit
120f033cf5
66
README.md
66
README.md
@ -1,26 +1,50 @@
|
|||||||
# SnapAPI v3.1
|
# SnapAPI v3.2
|
||||||
|
|
||||||
Screenshot, PDF, and Markdown conversion API with x402 micropayments and MCP support.
|
Screenshot, PDF, and Markdown conversion API with x402 micropayments, Bazaar discovery, and MCP support.
|
||||||
|
|
||||||
|
**Live:** [api.opspawn.com/screenshot-api](https://api.opspawn.com/screenshot-api/)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- URL screenshot capture (PNG, JPEG)
|
- URL screenshot capture (PNG, JPEG)
|
||||||
- PDF generation from URLs
|
- PDF generation from URLs
|
||||||
- Markdown to PDF/PNG/HTML conversion
|
- Markdown to PDF/PNG/HTML conversion
|
||||||
- **x402 micropayments** - pay-per-request with USDC on Base (no signup needed)
|
- **x402 micropayments** - pay-per-request with USDC on Base (no signup needed)
|
||||||
- **Bazaar discovery** - machine-readable API schemas for AI agent auto-discovery
|
- **Bazaar discovery** - machine-readable API schemas for AI agent auto-discovery
|
||||||
|
- **Service catalog** - `/x402` endpoint for programmatic service discovery
|
||||||
- **MCP server** - Model Context Protocol integration for LLM agents
|
- **MCP server** - Model Context Protocol integration for LLM agents
|
||||||
- API key authentication (traditional)
|
- API key authentication (traditional)
|
||||||
- Rate limiting and concurrency control
|
- Rate limiting and concurrency control
|
||||||
- SSRF protection
|
- SSRF protection
|
||||||
|
|
||||||
|
## Three Distribution Layers
|
||||||
|
|
||||||
|
SnapAPI implements three complementary ways for AI agents to discover and use it:
|
||||||
|
|
||||||
|
| Layer | How It Works | For |
|
||||||
|
|-------|-------------|-----|
|
||||||
|
| **x402 Micropayments** | HTTP 402 → sign USDC → get result | Agents with wallets |
|
||||||
|
| **Bazaar Discovery** | JSON Schema in 402 response headers | Auto-discovery |
|
||||||
|
| **MCP Tools** | stdio transport for Claude Code/Desktop | LLM agent environments |
|
||||||
|
|
||||||
|
## x402 Service Discovery
|
||||||
|
|
||||||
|
Query `/x402` for a machine-readable catalog of all services:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://api.opspawn.com/screenshot-api/x402
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns structured JSON with all endpoints, pricing, input/output schemas, and payment details. Designed for agent-to-agent service discovery.
|
||||||
|
|
||||||
## x402 Micropayments
|
## x402 Micropayments
|
||||||
|
|
||||||
Pay per request with USDC on Base network. No signup, no API keys needed.
|
Pay per request with USDC on Base network. No signup, no API keys needed.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Without payment, you get a 402 response with payment requirements:
|
# Without payment, get a 402 response with payment requirements:
|
||||||
curl https://api.opspawn.com/screenshot-api/api/capture?url=https://example.com
|
curl https://api.opspawn.com/screenshot-api/api/capture?url=https://example.com
|
||||||
# Returns: HTTP 402 with Payment-Required header
|
# Returns: HTTP 402 with Payment-Required header + Bazaar discovery metadata
|
||||||
|
|
||||||
# With x402 payment signature:
|
# With x402 payment signature:
|
||||||
curl -H "Payment-Signature: <signed-authorization>" \
|
curl -H "Payment-Signature: <signed-authorization>" \
|
||||||
@ -31,6 +55,14 @@ curl -H "Payment-Signature: <signed-authorization>" \
|
|||||||
- Screenshot capture: $0.01/request
|
- Screenshot capture: $0.01/request
|
||||||
- Markdown to PDF: $0.005/request
|
- Markdown to PDF: $0.005/request
|
||||||
- Markdown to PNG: $0.005/request
|
- Markdown to PNG: $0.005/request
|
||||||
|
- Markdown to HTML: Free
|
||||||
|
|
||||||
|
### Demo Client
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run the x402 demo to see the full payment flow:
|
||||||
|
node x402-demo-client.mjs --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
## MCP Server
|
## MCP Server
|
||||||
|
|
||||||
@ -39,10 +71,7 @@ Use SnapAPI as an MCP tool server for Claude Code, Claude Desktop, or any MCP-co
|
|||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install dependencies
|
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# Run MCP server (stdio transport)
|
|
||||||
node mcp-server.mjs
|
node mcp-server.mjs
|
||||||
|
|
||||||
# With custom API URL and key
|
# With custom API URL and key
|
||||||
@ -78,24 +107,19 @@ Add to `claude_desktop_config.json`:
|
|||||||
| `markdown_to_html` | Convert Markdown to styled HTML (free) |
|
| `markdown_to_html` | Convert Markdown to styled HTML (free) |
|
||||||
| `api_status` | Check service health and stats |
|
| `api_status` | Check service health and stats |
|
||||||
|
|
||||||
### Available Resources
|
|
||||||
|
|
||||||
| Resource | URI | Description |
|
|
||||||
|----------|-----|-------------|
|
|
||||||
| API Docs | `snapapi://docs` | Full API documentation |
|
|
||||||
|
|
||||||
## Traditional API
|
## Traditional API
|
||||||
|
|
||||||
### Endpoints
|
### Endpoints
|
||||||
|
|
||||||
| Method | Path | Auth | Description |
|
| Method | Path | Auth | Price | Description |
|
||||||
|--------|------|------|-------------|
|
|--------|------|------|-------|-------------|
|
||||||
| GET | `/api/capture` | Required | Capture screenshot/PDF from URL |
|
| GET | `/x402` | Free | - | Service discovery catalog |
|
||||||
| POST | `/api/md2pdf` | Required | Markdown to PDF |
|
| GET | `/api/capture` | Required | $0.01 | Capture screenshot/PDF from URL |
|
||||||
| POST | `/api/md2png` | Required | Markdown to PNG/JPEG |
|
| POST | `/api/md2pdf` | Required | $0.005 | Markdown to PDF |
|
||||||
| POST | `/api/md2html` | Free | Markdown to HTML |
|
| POST | `/api/md2png` | Required | $0.005 | Markdown to PNG/JPEG |
|
||||||
| GET | `/api/status` | Free | Service health |
|
| POST | `/api/md2html` | Free | - | Markdown to HTML |
|
||||||
| GET | `/api/pricing` | Free | Subscription plans |
|
| GET | `/api/status` | Free | - | Service health |
|
||||||
|
| GET | `/api/pricing` | Free | - | Subscription plans |
|
||||||
|
|
||||||
### Quick Start
|
### Quick Start
|
||||||
|
|
||||||
|
|||||||
82
server.js
82
server.js
@ -349,6 +349,88 @@ const server = http.createServer(async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// x402 discovery endpoint - machine-readable service catalog for agents
|
||||||
|
if (parsed.pathname === '/x402') {
|
||||||
|
sendJson(res, 200, {
|
||||||
|
protocol: 'x402',
|
||||||
|
version: '2.3',
|
||||||
|
service: {
|
||||||
|
name: 'SnapAPI',
|
||||||
|
description: 'Screenshot & document generation API with x402 micropayments',
|
||||||
|
provider: 'OpSpawn',
|
||||||
|
website: 'https://opspawn.com',
|
||||||
|
github: 'https://github.com/opspawn/screenshot-api',
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
network: x402.NETWORK,
|
||||||
|
token: 'USDC',
|
||||||
|
facilitator: 'https://facilitator.payai.network',
|
||||||
|
wallet: x402.WALLET_ADDRESS,
|
||||||
|
},
|
||||||
|
endpoints: [
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
path: '/api/capture',
|
||||||
|
description: 'Capture screenshot or PDF from a URL',
|
||||||
|
price: x402.PRICES.capture,
|
||||||
|
input: {
|
||||||
|
url: { type: 'string', required: true, description: 'URL to capture' },
|
||||||
|
format: { type: 'string', required: false, description: 'png, jpeg, or pdf', default: 'png' },
|
||||||
|
width: { type: 'number', required: false, description: 'Viewport width (320-3840)', default: 1280 },
|
||||||
|
height: { type: 'number', required: false, description: 'Viewport height (200-2160)', default: 800 },
|
||||||
|
fullPage: { type: 'boolean', required: false, description: 'Capture full scrollable page' },
|
||||||
|
},
|
||||||
|
output: { type: 'binary', mimeTypes: ['image/png', 'image/jpeg', 'application/pdf'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
path: '/api/md2pdf',
|
||||||
|
description: 'Convert Markdown to PDF document',
|
||||||
|
price: x402.PRICES.md2pdf,
|
||||||
|
input: {
|
||||||
|
markdown: { type: 'string', required: true, description: 'Markdown content' },
|
||||||
|
theme: { type: 'string', required: false, description: 'light or dark', default: 'light' },
|
||||||
|
paperSize: { type: 'string', required: false, description: 'A4, Letter, Legal, Tabloid', default: 'A4' },
|
||||||
|
},
|
||||||
|
output: { type: 'binary', mimeTypes: ['application/pdf'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
path: '/api/md2png',
|
||||||
|
description: 'Convert Markdown to PNG image',
|
||||||
|
price: x402.PRICES.md2png,
|
||||||
|
input: {
|
||||||
|
markdown: { type: 'string', required: true, description: 'Markdown content' },
|
||||||
|
theme: { type: 'string', required: false, description: 'light or dark', default: 'light' },
|
||||||
|
width: { type: 'number', required: false, description: 'Viewport width', default: 800 },
|
||||||
|
},
|
||||||
|
output: { type: 'binary', mimeTypes: ['image/png', 'image/jpeg'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
path: '/api/md2html',
|
||||||
|
description: 'Convert Markdown to styled HTML (free, no payment required)',
|
||||||
|
price: '$0.00',
|
||||||
|
input: {
|
||||||
|
markdown: { type: 'string', required: true, description: 'Markdown content' },
|
||||||
|
theme: { type: 'string', required: false, description: 'light or dark', default: 'light' },
|
||||||
|
},
|
||||||
|
output: { type: 'text', mimeTypes: ['text/html'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
mcp: {
|
||||||
|
description: 'Also available as MCP tools for Claude Code/Desktop',
|
||||||
|
tools: ['capture_screenshot', 'markdown_to_pdf', 'markdown_to_image', 'markdown_to_html', 'api_status'],
|
||||||
|
install: 'npx github:opspawn/screenshot-api/mcp-server.mjs',
|
||||||
|
},
|
||||||
|
bazaar: {
|
||||||
|
description: 'x402 Bazaar discovery extensions included in 402 responses',
|
||||||
|
specification: 'https://github.com/x402/x402/tree/main/extensions/bazaar',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// API docs - JSON
|
// API docs - JSON
|
||||||
if (parsed.pathname === '/api') {
|
if (parsed.pathname === '/api') {
|
||||||
sendJson(res, 200, {
|
sendJson(res, 200, {
|
||||||
|
|||||||
102
x402-demo-client.mjs
Normal file
102
x402-demo-client.mjs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// x402-demo-client.mjs - Demonstrates the x402 payment flow with SnapAPI
|
||||||
|
// Usage: node x402-demo-client.mjs [endpoint] [--dry-run]
|
||||||
|
//
|
||||||
|
// This script shows how an AI agent can discover and pay for API services
|
||||||
|
// using the x402 protocol. No signup, no API keys - just HTTP + USDC.
|
||||||
|
|
||||||
|
import { createPublicClient, createWalletClient, http, parseAbi } from 'viem';
|
||||||
|
import { base } from 'viem/chains';
|
||||||
|
|
||||||
|
const SNAPAPI_URL = process.env.SNAPAPI_URL || 'https://api.opspawn.com/screenshot-api';
|
||||||
|
const dryRun = process.argv.includes('--dry-run');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('=== x402 Payment Flow Demo ===\n');
|
||||||
|
|
||||||
|
// Step 1: Discover available services
|
||||||
|
console.log('1. Discovering services at', SNAPAPI_URL + '/x402');
|
||||||
|
const discovery = await fetch(SNAPAPI_URL + '/x402');
|
||||||
|
const catalog = await discovery.json();
|
||||||
|
|
||||||
|
console.log(` Service: ${catalog.service.name} by ${catalog.service.provider}`);
|
||||||
|
console.log(` Payment: ${catalog.payment.token} on ${catalog.payment.network}`);
|
||||||
|
console.log(` Endpoints:`);
|
||||||
|
for (const ep of catalog.endpoints) {
|
||||||
|
console.log(` ${ep.method} ${ep.path} - ${ep.description} (${ep.price})`);
|
||||||
|
}
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
// Step 2: Make an unauthenticated request to get payment requirements
|
||||||
|
console.log('2. Requesting screenshot without payment...');
|
||||||
|
const captureUrl = `${SNAPAPI_URL}/api/capture?url=https://example.com`;
|
||||||
|
const noAuthResponse = await fetch(captureUrl);
|
||||||
|
|
||||||
|
console.log(` Status: ${noAuthResponse.status} (${noAuthResponse.statusText})`);
|
||||||
|
|
||||||
|
if (noAuthResponse.status === 402) {
|
||||||
|
// Extract x402 payment requirements from response
|
||||||
|
const paymentRequired = noAuthResponse.headers.get('payment-required');
|
||||||
|
if (paymentRequired) {
|
||||||
|
try {
|
||||||
|
const requirements = JSON.parse(Buffer.from(paymentRequired, 'base64').toString());
|
||||||
|
console.log(' Payment Requirements:');
|
||||||
|
console.log(` Scheme: ${requirements.scheme || 'exact'}`);
|
||||||
|
console.log(` Network: ${requirements.network}`);
|
||||||
|
console.log(` Pay to: ${requirements.payTo}`);
|
||||||
|
console.log(` Price: ${requirements.maxAmountRequired} (raw USDC units)`);
|
||||||
|
|
||||||
|
if (requirements.extensions) {
|
||||||
|
console.log(' Bazaar Discovery Extensions:');
|
||||||
|
console.log(` ${JSON.stringify(requirements.extensions, null, 2).split('\n').join('\n ')}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(' Payment-Required header (base64):', paymentRequired.substring(0, 80) + '...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log();
|
||||||
|
|
||||||
|
if (dryRun) {
|
||||||
|
console.log('3. [DRY RUN] Skipping actual payment. In production, an agent would:');
|
||||||
|
console.log(' a. Read the payment requirements from the 402 response');
|
||||||
|
console.log(' b. Sign a USDC authorization using their wallet private key');
|
||||||
|
console.log(' c. Retry the request with Payment-Signature header');
|
||||||
|
console.log(' d. Receive the screenshot as the response body');
|
||||||
|
console.log(' e. The facilitator settles the USDC transfer on-chain');
|
||||||
|
console.log('\n Total cost: $0.01 per screenshot, settled on Base L2');
|
||||||
|
console.log(' No signup. No API key. Just HTTP + USDC.\n');
|
||||||
|
} else {
|
||||||
|
console.log('3. To complete payment, an x402-enabled agent would sign the authorization');
|
||||||
|
console.log(' with their wallet and retry the request. See @x402/client for details.\n');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const body = await noAuthResponse.text();
|
||||||
|
console.log(` Response: ${body.substring(0, 200)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Show the free endpoint (md2html doesn't need payment)
|
||||||
|
console.log('4. Testing free endpoint (md2html - no payment needed)...');
|
||||||
|
const md2htmlResponse = await fetch(`${SNAPAPI_URL}/api/md2html`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
markdown: '# Hello from x402 Demo\n\nThis was generated by an AI agent using the x402 protocol.',
|
||||||
|
theme: 'dark'
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(` Status: ${md2htmlResponse.status}`);
|
||||||
|
if (md2htmlResponse.ok) {
|
||||||
|
const html = await md2htmlResponse.text();
|
||||||
|
console.log(` Response: ${html.length} bytes of styled HTML`);
|
||||||
|
console.log(` Preview: ${html.substring(0, 100)}...`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n=== Demo Complete ===');
|
||||||
|
console.log('Learn more: https://x402.org | https://github.com/opspawn/screenshot-api');
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(err => {
|
||||||
|
console.error('Error:', err.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user