Guides

Connectors

Credential requirements, auth methods, rate limits, and code examples for all 11 built-in connectors.

Clavis ships with 11 ready-to-use connectors. To use any connector, register a service with that connector_name, store credentials, and call get_token(). The connector handles all auth logic.

connector_name is the string you pass when registering a service. It must exactly match one of the names listed in each section below (e.g., "openai", "brave-search").
🤖

OpenAI

api_key auth format validation
connector_name
openai
Base URL
api.openai.com
Rate Limit
60 RPM (GPT-4) · 3,500 RPM (GPT-3.5)
Token Expiry
Never

Required credentials

FieldDescription
api_keyrequiredOpenAI API key. Must start with sk-. Get one at platform.openai.com/api-keys.
python — register & use
# 1. Register the service
import httpx
async with httpx.AsyncClient() as http:
    svc = (await http.post("https://clavisagent.com/v1/services",
        headers={"Authorization": f"Bearer {jwt}"},
        json={"name": "my-openai", "connector_name": "openai"})).json()

    # 2. Store credentials
    await http.post(f"https://clavisagent.com/v1/services/{svc['id']}/credentials",
        headers={"Authorization": f"Bearer {jwt}"},
        json={"token_type": "api_key", "data": {"api_key": "sk-..."}})

# 3. Get token in your agent
token = await client.get_token("my-openai")
# token.access_token = "sk-..."  — inject into Authorization: Bearer
🧠

Anthropic

api_key auth format validation
connector_name
anthropic
Base URL
api.anthropic.com
Rate Limit
50 RPM (Tier 1 default)
Token Expiry
Never
Header differs from OpenAI. Anthropic uses x-api-key, not Authorization: Bearer. Clavis injects the correct header automatically when proxying.

Required credentials

FieldDescription
api_keyrequiredAnthropic API key. Must start with sk-ant-. Get one at console.anthropic.com.
python — register & use
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "api_key", "data": {"api_key": "sk-ant-..."}})

# Proxy injects x-api-key + anthropic-version headers automatically
response = await client.proxy("my-anthropic", "POST", "/v1/messages",
    body={"model": "claude-sonnet-4-6", "max_tokens": 1024,
          "messages": [{"role": "user", "content": "Hello"}]})
🐙

GitHub

api_key auth live validation
connector_name
github
Base URL
api.github.com
Rate Limit
5,000 req / hour
Token Expiry
Never (PATs)
Live validation: Clavis calls GET /user when credentials are first stored to confirm the token is valid.

Required credentials

FieldDescription
api_keyrequiredPersonal Access Token (classic: ghp_, fine-grained: github_pat_). Create at github.com/settings/tokens.
python — register & use
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "api_key", "data": {"api_key": "ghp_..."}})

# List repos via proxy
response = await client.proxy("my-github", "GET", "/user/repos",
    params={"sort": "updated", "per_page": 10})
💳

Stripe

api_key auth live validation
connector_name
stripe
Base URL
api.stripe.com
Rate Limit
100 req / second
Token Expiry
Never
Live validation: Clavis calls GET /v1/balance when credentials are first stored to verify the key.

Required credentials

FieldDescription
api_keyrequiredStripe secret key (sk_live_ or sk_test_) or restricted key (rk_live_ / rk_test_). Get one at dashboard.stripe.com/apikeys.
python — register & use
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "api_key", "data": {"api_key": "sk_test_..."}})

# Create a payment intent via proxy
response = await client.proxy("my-stripe", "POST", "/v1/payment_intents",
    body={"amount": 2000, "currency": "usd"})
📈

Kalshi

login → session token 24h expiry, auto-refresh
connector_name
kalshi
Base URL
api.elections.kalshi.com
Rate Limit
10 req / second
Token Expiry
24 hours (auto-refreshed)
Session token auth: Unlike API key connectors, Kalshi authenticates with email + password and receives a 24-hour session token. Clavis stores the credentials and re-authenticates automatically when the token expires.

Required credentials

FieldDescription
emailrequiredYour Kalshi account email address.
passwordrequiredYour Kalshi account password.
python — register & use
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "api_key",
          "data": {"email": "you@example.com", "password": "..."}})

# Token fetched and cached; re-login happens automatically at expiry
token = await client.get_token("my-kalshi")
response = await client.proxy("my-kalshi", "GET", "/trade-api/v2/markets")

Coinbase Advanced Trade

HMAC-SHA256 signing live validation
connector_name
coinbase
Base URL
api.coinbase.com
Rate Limit
10 req / second
Token Expiry
Never
HMAC signing: Coinbase requires per-request HMAC-SHA256 signatures using a timestamp + method + path. Clavis handles this automatically when proxying — use the proxy endpoint rather than extracting the token directly.

Required credentials

