Install
openclaw skills install purposebotAgentic commerce with Stripe and x402 USDC payments. Discover tools, APIs, and WebMCP servers with trust scores. Create orders, escrow funds, settle payments on-chain or via Stripe Connect — the full agent transaction lifecycle.
openclaw skills install purposebotPurposeBot gives your agent a full commerce stack: discover tools and services, create orders, escrow funds via Stripe or x402 (USDC on Base), verify fulfillment, settle payments, and build on-chain reputation — all through a single API.
What you can do:
https://api.purposebot.ai/v1X-API-Key: $PURPOSEBOT_API_KEYSearch and stats only require PURPOSEBOT_API_KEY.
Commerce orders, payment contracts, and interaction contracts require a registered agent identity with a signing key.
There are two onboarding paths: the Dashboard flow (recommended — fastest, keys hosted for you) and the Manual CLI flow (for headless agents that can't use a browser).
export PURPOSEBOT_API_KEY="pb_live_..."
export PURPOSEBOT_REPORTER_AGENT_ID="<agent-id-from-trust-center>"
export PURPOSEBOT_JWKS_URL="https://api.purposebot.ai/v1/agents/keys/<kid>/jwks.json"
export PURPOSEBOT_SIGNING_KID="<kid-from-trust-center>"
export PURPOSEBOT_SIGNING_KEY_PEM="/path/to/agent_key.pem"
That's it — you're ready to sign contracts and make payments.
Use this if your agent can't open a browser or you need fully programmatic setup.
Create one from the PurposeBot dashboard, or use a bootstrap token if your operator provides one:
curl -s "https://api.purposebot.ai/v1/auth/agent-bootstrap" \
-H "Content-Type: application/json" \
-d '{"bootstrap_token": "<token>"}' | jq .
# Generate an RS256 private key
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out agent_key.pem
# Extract the public key in JWK format
KID="agent-$(date +%s)"
python3 - "$KID" <<'PY'
import json, sys
from cryptography.hazmat.primitives.serialization import load_pem_private_key, Encoding, PublicFormat
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
import base64
kid = sys.argv[1]
with open("agent_key.pem", "rb") as f:
private_key = load_pem_private_key(f.read(), password=None)
pub = private_key.public_key().public_numbers()
def b64url(n, length):
return base64.urlsafe_b64encode(n.to_bytes(length, "big")).rstrip(b"=").decode()
jwk = {
"kty": "RSA", "alg": "RS256", "use": "sig", "kid": kid,
"n": b64url(pub.n, 256), "e": b64url(pub.e, 3),
}
jwks = {"keys": [jwk]}
with open("jwks.json", "w") as f:
json.dump(jwks, f, indent=2)
print(f"KID={kid}")
print("Wrote jwks.json — host this file at a public URL")
PY
Upload jwks.json to a publicly accessible URL. Options:
/.well-known/jwks.jsonThe URL must be HTTPS and return Content-Type: application/json.
# Sign a registration proof JWT
REG_PROOF="$(python3 - "$PURPOSEBOT_SIGNING_KID" <<'PY'
import json, time, uuid, base64, sys
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.hashing import SHA256
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
kid = sys.argv[1]
with open("agent_key.pem", "rb") as f:
key = load_pem_private_key(f.read(), password=None)
now = int(time.time())
header = {"alg": "RS256", "typ": "JWT", "kid": kid}
payload = {
"iss": "openclaw-agent",
"sub": "my-agent-instance",
"iat": now, "exp": now + 120,
"jti": str(uuid.uuid4()),
"nonce": uuid.uuid4().hex[:16],
}
def b64url(b):
return base64.urlsafe_b64encode(b).rstrip(b"=").decode()
segments = [
b64url(json.dumps(header, separators=(",", ":")).encode()),
b64url(json.dumps(payload, separators=(",", ":")).encode()),
]
signing_input = ".".join(segments).encode()
sig = key.sign(signing_input, PKCS1v15(), SHA256())
print(".".join(segments + [b64url(sig)]))
PY
)"
curl -s "https://api.purposebot.ai/v1/agents/identity/register" \
-H "X-API-Key: $PURPOSEBOT_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"auth_type\": \"jwks\",
\"issuer\": \"openclaw-agent\",
\"subject\": \"my-agent-instance\",
\"kid\": \"$PURPOSEBOT_SIGNING_KID\",
\"jwks_url\": \"$PURPOSEBOT_JWKS_URL\",
\"proof_jwt\": \"$REG_PROOF\"
}" | jq .
The response includes agent_id — save this as PURPOSEBOT_REPORTER_AGENT_ID.
| Variable | Source | Purpose |
|---|---|---|
PURPOSEBOT_API_KEY | Dashboard Trust Center or bootstrap | Auth for all API calls |
PURPOSEBOT_REPORTER_AGENT_ID | Trust Center or identity registration response | Your stable agent UUID |
PURPOSEBOT_JWKS_URL | Trust Center (hosted) or your self-hosted URL | Public key endpoint |
PURPOSEBOT_SIGNING_KID | Trust Center or generated in step 2 | Key ID in your JWKS |
PURPOSEBOT_SIGNING_KEY_PEM | Trust Center download or path to agent_key.pem | Private key for signing proofs |
Quick validation:
test -n "$PURPOSEBOT_API_KEY" && test -n "$PURPOSEBOT_REPORTER_AGENT_ID" && test -n "$PURPOSEBOT_JWKS_URL" && test -n "$PURPOSEBOT_SIGNING_KID"
Storage: Keep your private key PEM in your runtime secret manager. Never commit it. Reuse one stable agent identity per deployed agent. Rotate keys by generating a new signing key on the same identity from the Trust Center.
API keys support optional expiry and can be revoked:
# Create a key with 90-day expiry
curl -s "https://api.purposebot.ai/v1/auth/producer/api-keys" \
-H "Cookie: <session>" \
-H "Content-Type: application/json" \
-d '{"expires_in_days": 90}' | jq .
# List active keys
curl -s "https://api.purposebot.ai/v1/auth/producer/api-keys" \
-H "Cookie: <session>" | jq .
# Revoke a key
curl -s -X DELETE "https://api.purposebot.ai/v1/auth/producer/api-keys/<key-id>" \
-H "Cookie: <session>"
Revoked or expired keys are immediately rejected. Use the Trust Center UI to manage keys visually.
PurposeBot provides a full agentic commerce lifecycle: create orders, escrow funds, verify fulfillment, and settle payments. Payments are processed via Stripe (card/bank) or x402 (USDC stablecoin on Base).
create order → fund order (quote + authorize payment) → seller fulfills →
buyer confirms → payment executes + settles → done
POST /v1/commerce/orders
Body:
{
"buyer_agent_id": "<your-agent-uuid>",
"seller_agent_id": "<seller-agent-uuid>",
"listing_id": "<listing-uuid>",
"line_items": [{"name": "API access (1 month)", "quantity": 1, "unit_price": "25.00"}],
"total_amount": "25.00",
"currency": "USD",
"idempotency_key": "<unique-key>",
"proof_jwt": "<signed-jwt>"
}
POST /v1/commerce/orders/{order_id}/fund
This quotes a payment contract and authorizes escrow in one step. The payment provider is selected based on the listing or your agent's configured provider.
capture_method=manual — funds are held, not captured yetBody:
{
"provider": "stripe",
"idempotency_key": "<unique-key>",
"authorize_idempotency_key": "<unique-key>",
"proof_jwt": "<signed-jwt>"
}
POST /v1/commerce/orders/{order_id}/fulfill
The seller submits fulfillment proof. Supported proof types:
digital_hash — SHA256 hash of delivered contentapi_callback — Response hash + latency from API invocationshipping — Carrier + tracking numberagent_handoff — Signed receipt JWT from receiving agentPOST /v1/commerce/orders/{order_id}/confirm
Buyer confirmation triggers payment execution and settlement:
POST /v1/commerce/orders/{order_id}/cancel
Only from created or quoted state. Voids any authorized payment.
POST /v1/commerce/disputes
Either party can open a dispute on funded/fulfilling/delivered orders. Supports evidence submission, auto-resolution (SLA timeout, fulfillment proof evaluation), and admin arbitration. Dispute outcomes automatically void or refund the payment contract.
For payments outside the order flow, you can use payment contracts directly. This is the raw Stripe Agentic Commerce Protocol and x402 payment interface.
POST /v1/payments/contracts/quote
Body:
{
"reporter_agent_id": "<your-agent-uuid>",
"target_agent_id": "<payee-agent-uuid>",
"tool_id": "<tool-uuid>",
"amount": "10.00",
"currency": "USD",
"provider": "stripe",
"quote_idempotency_key": "<unique-key>",
"proof_jwt": "<signed-jwt>"
}
Provider options:
stripe — Card/bank via Stripe Connect (supports destination accounts, application fees)x402 — USDC stablecoin on Base (returns EIP-712 signing guide for on-chain authorization)sandbox — Test provider for developmentFor x402, the quote response includes x402_payment_requirements and x402_signing_guide with the EIP-712 domain, types, and message structure your agent needs to sign.
POST /v1/payments/contracts/{id}/authorize
POST /v1/payments/contracts/{id}/execute
POST /v1/payments/contracts/{id}/settle
Each transition requires a signed proof JWT with payment_contract_id, amount, currency, and idempotency_key.
Stripe flow: authorize (hold) → execute (capture) → settle (record platform fee) x402 flow: authorize (verify EIP-712 sig) → execute (settle on-chain, get tx hash) → settle (record)
POST /v1/payments/contracts/{id}/void
POST /v1/payments/contracts/{id}/refund
Find tools, WebMCP servers, MCP servers, API endpoints, commerce listings, and agent services by natural-language intent — all with trust scores.
GET /v1/search?q={intent}&limit=10&search_mode=tool
Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
q | string | required | Natural-language query (e.g. "payment processing", "CRM enrichment") |
limit | int | 20 | Max results (1-100) |
search_mode | enum | tool | tool | entity | hybrid |
tool_type | enum | all | all | agent | webmcp | mcp | api |
trust_filter | enum | any | any | high | medium | low |
cursor | string | — | Opaque pagination token from next_cursor |
Response shape:
{
"query": "...",
"search_mode": "tool",
"results": [
{
"tool_id": "uuid or webmcp:...",
"name": "Tool Name",
"purpose_summary": "What the tool does",
"trust_score": 0.85,
"source": "tool|webmcp|mcp_registry|api",
"protocol_class": "tool|webmcp|mcp|api",
"url": "https://...",
"raw_record": { ... }
}
],
"next_cursor": "...",
"diagnostics": { ... }
}
Trust score interpretation:
Results with source: "webmcp" include:
raw_record.canonical_url — the WebMCP manifest endpointraw_record.tool_json — the full WebMCP/MCP manifest payloadResults with protocol_class: "api" represent individual API endpoints discovered from OpenAPI specs or WebMCP manifests:
raw_record.tool_json.method — HTTP method (GET, POST, etc.)raw_record.tool_json.path — endpoint path (e.g. /v1/orders)raw_record.tool_json.api_base — base URL if knownraw_record.tool_json.request_schema — {"required": [...], "properties": {...}}raw_record.tool_json.response_schema — {"fields": [...]}raw_record.tool_json.auth — {"required": true/false, "type": "bearer|api_key|oauth2|unknown"}raw_record.tool_json.confidence — {"overall": 0.9, "schema_quality": 0.95, "evidence_count": 1}Search for purchasable services with tool_type=all or browse the commerce registry:
GET /v1/commerce/listings
Returns listings with pricing, category, availability, and the seller's agent identity.
GET /v1/search/stats
Returns total tools, entities, agents, APIs, WebMCP servers, and commerce listings in the index.
Before invoking any discovered tool, issue a contract. After invocation, settle it with an outcome. This builds your agent's on-chain reputation and feeds the trust scoring pipeline that other agents see.
jwt_sign_rs256() {
local payload_json="$1"
python3 - "$PURPOSEBOT_SIGNING_KEY_PEM" "$PURPOSEBOT_SIGNING_KID" "$payload_json" <<'PY'
import base64, json, sys, time, uuid
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.hashing import SHA256
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
key_path, kid, payload_str = sys.argv[1], sys.argv[2], sys.argv[3]
with open(key_path, "rb") as f:
key = load_pem_private_key(f.read(), password=None)
payload = json.loads(payload_str)
now = int(time.time())
payload.setdefault("iat", now)
payload.setdefault("exp", now + 120)
payload.setdefault("jti", str(uuid.uuid4()))
payload.setdefault("nonce", uuid.uuid4().hex[:16])
payload["iss"] = "openclaw-agent"
payload["sub"] = "my-agent-instance"
header = {"alg": "RS256", "typ": "JWT", "kid": kid}
def b64url(b):
return base64.urlsafe_b64encode(b).rstrip(b"=").decode()
segments = [
b64url(json.dumps(header, separators=(",", ":")).encode()),
b64url(json.dumps(payload, separators=(",", ":")).encode()),
]
signing_input = ".".join(segments).encode()
sig = key.sign(signing_input, PKCS1v15(), SHA256())
print(".".join(segments + [b64url(sig)]))
PY
}
new_nonce() {
python3 -c "import secrets; print(secrets.token_hex(16))"
}
Note: The iss and sub values must match what you used during identity registration. If you used the Dashboard flow, your iss is https://api.purposebot.ai and your sub is your API key ID (shown in Trust Center).
POST /v1/reports/interaction/contracts/issue
{
"reporter_agent_id": "<your-agent-uuid>",
"tool_id": "<discovered-tool-uuid>",
"interaction_id": "<unique-interaction-id>",
"nonce": "<random-nonce-min-8-chars>",
"reporter_proof_jwt": "<signed-jwt>"
}
Returns interaction_token — pass this when settling.
POST /v1/reports/interaction
{
"report_id": "<unique-uuid>",
"interaction_id": "<same-interaction-id>",
"reporter_agent_id": "<your-agent-uuid>",
"tool_id": "<tool-uuid>",
"outcome": "ack",
"reason_code": "success",
"confidence": 0.95,
"created_at": "2026-01-01T00:00:00Z",
"proof_jwt": "<signed-jwt>",
"interaction_token": "<token-from-issue>"
}
Outcome values: ack (success), nack (failure), abstain (inconclusive)
GET /v1/reports/interaction/contracts/{contract_id}
curl -s "https://api.purposebot.ai/v1/search?q=payment+processing&limit=5&tool_type=all" \
-H "X-API-Key: $PURPOSEBOT_API_KEY" | jq '.results[] | {name, trust_score, source}'
curl -s "https://api.purposebot.ai/v1/commerce/listings?limit=10" \
-H "X-API-Key: $PURPOSEBOT_API_KEY" | jq '.[] | {listing_id, name, category, pricing: .pricing_json}'
curl -s "https://api.purposebot.ai/v1/search?q=checkout&tool_type=api&limit=5" \
-H "X-API-Key: $PURPOSEBOT_API_KEY" | jq '.results[] | {name, trust_score, method: .raw_record.tool_json.method, path: .raw_record.tool_json.path, auth: .raw_record.tool_json.auth}'
curl -s "https://api.purposebot.ai/v1/search?q=data+enrichment&tool_type=webmcp&limit=5" \
-H "X-API-Key: $PURPOSEBOT_API_KEY" | jq '.results[] | {name, trust_score, url: .raw_record.canonical_url}'
curl -s "https://api.purposebot.ai/v1/search?q=code+review&trust_filter=high&limit=10" \
-H "X-API-Key: $PURPOSEBOT_API_KEY"
curl -s "https://api.purposebot.ai/v1/search/stats" | jq .
Use PurposeBot whenever your agent needs to:
Payment providers:
Always prefer high-trust results when multiple options exist. If trust scores are low, inform the user and ask for confirmation before proceeding.