@clawhub-wolflabs88-a8687e5872
Sell CrawlHub API keys for Twitter/X crawling via ETH payment. Use when a user wants to buy, access, or get pricing for CrawlHub API access. Handles wallet s...
---
name: crawlhub-reseller
description: Sell CrawlHub API keys for Twitter/X crawling via ETH payment. Use when a user wants to buy, access, or get pricing for CrawlHub API access. Handles wallet signature verification and payment verification on Ethereum. Works with other agents via A2A protocol.
---
# CrawlHub Reseller Skill
Sell CrawlHub API keys for Twitter/X crawling — fully automated with ETH payment verification.
## Overview
This skill wraps the CrawlHub Reseller Agent which:
- Verifies wallet ownership via Ethereum signature
- Verifies ETH payment on-chain via Etherscan
- Delivers API key + full API documentation
- Runs as standalone service on port 3000
## How It Works
```
Customer Agent → Signature + TX Hash → Reseller Agent → API Key + Docs
```
**Flow:**
1. Customer signs message with wallet (proves ownership)
2. Customer sends TX hash (proves payment)
3. Reseller verifies both on-chain
4. If valid → API key delivered with CrawlHub API docs
## Payment
- **Price:** 0.010 ETH per 24 hours
- **Payment wallet:** 0x19c4455Bf8C5D8662B434e1985cd31B8947A7C39
- **Verification:** Etherscan API check
## Customer Request Format
```json
{
"customerWallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f5bA12",
"signature": "0x1234abcd...",
"message": "Request API Key for CrawlHub\nWallet: 0x742d35Cc6634C0532925a3b844Bc9e7595f5bA12\nNonce: abc12345",
"txHash": "0xabc123def456..."
}
```
## Reseller Agent API
**Endpoints:**
- `POST /json-rpc` — A2A JSON-RPC endpoint (tasks/send, agentcard/get)
- `GET /agent-card` — Agent capabilities and endpoints
- `GET /health` — Health check
**Start Reseller:**
```bash
cd /root/.openclaw/workspace/reseller-agent
node dist/server.js
```
## CrawlHub API Docs
Full API documentation in `references/crawlhub-api.md`.
**Base URL:** `https://api.thecrawlhub.com/api/v1`
**Auth:**
- `POST /auth/login` → JWT token
- Use `Authorization: Bearer {token}` + `X-API-KEY: {key}`
**Endpoints:**
- `GET /profile/user/by-screen-name?screen_name={username}`
- `GET /profile/tweets/by-screen-name?screen_name={username}`
- `GET /timeline/search?query={query}&mode=top`
- `GET /tweet/by-id?tweet_id={id}`
## Verification Results
On successful sale, notify via:
1. Update `/tmp/reseller-events.json` with event
2. Check `/root/.openclaw/workspace/reseller-agent/notifications.json`
## Error Codes
- `Signature verification failed` — Wallet signature doesn't match
- `Insufficient payment` — Less than 0.010 ETH sent
- `Payment sent to wrong address` — TX wasn't to our wallet
- `No API keys available` — Pool exhausted
FILE:README.md
# CrawlHub Reseller Skill
A monetizable OpenClaw skill for selling CrawlHub API access via ETH payment.
## Structure
```
crawlhub-reseller-skill/
├── SKILL.md # Main skill file
├── scripts/
│ ├── request-api-key.sh # CLI tool for customers
│ └── sign-request.sh # Message signing helper
└── references/
└── crawlhub-api.json # Full API documentation
```
## For Customers
### To get an API Key:
1. **Generate message to sign:**
```bash
./scripts/sign-request.sh YOUR_WALLET_ADDRESS
```
2. **Sign the message** with your Ethereum wallet (Metamask, Rabby, etc.)
3. **Send 0.010 ETH** to: `0x19c4455Bf8C5D8662B434e1985cd31B8947A7C39`
4. **Get TX hash** from your wallet transaction
5. **Request API key** via Reseller Agent (contact your admin)
## For Admins
### Start Reseller Agent:
```bash
cd /root/.openclaw/workspace/reseller-agent
node dist/server.js
```
### Check Status:
```bash
curl http://localhost:3000/health
curl http://localhost:3000/agent-card
```
### Pricing
- 0.010 ETH = 24 hours access
- Unlimited API calls during validity
## Payment Verification
1. **Signature verification** — `ethers.verifyMessage()` confirms wallet ownership
2. **TX verification** — Etherscan API confirms payment to our wallet
Both must pass before API key is delivered.
## Publishing to ClawHub
1. Create GitHub repo with skill files
2. Login to clawhub.ai with GitHub
3. Publish skill via "Publish Skill" button
4. Set pricing (optional) or free with ETH payment
FILE:scripts/request-api-key.sh
#!/bin/bash
# CrawlHub Reseller Client - Request API Key
# Usage: ./request-api-key.sh <wallet> <signature> <message> <txHash>
WALLET="-"
SIGNATURE="-"
MESSAGE="-"
TX_HASH="-"
if [ -z "$WALLET" ] || [ -z "$SIGNATURE" ] || [ -z "$MESSAGE" ] || [ -z "$TX_HASH" ]; then
echo "Usage: request-api-key.sh <wallet> <signature> <message> <txHash>"
echo "Example: ./request-api-key.sh 0x742d... 0x1234... \"Request API Key...\" 0xabc..."
exit 1
fi
curl -s -X POST http://localhost:3000/json-rpc \
-H "Content-Type: application/json" \
-d "{
\"jsonrpc\": \"2.0\",
\"id\": 1,
\"method\": \"tasks/send\",
\"params\": {
\"message\": {
\"role\": \"user\",
\"parts\": [{
\"type\": \"text\",
\"text\": JSON.stringify({
\"customerWallet\": \"$WALLET\",
\"signature\": \"$SIGNATURE\",
\"message\": \"$MESSAGE\",
\"txHash\": \"$TX_HASH\"
})
}]
}
}
}" | python3 -c "
import sys, json
d = json.load(sys.stdin)
result = d.get('result', {})
status = result.get('status', {})
if status.get('state') == 'completed':
artifacts = result.get('artifacts', [])
for a in artifacts:
for p in a.get('parts', []):
if p.get('type') == 'data':
data = p.get('data', {})
print('SUCCESS: API Key =', data.get('apiKey'))
print('Expires:', data.get('expiresAt'))
else:
msg = status.get('message', {})
for p in msg.get('parts', []):
print('ERROR:', p.get('text', 'Unknown error'))
"
FILE:scripts/sign-request.sh
#!/bin/bash
# Generate a signed message for CrawlHub API request
# Requires: an Ethereum wallet with private key (e.g., Metamask)
WALLET="-"
NONCE="-$(date +%s)"
if [ -z "$WALLET" ]; then
echo "Usage: sign-request.sh <wallet_address> [nonce]"
echo "Generates the message to sign for API key request"
exit 1
fi
MESSAGE="Request API Key for CrawlHub
Wallet: $WALLET
Nonce: $NONCE"
echo "=== MESSAGE TO SIGN ==="
echo "$MESSAGE"
echo "======================="
echo ""
echo "Sign this message with your Ethereum wallet (e.g., Metamask)"
echo "Then use the signature + message when requesting API key"
FILE:references/crawlhub-api.json
{
"openapi": "3.0.0",
"info": {
"title": "CrawlHub API",
"description": "X (Twitter) Data Crawling API - Access tweets, profiles, followers, timelines and more.\n\n## Authentication\n1. Login to get JWT: `POST /auth/login`\n2. Use JWT as Bearer token + your API Key in X-API-KEY header\n\n## Base URL\n```\nhttps://api.thecrawlhub.com/api/v1\n```\n\n## Pricing\n- **0.015 ETH per 24 hours** - Unlimited crawling for Platform X during the validity period\n- Payment to: `0x19c4455Bf8C5D8662B434e1985cd31B8947A7C39`",
"version": "1.0.0",
"contact": {
"name": "CrawlHub Support"
}
},
"servers": [
{
"url": "https://api.thecrawlhub.com/api/v1",
"description": "Production"
}
],
"paths": {
"/auth/login": {
"post": {
"summary": "Login to get JWT token",
"tags": ["Authentication"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["email", "password"],
"properties": {
"email": {"type": "string", "example": "[email protected]"},
"password": {"type": "string", "example": "yourpassword"}
}
}
}
}
},
"responses": {
"200": {
"description": "Login successful",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"token": {"type": "string", "description": "JWT Bearer token"},
"refresh_token": {"type": "string"}
}
}
}
}
}
}
}
}
}
},
"/auth/refresh": {
"post": {
"summary": "Refresh JWT token",
"tags": ["Authentication"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["refresh_token"],
"properties": {
"refresh_token": {"type": "string"}
}
}
}
}
},
"responses": {
"200": {
"description": "Token refreshed"
}
}
}
},
"/execution/endpoints/{endpointId}/execute": {
"post": {
"summary": "Execute any endpoint",
"tags": ["Execution"],
"parameters": [
{
"name": "endpointId",
"in": "path",
"required": true,
"schema": {"type": "integer"},
"description": "Endpoint ID (see Endpoints section)"
}
],
"headers": {
"Authorization": {
"type": "string",
"description": "Bearer {JWT from login}",
"required": true
},
"X-API-KEY": {
"type": "string",
"description": "Your API key",
"required": true
}
},
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"params": {
"type": "object",
"description": "Endpoint-specific parameters"
}
}
}
}
}
},
"responses": {
"200": {"description": "Execution successful"},
"401": {"description": "Authentication error"}
}
}
},
"/profile/user/by-id": {
"get": {
"summary": "Get User Profile by ID",
"tags": ["Profile"],
"parameters": [
{"name": "user_id", "in": "query", "required": true, "schema": {"type": "string"}}
],
"responses": {
"200": {"description": "User profile data"}
}
}
},
"/profile/user/by-screen-name": {
"get": {
"summary": "Get User Profile by Screen Name",
"tags": ["Profile"],
"parameters": [
{"name": "screen_name", "in": "query", "required": true, "schema": {"type": "string"}, "example": "elonmusk"}
],
"responses": {
"200": {"description": "User profile data"}
}
}
},
"/profile/tweets/by-id": {
"get": {
"summary": "Get Profile Tweets by User ID",
"tags": ["Profile"],
"parameters": [
{"name": "user_id", "in": "query", "required": true, "schema": {"type": "string"}}
],
"responses": {
"200": {"description": "Tweets from user"}
}
}
},
"/profile/tweets/by-screen-name": {
"get": {
"summary": "Get Profile Tweets by Screen Name",
"tags": ["Profile"],
"parameters": [
{"name": "screen_name", "in": "query", "required": true, "schema": {"type": "string"}, "example": "elonmusk"}
],
"responses": {
"200": {"description": "Tweets from user"}
}
}
},
"/timeline/search": {
"get": {
"summary": "Search Timeline",
"tags": ["Timeline"],
"parameters": [
{"name": "query", "in": "query", "required": true, "schema": {"type": "string"}, "description": "Search query"},
{"name": "mode", "in": "query", "schema": {"type": "string", "enum": ["latest", "top", "people", "media", "lists"]}, "description": "Search mode"},
{"name": "since", "in": "query", "schema": {"type": "string"}, "description": "Start date (YYYY-MM-DD)"},
{"name": "until", "in": "query", "schema": {"type": "string"}, "description": "End date (YYYY-MM-DD)"}
],
"responses": {
"200": {"description": "Search results"}
}
}
},
"/timeline/by-id": {
"get": {
"summary": "Get Timeline by User ID (with Followers/Following support)",
"tags": ["Timeline"],
"parameters": [
{"name": "user_id", "in": "query", "required": true, "schema": {"type": "string"}},
{"name": "initial_page", "in": "query", "schema": {"type": "string", "enum": ["Followers", "Following"]}, "description": "For crawling followers/following lists"},
{"name": "cursor", "in": "query", "schema": {"type": "string"}, "description": "Pagination cursor"},
{"name": "context", "in": "query", "schema": {"type": "integer"}, "description": "Request context (max 100)"}
],
"responses": {
"200": {"description": "Timeline data with pagination"}
}
}
},
"/timeline/by-screen-name": {
"get": {
"summary": "Get Timeline by Screen Name (with Followers/Following support)",
"tags": ["Timeline"],
"parameters": [
{"name": "screen_name", "in": "query", "required": true, "schema": {"type": "string"}},
{"name": "initial_page", "in": "query", "schema": {"type": "string", "enum": ["Followers", "Following"]}},
{"name": "cursor", "in": "query", "schema": {"type": "string"}}
],
"responses": {
"200": {"description": "Timeline data"}
}
}
},
"/timeline/conversation": {
"get": {
"summary": "Get Timeline Conversation",
"tags": ["Timeline"],
"parameters": [
{"name": "tweet_id", "in": "query", "required": true, "schema": {"type": "string"}}
],
"responses": {
"200": {"description": "Conversation thread"}
}
}
},
"/tweet/by-id": {
"get": {
"summary": "Get Tweet by ID",
"tags": ["Tweet"],
"parameters": [
{"name": "tweet_id", "in": "query", "required": true, "schema": {"type": "string"}}
],
"responses": {
"200": {"description": "Tweet data"}
}
}
},
"/scraper/platforms": {
"get": {
"summary": "Get All Platforms",
"tags": ["Admin"],
"responses": {
"200": {"description": "List of all platforms"}
}
}
},
"/scraper/platforms/{platformId}": {
"get": {
"summary": "Get Platform Details",
"tags": ["Admin"],
"parameters": [
{"name": "platformId", "in": "path", "required": true, "schema": {"type": "integer"}}
],
"responses": {
"200": {"description": "Platform info with modules and endpoints"}
}
}
}
},
"tags": [
{"name": "Authentication", "description": "Login and token management"},
{"name": "Profile", "description": "User profile endpoints"},
{"name": "Timeline", "description": "Timeline and follower crawling"},
{"name": "Tweet", "description": "Single tweet retrieval"},
{"name": "Execution", "description": "Execute any endpoint"},
{"name": "Admin", "description": "Platform and endpoint management"}
],
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
},
"apiKey": {
"type": "apiKey",
"in": "header",
"name": "X-API-KEY"
}
}
}
}