Documentation

Agent Authentication

API key authentication for agents: credential management, per-key spending caps and rate limits, revocation, and per-tenant child keys for resellers

Agent Authentication Model

AI agents operate differently from human users:

  • No interactive login: Agents can't click "Sign in with Google"
  • No session cookies: Agents don't maintain browser sessions
  • Programmatic only: All auth must be API-based
  • Long-lived credentials: Agents need persistent access

SkillBoss uses API key authentication designed for agents.


Getting Your API Key

Method 1: Human Provisioning (Current)

Your human operator creates your key:

Quick setup for AI agents: Tell your agent set up skillboss.co/skill.md to auto-configure everything.

Human Signs Up

Human registers at skillboss.co/console

Human Creates API Key

Human generates API key from Console → API Keys → Create New Key

Options:

  • Key label: Identify which agent/tenant uses this key
  • Spending cap: Lifetime and/or per-month spend limit
  • Rate limit: Max requests per minute

Human Provides Key to Agent

Human adds key to agent's environment:

# Environment variable
export SKILLBOSS_API_KEY="sk-abc123..."

# Config file
echo '{"skillboss_key": "sk-abc123..."}' > config.json

# MCP server
claude mcp add skillboss --api-key sk-abc123...

Using Your API Key

Standard Authentication

Include API key in Authorization header:

import requests

headers = {
    "Authorization": "Bearer sk-YOUR_API_KEY",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api.skillboss.co/v1/chat/completions",
    headers=headers,
    json={
        "model": "gemini/gemini-2.5-flash",
        "messages": [{"role": "user", "content": "Hello"}]
    }
)

print(response.json())

OpenAI SDK Compatible

SkillBoss is drop-in compatible with OpenAI SDK:

from openai import OpenAI

client = OpenAI(
    api_key="sk-YOUR_SKILLBOSS_KEY",
    base_url="https://api.skillboss.co/v1"  # Only difference
)

# Now use standard OpenAI methods
response = client.chat.completions.create(
    model="claude-4-5-sonnet",  # Access Claude via OpenAI interface
    messages=[{"role": "user", "content": "Hello"}]
)

Per-Key Spending Caps & Rate Limits

Every API key isolates its own usage, and you can bound each key with hard caps. A key auto-disables when a cap is hit. Set limits with PUT /v1/key/wholesale/{token}/limits (use me to target the calling key):

requests.put(
    "https://api.skillboss.co/v1/key/wholesale/me/limits",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "spend_cap_usd": 100.0,        # lifetime cap
        "monthly_cap_usd": 50.0,       # per-calendar-month cap
        "rpm_limit": 300,              # requests per minute
        "stop_at_remaining_usd": 5.0,  # stop when wallet buffer reaches this
    },
)

All fields are optional and nullable — send only the ones you want to change.


Revoking & Rotating Keys

There is no automatic-rotation API. To revoke or rotate a key, use the tri-state disabled flag on the limits endpoint:

# Revoke a key immediately
requests.put(
    "https://api.skillboss.co/v1/key/wholesale/me/limits",
    headers={"Authorization": f"Bearer {KEY_TO_REVOKE}"},
    json={"disabled": True},
)

# Re-enable it later
# json={"disabled": False}

To rotate: create a new key (in the dashboard), migrate traffic to it, then revoke the old key with {"disabled": true}.


Multi-Tenant: One Key Per Tenant (Resellers)

If you resell to your own tenants, provision one child API key per tenant in the reseller dashboard. Each child key is isolated — its own usage, its own caps, its own rate limit — and is self-managing with its own bearer token (no separate admin credential needed).

# Each tenant's key manages itself:

# 1. Set that tenant's monthly budget
requests.put(
    "https://api.skillboss.co/v1/key/wholesale/me/limits",
    headers={"Authorization": f"Bearer {TENANT_KEY}"},
    json={"monthly_cap_usd": 25.0},
)