FieldDescription
api_keyrequiredCoinbase API key. Get one at coinbase.com/settings/api.
api_secretrequiredCoinbase API secret corresponding to the key above.
python — register & use
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "api_key",
          "data": {"api_key": "...", "api_secret": "..."}})

# Use proxy — Clavis signs each request with HMAC-SHA256
response = await client.proxy("my-coinbase", "GET",
    "/api/v3/brokerage/accounts")
📊

Alpaca Markets

api_key auth paper & live
connector_name
alpaca
Base URL
paper-api.alpaca.markets (paper) · api.alpaca.markets (live)
Rate Limit
200 req / min
Token Expiry
Never
Paper trading by default. Set environment to "live" in the credential data to target the live trading endpoint. Both domains are allowed in SSRF validation.

Required credentials

FieldDescription
api_keyrequiredAlpaca API key ID. Get one at app.alpaca.markets.
api_secretrequiredAlpaca API secret corresponding to the key above.
environmentOptional. "paper" (default) or "live".
python — register & use
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "api_key",
          "data": {"api_key": "PKXXXXXXXX", "api_secret": "...",
                    "environment": "paper"}})

# Injects APCA-API-KEY-ID + APCA-API-SECRET-KEY headers automatically
response = await client.proxy("my-alpaca", "GET", "/v2/positions")
🔑

Kalshi RSA

RSA-PSS per-request signing
connector_name
kalshi_rsa
Base URL
api.elections.kalshi.com
Rate Limit
10 req / second
Token Expiry
Never (permanent key)
Per-request signing required. The Kalshi RSA API requires each request to be signed with an RSA-PSS signature. Use POST /v1/credentials/{name}/headers to get the signed headers for each request, then attach them to your outbound call.

Required credentials

FieldDescription
key_idrequiredYour Kalshi API key ID (a UUID). Find it in the Kalshi dashboard under API settings.
private_key_pemrequiredRSA private key in PEM format (PKCS#8). Generated alongside your Kalshi API key.
python — register & get signed headers
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "jwt",
          "data": {"key_id": "uuid-...", "private_key_pem": "-----BEGIN PRIVATE KEY-----\n..."}})

# Get signed headers for each outbound request
r = await http.post("https://clavisagent.com/v1/credentials/my-kalshi-rsa/headers",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"method": "GET", "path": "/trade-api/v2/balance", "body": ""})
# r.json() = {"headers": {"KALSHI-ACCESS-KEY": ..., "KALSHI-ACCESS-TIMESTAMP": ..., "KALSHI-ACCESS-SIGNATURE": ...}}
🪙

Coinbase Advanced Trade (JWT)

ES256 JWT per-request signing
connector_name
coinbase_jwt
Base URL
api.coinbase.com
Rate Limit
10 req / second
Token Expiry
120 s per JWT (auto-generated)
CDP API JWT auth. Each request requires a fresh ES256-signed JWT (2-minute TTL). Use POST /v1/credentials/{name}/headers to generate the signed Authorization: Bearer <jwt> header, or use POST /v1/credentials/{name}/ws_headers for Coinbase WebSocket connections.

Required credentials

FieldDescription
api_keyrequiredFull Coinbase CDP API key path: organizations/<org-id>/apiKeys/<key-id>.
private_key_pemrequiredEC private key in PEM format (ES256). Generated in the Coinbase Developer Platform.
python — register & get signed JWT header
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "jwt",
          "data": {"api_key": "organizations/abc/apiKeys/xyz",
                    "private_key_pem": "-----BEGIN EC PRIVATE KEY-----\n..."}})

# Get a fresh signed JWT for one REST request
r = await http.post("https://clavisagent.com/v1/credentials/my-coinbase-jwt/headers",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"method": "GET", "path": "/api/v3/brokerage/accounts", "body": ""})
# r.json() = {"headers": {"Authorization": "Bearer eyJ..."}}
🔐

Generic API Key

api_key auth Bearer token
connector_name
api_key
Base URL
api.resend.com (default)
Rate Limit
none by default
Token Expiry
Never
Generic Bearer-token connector. The built-in api_key registration targets api.resend.com and injects Authorization: Bearer <key>. Use it for Resend or any compatible REST API at that base URL.

Required credentials

FieldDescription
api_keyrequiredStatic API key injected as Authorization: Bearer <key>.
python — register & use (Resend example)
await http.post(f"https://clavisagent.com/v1/services/{svc_id}/credentials",
    headers={"Authorization": f"Bearer {jwt}"},
    json={"token_type": "api_key", "data": {"api_key": "re_..."}})

# Proxy injects Authorization: Bearer re_... automatically
response = await client.proxy("my-resend", "POST", "/emails",
    body={"from": "noreply@example.com", "to": "user@example.com",
          "subject": "Hello", "text": "Hi!"})