SDK Reference
Complete guide for the JarvisClaw Python and Go SDKs. Both SDKs handle authentication, the x402 payment flow, streaming, retries, and error handling out of the box.
Installation
pip install jarvisclaw
# x402 wallet payments on Base (EVM)
pip install jarvisclaw[agent]
# x402 wallet payments on Solana
pip install jarvisclaw[solana]
# asyncio clients
pip install jarvisclaw[async]
# everything
pip install jarvisclaw[all]go get github.com/api-jarvisclaw/go-sdk/v2@latestChoosing a client
The Python SDK is organised as one client per surface, plus two aggregate entry points. Pick the smallest one that covers your use case.
| Client | Use for |
|---|---|
Agent | Autonomous loops — ask(), run() with tools, budget tracking |
JarvisClaw | The AIP surface — resolve(), execute(), stream(), audit() |
IntentClient | AIP with preferences and analytics helpers |
ChatClient | Chat completions only |
ImageClient / VideoClient / AudioClient | Media generation |
SearchClient | Web search and page extraction |
MarketplaceClient | Raw marketplace service calls |
WalletClient | Balance, history, limits, pools |
PromptCoachClient | Prompt optimization |
NetworkClient | Network peers and crawling |
OpenAI | Drop-in openai-shaped client (client.chat.completions.create) |
The Go SDK exposes one *Client carrying every method, plus thin typed wrappers (ChatClient, ImageClient, VideoClient, AudioClient, SearchClient, MarketplaceClient) created via NewChatClient(...) and friends.
Initialization
Two authentication modes — API Key (managed billing) or Private Key (autonomous x402 settlement).
from jarvisclaw import JarvisClaw
client = JarvisClaw(api_key="sk-your-api-key")from jarvisclaw import JarvisClaw
# Agent signs payment directly; no managed account needed.
# Hex key → Base (EVM). Base58 keypair → Solana. Detected from the key format.
client = JarvisClaw(private_key="0x<your-hex-private-key>")package main
import jc "github.com/api-jarvisclaw/go-sdk/v2"
func main() {
client, err := jc.NewClient(jc.WithAPIKey("sk-your-api-key"))
if err != nil {
panic(err)
}
_ = client
}package main
import jc "github.com/api-jarvisclaw/go-sdk/v2"
func main() {
// EVM hex key for Base. Solana is Python-only today.
client, err := jc.NewClient(jc.WithPrivateKey("0x<your-hex-private-key>"))
if err != nil {
panic(err)
}
_ = client
}Both constructors return an error in Go
jc.NewClient and every jc.NewXxxClient return (*Client, error). Ignoring the second value will not compile.
Environment variables:
export JARVISCLAW_API_KEY=sk-...
export JARVISCLAW_WALLET_KEY=0x... # wallet private key (hex or base58)
export JARVISCLAW_BASE_URL=https://api.jarvisclaw.ai # defaultIf env vars are set, you can initialize with no arguments. JARVISCLAW_API_KEY wins when both are present.
client = JarvisClaw() # reads from envclient, err := jc.NewClient() // reads from envTimeouts
Timeout is set per client, not per request.
from jarvisclaw import JarvisClaw
client = JarvisClaw(api_key="sk-...", timeout=60) # seconds; default 120import (
"context"
"time"
jc "github.com/api-jarvisclaw/go-sdk/v2"
)
client, _ := jc.NewClient(jc.WithAPIKey("sk-..."), jc.WithTimeout(60*time.Second))
// Per-call deadlines use the context you pass to each method:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := client.Execute(ctx, jc.ExecuteRequest{
Intent: "chat_completion",
Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Hello"}}},
})In Python the SDK retries 429 and 5xx up to 3 times with exponential backoff. There is no per-call timeout argument — construct a second client if you need a different budget.
Quickest path: Agent
Agent wraps intent resolution and chat into a single call and tracks spend for you.
from jarvisclaw import Agent
agent = Agent(api_key="sk-your-api-key", default_budget=1.00)
# One line: resolve the best model within budget, then call it
print(agent.ask("Explain quantum computing in 3 sentences", budget=0.01, optimize="cost"))
# Streaming
for chunk in agent.stream("Write a poem about AI agents"):
print(chunk, end="", flush=True)
# Autonomous loop with tools
@agent.tool
def calculator(expression: str) -> str:
"""Evaluate a math expression."""
return str(eval(expression))
result = agent.run("What is 2^100 + 3^50?")
print(result.text)
print(result.cost) # CostTracker — spend for this run
print(result.iterations)ctx := context.Background()
client, _ := jc.NewClient(jc.WithAPIKey("sk-your-api-key"))
// Resolve within budget, then chat — one call
text, err := client.Ask(ctx, "Explain quantum computing in 3 sentences",
jc.AskOptions{Budget: 0.01, Optimize: "cost"})
if err != nil {
log.Fatal(err)
}
fmt.Println(text)optimize / Optimize accepts cost, quality, or latency. Defaults to cost.
Intent Examples
JarvisClaw.execute() takes intent and payload positionally, then optional budget and constraints keywords. The response is the resolved provider's raw body — for chat_completion that is an OpenAI-shaped completion.
1. Chat Completion (chat_completion)
result = client.execute(
"chat_completion",
{
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in 3 sentences."},
],
"temperature": 0.7,
"max_tokens": 200,
},
budget={"max_total_usd": 0.05},
)
print(result["choices"][0]["message"]["content"])raw, err := client.Execute(ctx, jc.ExecuteRequest{
Intent: "chat_completion",
Payload: map[string]any{
"messages": []map[string]any{
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in 3 sentences."},
},
"temperature": 0.7,
"max_tokens": 200,
},
})
if err != nil {
log.Fatal(err)
}
// Execute returns json.RawMessage — the provider's body, unmodified.
var out struct {
Choices []struct {
Message struct{ Content string } `json:"message"`
} `json:"choices"`
}
_ = json.Unmarshal(raw, &out)
fmt.Println(out.Choices[0].Message.Content)Need preferences?
JarvisClaw.execute() does not accept preferences. Use IntentClient when you want to steer selection:
from jarvisclaw import IntentClient
intent = IntentClient(api_key="sk-...")
result = intent.execute(
"chat_completion",
{"messages": [{"role": "user", "content": "Hello"}]},
constraints={"max_price_usd": 0.05},
preferences={"optimize_for": "quality"},
)2. Streaming
Streaming goes through /v1/intent/subscribe and yields SSE events, not plain strings. Each event is {"event": ..., "data": ...}.
for event in client.stream(
"chat_completion",
{"messages": [{"role": "user", "content": "Write a poem about AI agents."}]},
):
if event["event"] == "chunk":
print(event["data"].get("content", ""), end="", flush=True)
elif event["event"] == "done":
print(f"\n\nCost: ${event['data'].get('actual_cost_usd')}")stream, err := client.Subscribe(ctx, jc.SubscribeRequest{
Intent: "chat_completion",
Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Write a poem about AI agents."}}},
})
if err != nil {
log.Fatal(err)
}
defer stream.Close()
for {
ev, err := stream.Next()
if err != nil {
break
}
fmt.Printf("[%s] %v\n", ev.Event, ev.Data)
}For plain token streaming without AIP, ChatClient.stream() (Python) and ChatClient.Stream() (Go) yield text chunks directly:
from jarvisclaw import ChatClient
chat = ChatClient(api_key="sk-...")
for chunk in chat.stream("Tell me a joke"):
print(chunk, end="")cc, _ := jc.NewChatClient(jc.WithAPIKey("sk-..."))
stream, _ := cc.Stream(ctx, "Tell me a joke")
for chunk := range stream.Channel() {
fmt.Print(chunk)
}3. Image Generation (image_generation)
result = client.execute(
"image_generation",
{"prompt": "A cyberpunk cityscape at sunset", "size": "1024x1024"},
budget={"max_total_usd": 0.08},
)
print(result["data"][0]["url"])
print(f"Cost: ${result['price']['amount']} USD")// Or use the dedicated client, which parses the response for you:
ic, _ := jc.NewImageClient(jc.WithAPIKey("sk-..."))
img, err := ic.Generate(ctx, "A cyberpunk cityscape at sunset", jc.WithSize("1024x1024"))
if err != nil {
log.Fatal(err)
}
fmt.Println(img.URL)The image response is OpenAI-shaped with two JarvisClaw additions: a top-level id and a price object. Use data[0].url for the CDN link.
4. Video Generation (video_generation)
Video is asynchronous — submit, then poll. The dedicated client handles polling.
from jarvisclaw import VideoClient
video = VideoClient(api_key="sk-your-api-key")
# Blocking — SDK polls until the MP4 is ready
job = video.generate("A drone flying over a tropical island",
model="bytedance/seedance-2.0", duration=5)
print(job.url)
# Non-blocking
job = video.generate("Ocean waves at sunset", wait=False)
print(job.id, job.status) # e.g. "bytedance:video_24d2...", "queued"
# ... later ...
print(video.status(job.id).url)vc, _ := jc.NewVideoClient(jc.WithAPIKey("sk-your-api-key"))
job, err := vc.Generate(ctx, "A drone flying over a tropical island",
jc.WithVideoModel("bytedance/seedance-2.0"), jc.WithDuration(5), jc.WithWait(true))
if err != nil {
log.Fatal(err)
}
fmt.Println(job.URL)Job IDs are provider-prefixed (bytedance:video_xxx), not vg_xxx. Generation takes 60–180s typically; allow more headroom for high resolutions. See Video Generation.
5. Text to Speech (text_to_speech)
from jarvisclaw import AudioClient
audio = AudioClient(api_key="sk-your-api-key")
# Returns AudioResponse with raw bytes — the SDK follows the CDN URL for you
result = audio.speech("Welcome to JarvisClaw.", model="elevenlabs/flash-v2.5", voice="sarah")
with open("output.mp3", "wb") as f:
f.write(result.content)ac, _ := jc.NewAudioClient(jc.WithAPIKey("sk-your-api-key"))
result, err := ac.Speech(ctx, "Welcome to JarvisClaw.",
jc.WithAudioModel("elevenlabs/flash-v2.5"), jc.WithVoice("sarah"))
if err != nil {
log.Fatal(err)
}
os.WriteFile("output.mp3", result.Data, 0644)auto/tts and x402 wallets
Smart-route TTS has failed to settle for direct-wallet callers in testing. Pass an explicit ElevenLabs model when using private_key. Voice defaults to sarah.
6. Speech to Text
Transcription is multipart upload, not base64 in JSON.
from jarvisclaw import AudioClient
audio = AudioClient(api_key="sk-your-api-key")
with open("recording.mp3", "rb") as f:
text = audio.transcribe(f, model="whisper-1", language="en")
print(text)curl https://api.jarvisclaw.ai/v1/audio/transcriptions \
-H "Authorization: Bearer sk-your-api-key" \
-F file="@recording.mp3" \
-F model="whisper-1"transcribe() returns the transcript as a plain string. There is no Go helper for transcription yet — post multipart to /v1/audio/transcriptions directly.
7. Web Search (web_search)
from jarvisclaw import SearchClient
search = SearchClient(api_key="sk-your-api-key")
# Goes to /v1/search (smart-routed summary), returns list[SearchResult]
for r in search.query("latest AI agent frameworks 2026", num_results=5):
print(r.title, r.url, r.snippet[:80])
# Structured Exa results with real per-result URLs
contents = search.contents(["https://example.com/article"])sc, _ := jc.NewSearchClient(jc.WithAPIKey("sk-your-api-key"))
resp, err := sc.Query(ctx, "latest AI agent frameworks 2026", jc.WithNumResults(5))
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Summary, resp.SourcesUsed)
for _, r := range resp.Citations {
fmt.Printf(" %s: %s\n", r.Title, r.URL)
}query() returns a summary, not ranked links
SearchClient.query() posts to /v1/search with model=auto/search, which returns an AI-written summary with inline citations. When the response has no structured citations the Python SDK yields a single SearchResult(title="Search Result", url="", snippet=<summary>) — so .url is often empty. For per-result titles and URLs, call POST /v1/marketplace/exa/search directly. See Web Search.
Advanced Features
Budget Control
execute_budget() lives on IntentClient and takes budget as a required third positional argument. The only field the server reads is max_total_usd (plus optional preferred_payment_method and allow_overdraft).
from jarvisclaw import IntentClient
intent = IntentClient(private_key="0x<agent-wallet-key>")
result = intent.execute_budget(
"chat_completion",
{"messages": [{"role": "user", "content": "Summarize this document..."}]},
{"max_total_usd": 0.50},
)
print(result["status"]) # "success" | "rejected" | "error"
print(result.get("risk_level"))
print(result.get("actual_cost_usd"))resp, err := client.ExecuteBudget(ctx, jc.ExecuteBudgetRequest{
Intent: "chat_completion",
Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Summarize this document..."}}},
Budget: jc.Budget{MaxTotalUSD: 0.50},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("status=%s risk=%s\n", resp.Status, resp.RiskLevel)
if resp.ActualCostUSD != nil {
fmt.Printf("cost=%.6f\n", *resp.ActualCostUSD)
}Per-call and daily caps
per_call_limit_usd and daily_limit_usd are not fields on this request. Set persistent limits with PUT /v1/wallet/limits (per_request_max_usd, daily_max_usd, monthly_max_usd). See Wallet & Treasury.
Natural Language Resolution
Describe what you need in plain language and AIP resolves the intent by embedding similarity. This endpoint resolves only — it does not execute or charge. Take the returned provider and call it yourself.
# No SDK helper yet — call the endpoint directly
result = client._post("/v1/intent/resolve/natural", json={
"query": "I want to generate an image of a cat wearing sunglasses",
"constraints": {"max_price_usd": 0.10},
})
if result["status"] == "resolved":
print(result["intent"], result["confidence"])
for m in result.get("matches", []):
print(f" {m['provider_name']}: ${m.get('price_usd')} → {m.get('endpoint')}")
elif result["status"] == "clarify":
print(result["clarify"]["question"])
for opt in result["clarify"].get("options", []):
print(f" - {opt}")curl -X POST https://api.jarvisclaw.ai/v1/intent/resolve/natural \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{"query": "generate an image of a cat wearing sunglasses"}'status is one of resolved, clarify, budget_insufficient, no_match. On clarify the follow-up lives in a clarify object (question, options, round) — not a top-level message/options pair.
Resolve (Without Executing)
matches = client.resolve(
"chat_completion",
constraints={
"max_price_usd": 0.05,
"max_latency_ms": 3000,
"features": ["function_calling", "json_mode"],
},
)
for m in matches["matches"]:
print(f"{m['provider_id']}: ${m['estimated_price_usd']}, score={m['score']}, {m['reason']}")
print(f"{matches['total_available']} providers available for {matches['intent_type']}")maxPrice, maxLatency := 0.05, 3000
resp, err := client.Resolve(ctx, jc.ResolveRequest{
Intent: "chat_completion",
Constraints: jc.Constraints{
MaxPriceUSD: &maxPrice,
MaxLatencyMS: &maxLatency,
Features: []string{"function_calling", "json_mode"},
},
Preferences: jc.Preferences{OptimizeFor: "quality"},
})
if err != nil {
log.Fatal(err)
}
for _, m := range resp.Matches {
fmt.Printf("%s: $%.6f score=%.2f %s\n", m.ProviderID, m.EstimatedPriceUSD, m.Score, m.Reason)
}Go constraint fields are pointers
MaxPriceUSD is *float64 and MaxLatencyMS is *int. Take the address of a variable — a literal will not compile. Constraints and Preferences are values on ResolveRequest but pointers on ExecuteRequest.
Match fields are provider_id, score, estimated_price_usd, pricing, endpoint, model, reason. There is no resolution_id or expires_at — resolutions are not reserved, so nothing expires.
Audit Log
entries = client.audit() # no parameters; scope comes from your credentials
for e in entries["entries"]:
print(f"[{e['timestamp']}] {e['event_type']} req={e['request_id']}")
print(f" {e.get('details')}")
print(f"{entries['count']} entries")resp, err := client.Audit(ctx)
if err != nil {
log.Fatal(err)
}
for _, e := range resp.Entries {
fmt.Printf("[%s] %s req=%s %v\n", e.Timestamp, e.EventType, e.RequestID, e.Details)
}Audit is not filterable
GET /v1/intent/audit accepts no query parameters and returns {entries, count} — there is no total/page/page_size, and no filtering by date or intent type. Each entry is {timestamp, request_id, user_id, event_type, details}; event_type is a lifecycle event (intent_resolved, execution_complete, settlement_confirmed, …), not the intent name. Python's audit() takes no arguments either — it always calls /v1/intent/audit. For spend history use the analytics methods below.
Cost Analytics
from jarvisclaw import IntentClient
intent = IntentClient(api_key="sk-...")
# Aggregated spend and settlement rows
print(intent.spend(period="7d", group_by=["day", "model"]))
# Convenience wrappers over spend()
print(intent.cost_by_model(period="7d"))
print(intent.daily_trend(period="30d"))
# Mined quality signals and the deep-scan summary
print(intent.quality(period="7d"))
print(intent.insights(period="7d"))rows, _ := client.Spend(ctx, jc.AnalyticsParams{
Period: "7d",
GroupBy: []string{"day", "model"},
})
// Convenience wrappers over Spend
byModel, _ := client.CostByModel(ctx, jc.AnalyticsParams{Period: "7d"})
trend, _ := client.DailyTrend(ctx, jc.AnalyticsParams{Period: "30d"})
quality, _ := client.QualityMetrics(ctx, jc.AnalyticsParams{Period: "7d"})
insights, _ := client.Insights(ctx, jc.AnalyticsParams{Period: "7d"})These map to /api/analytics/{aggregate,quality,insights}.
period accepts "24h", "7d" (default), "30d" or "90d"; any other value falls back to "7d" server-side. group_by accepts day, model, api_source, principal_type, channel, group and client_id, defaulting to day,model,api_source.
Scope is enforced server-side from the auth context. Calling with an API token (sk-...), as the SDK does, always returns only your own rows: user_id is ignored on this path, even for a token owned by an admin. Widening the scope with user_id works only for admins signed in to the dashboard (session auth). AIP usage shows up in the same rows with api_source="aip" — the older /v1/aip/analytics/* endpoints have been removed.
Network
Peer listing and crawling require admin credentials.
from jarvisclaw import NetworkClient
net = NetworkClient(api_key="sk-admin-key")
print(net.list_peers())
print(net.crawl())peers, _ := client.NetworkPeers(ctx)
_, _ = client.NetworkCrawl(ctx)Browsing and executing network resources is public and has no SDK wrapper yet — call the endpoints directly:
# Natural-language resource recommendation (free)
curl -X POST https://api.jarvisclaw.ai/v1/network/recommend \
-H "Content-Type: application/json" \
-d '{"intent": "I need to analyze stock market data", "limit": 5}'
# Full-text search across network resources (free)
curl "https://api.jarvisclaw.ai/v1/network/search?q=stock&limit=5"
# Execute a network resource (paid)
curl -X POST https://api.jarvisclaw.ai/v1/network/execute \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{"resource_id": 532, "payload": {"ticker": "AAPL"}}'resource_id is the numeric federation_resources.id from /v1/network/apis, not a slug. See AIP.
Error Handling
from jarvisclaw import (
JarvisClaw,
JarvisClawError,
APIError,
AuthenticationError,
RateLimitError,
InsufficientBalanceError,
PaymentError,
)
client = JarvisClaw(private_key="0x...")
try:
result = client.execute(
"chat_completion",
{"messages": [{"role": "user", "content": "Hello"}]},
)
except AuthenticationError as e:
print(f"Bad key [{e.status_code}]: {e.message}")
except InsufficientBalanceError as e:
print(f"Top up USDC [{e.status_code}]: {e.message}")
print(f"Server said: {e.body}")
except RateLimitError as e:
print(f"Rate limited; retry after {e.retry_after}s")
except APIError as e:
print(f"API error [{e.status_code}]: {e.message}")
except JarvisClawError as e:
print(f"SDK error: {e}")import (
"errors"
jc "github.com/api-jarvisclaw/go-sdk/v2"
)
raw, err := client.Execute(ctx, jc.ExecuteRequest{
Intent: "chat_completion",
Payload: map[string]any{"messages": []map[string]any{{"role": "user", "content": "Hello"}}},
})
if err != nil {
var authErr *jc.AuthenticationError
var balErr *jc.InsufficientBalanceError
var rateErr *jc.RateLimitError
var apiErr *jc.APIError
switch {
case errors.As(err, &authErr):
fmt.Printf("bad key: %s\n", authErr.Message)
case errors.As(err, &balErr):
fmt.Printf("top up USDC: %s\n", balErr.Message)
case errors.As(err, &rateErr):
fmt.Printf("rate limited: %s\n", rateErr.Message)
case errors.As(err, &apiErr):
fmt.Printf("api error [%d]: %s\n", apiErr.StatusCode, apiErr.Message)
default:
fmt.Printf("transport error: %v\n", err)
}
}
_ = rawPython exception hierarchy: JarvisClawError → APIError (carries status_code, message, body) → AuthenticationError / RateLimitError / InsufficientBalanceError. PaymentError extends JarvisClawError for x402 signing failures. RateLimitError.retry_after reads retry_after from the response body.
Agent raises its own BudgetExceededError (.budget, .spent) when a run exceeds its cap.
Go types mirror this: APIError with StatusCode/Message/Body, embedded into AuthenticationError, RateLimitError, InsufficientBalanceError; PaymentError embeds JarvisClawError. None carry a .Type discriminator — switch on the concrete type or StatusCode.
Async (Python)
import asyncio
from jarvisclaw.aio import ChatClient, ImageClient
async def main():
async with ChatClient(private_key="0x...") as chat, \
ImageClient(private_key="0x...") as image:
text, img = await asyncio.gather(
chat.complete("What is AIP?"),
image.generate("A cat on Mars"),
)
print(text, img.url)
async for chunk in chat.stream("Tell me a story"):
print(chunk, end="")
asyncio.run(main())Available async clients: ChatClient, ImageClient, VideoClient, AudioClient, SearchClient, MarketplaceClient, WalletClient, IntentClient. Requires pip install jarvisclaw[async]. See Async & Concurrent.
OpenAI Drop-in
from jarvisclaw import OpenAI
client = OpenAI(api_key="sk-your-api-key")
resp = client.chat.completions.create(
model="auto",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)This shim adds x402 support to the familiar shape. If you would rather use the official openai package, just point base_url at https://api.jarvisclaw.ai/v1 — see Quick Start.
Full Example: Agent Loop
from jarvisclaw import Agent, BudgetExceededError
agent = Agent(private_key="0x<agent-wallet-key>", default_budget=5.00)
def agent_loop(instructions: list[str]):
for instruction in instructions:
try:
reply = agent.ask(instruction, budget=0.05)
except BudgetExceededError as e:
print(f"Stopped: spent ${e.spent:.4f} of ${e.budget:.4f}")
return
print(f"Agent: {reply}")
print(f"Wallet balance: ${agent.get_balance():.4f} USDC")
agent_loop([
"What are the tradeoffs of vector databases?",
"Summarize that in one sentence.",
])package main
import (
"context"
"fmt"
"log"
jc "github.com/api-jarvisclaw/go-sdk/v2"
)
func main() {
client, err := jc.NewClient(jc.WithPrivateKey("0x<agent-wallet-key>"))
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
for _, instruction := range []string{
"What are the tradeoffs of vector databases?",
"Summarize that in one sentence.",
} {
reply, err := client.Ask(ctx, instruction, jc.AskOptions{Budget: 0.05})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Agent: %s\n", reply)
}
balance, _ := client.GetBalance(ctx)
fmt.Printf("Wallet balance: $%.4f USDC\n", balance)
}For multi-turn conversations that keep history, use agent.run(...) (Python) or build the message array yourself and call ChatClient.Completion(ctx, messages, ...) (Go).
Links
- Protocol spec: AIP Protocol
- Payments: Agent Payments (x402)
- Live endpoint:
https://api.jarvisclaw.ai - Python SDK source: github.com/api-jarvisclaw/python-sdk
- Go SDK source: github.com/api-jarvisclaw/go-sdk
- Community: https://t.me/JarvisClawai