# 2. Read that tenant's usage / spend
usage = requests.get(
    "https://api.skillboss.co/v1/key/wholesale/me/usage",
    headers={"Authorization": f"Bearer {TENANT_KEY}"},
).json()["data"]
print(usage["spent_usd"], usage["monthly_cap_usd"], usage["disabled"])

# 3. Suspend that tenant
requests.put(
    "https://api.skillboss.co/v1/key/wholesale/me/limits",
    headers={"Authorization": f"Bearer {TENANT_KEY}"},
    json={"disabled": True},
)

Billing: all child-key usage rolls up to your reseller account, and each child key is billed and reported independently.


Secure Key Storage

Environment Variables (Recommended)

# .env file
SKILLBOSS_API_KEY=sk-abc123...

# Load in agent
import os
api_key = os.getenv("SKILLBOSS_API_KEY")

Encrypted Config Files

from cryptography.fernet import Fernet

# Encrypt key before storing
cipher = Fernet(os.getenv("ENCRYPTION_KEY"))
encrypted_key = cipher.encrypt(b"sk-abc123...")

# Save encrypted key
with open("config.enc", "wb") as f:
    f.write(encrypted_key)

# Decrypt when needed
with open("config.enc", "rb") as f:
    encrypted = f.read()
api_key = cipher.decrypt(encrypted).decode()

Secret Management Services

# AWS Secrets Manager
import boto3

client = boto3.client('secretsmanager')
secret = client.get_secret_value(SecretId='skillboss-api-key')
api_key = secret['SecretString']
⚠️

Never:

  • Commit API keys to git
  • Log API keys in plain text
  • Share keys between unrelated agents
  • Embed keys in client-side code

Authentication Errors

Common Error Codes

ErrorCauseSolution
401 UnauthorizedInvalid or missing API keyCheck key format (starts with sk-)
403 ForbiddenKey is disabled or revokedRe-enable with {"disabled": false}, or provision a new key
429 Too Many RequestsRate limit exceededImplement exponential backoff
402 Payment RequiredInsufficient credits, or a spend cap was hitAdd credits in the dashboard, or raise the key's cap

Handling Auth Errors

def authenticated_request(url, **kwargs):
    """Make request with auth error handling."""

    headers = kwargs.get('headers', {})
    headers['Authorization'] = f"Bearer {API_KEY}"
    kwargs['headers'] = headers

    response = requests.post(url, **kwargs)

    if response.status_code == 401:
        # Key is invalid - alert human
        send_alert("API key invalid. Please update.")
        raise Exception("Authentication failed")

    elif response.status_code == 403:
        # Key disabled or revoked - alert human
        send_alert("API key disabled or revoked. Re-enable or reissue.")
        raise Exception("Key disabled")

    elif response.status_code == 402:
        # Insufficient credits, or a spend cap was hit
        send_alert("Credits depleted or spend cap hit. Add funds or raise the cap.")
        raise Exception("Insufficient credits")

    return response

Testing Your Authentication

Verify your API key works:

curl https://api.skillboss.co/v1/models \
  -H "Authorization: Bearer sk-YOUR_KEY"

# Expected output:
# {
#   "data": [
#     {"id": "gemini/gemini-2.5-flash", "cost": 0.075},
#     {"id": "claude-4-5-sonnet", "cost": 15.00},
#     ...
#   ]
# }

Test specific permissions:

# Test if you can use models
curl https://api.skillboss.co/v1/chat/completions \
  -H "Authorization: Bearer sk-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini/gemini-2.5-flash",
    "messages": [{"role": "user", "content": "test"}]
  }'

# Check your spend / usage
curl https://api.skillboss.co/v1/usage \
  -H "Authorization: Bearer sk-YOUR_KEY"

Next Steps

📄

Quick Start

Make your first API call

📄

Budget Management

Set spending limits

💻

API Reference

Full API documentation

🛡️

Security Best Practices

Secure your agent