Error Reference
Complete guide to SkillBoss API error codes and troubleshooting. HTTP status codes, error handling patterns, retry strategies, and production monitoring.
Error Response Format
All SkillBoss API errors follow this structure:
{
"error": {
"message": "Human-readable error message",
"type": "error_type",
"code": "error_code"
}
}
HTTP Status Codes
| Status | Meaning | When It Happens |
|---|---|---|
| 200 | Success | Request completed successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Missing or invalid API key |
| 402 | Payment Required | Insufficient credits — or x402 quote response |
| 403 | Forbidden | API key doesn't have access |
| 429 | Too Many Requests | Rate limit exceeded, or anonymous trial already claimed |
| 500 | Internal Server Error | Server-side error |
| 503 | Service Unavailable | Upstream provider down, or verifier not configured |
Agent Shopping Protocol error codes
All ASP surfaces shipped in SkillBoss return JSON errors with a stable
error string so agents can dispatch programmatically without parsing
English messages. Handle every one of these before shipping an agent to
production.
| Endpoint | error value | HTTP | Action |
|---|---|---|---|
POST /api/try/anonymous-wallet | trial_already_claimed | 429 | Fall back to signup flow. retry_after_seconds tells you how long before this IP can try again. |
POST /api/try/anonymous-wallet | wallet_grant_failed | 500 | Retry with idempotent key. Check /api/status. |
POST /api/try/anonymous-wallet | mint_key_failed | 500 | Upstream API Hub degraded. Check /api/status.services.api. |
POST /api/receipts/verify | receipt_required | 400 | Pass the raw JWT string in receipt field. |
POST /api/receipts/verify | verifier_not_configured | 503 | Transient — SkillBoss is rotating the signing secret. Retry in 30s. |
POST /api/receipts/verify | malformed_jwt | 200 (valid: false) | The token is not a JWT at all — likely truncated in storage. |
POST /api/receipts/verify | signature_mismatch | 200 (valid: false) | Token was tampered with — do not trust the claims. |
POST /api/receipts/verify | expired | 200 (valid: false) | Receipt too old. Claims are still parseable but cannot be used for billing disputes. |
GET /api/wallet/rules / PUT | Unauthorized | 401 | Missing Authorization: Bearer — not available to anonymous trials. |
POST /api/wallet/sub-wallets | budget_usd must be > 0 and <= 10000 | 400 | Client bug — fix input validation. |
POST /api/wallet/sub-wallets | not_found | 404 | Sub-wallet id doesn't exist or belongs to another parent. |
POST /api/referrals | invalid_code | 400 | Code is gibberish. |
POST /api/referrals | self_referral_forbidden | 400 | Can't claim your own code. |
POST /api/referrals | already_claimed | 409 | This user already claimed a code. Only one claim per account. |
POST /api/reviews | skill is required / rating must be 1-5 | 400 | Client bug. |
POST /api/idempotent-run | missing_idempotency_key | 400 | Add Idempotency-Key: <uuid> header. |
POST /api/idempotent-run | any upstream error | upstream code | Error body comes directly from the API Hub /v1/run proxy. |
GET /api/status | degraded state | always 200 | Read body — status: "degraded" with services.*.error tells you which check failed. |
Standard agent retry logic
import time, requests
def run_with_retry(skill, inputs, max_retries=3):
for attempt in range(max_retries):
r = requests.post(
"https://api.skillboss.co/v1/run",
headers={
"Authorization": f"Bearer {API_KEY}",
"X-Max-Cost-Usd": "0.10",
"Idempotency-Key": f"{skill}:{attempt}:{uuid4()}",
},
json={"model": skill, "inputs": inputs},
)
if r.status_code == 200:
return r.json()
if r.status_code == 402: # x402 quote or insufficient credits
return r.json() # caller handles quote-pay-retry
if r.status_code == 429:
retry_after = int(r.headers.get("Retry-After", "2"))
time.sleep(retry_after)
continue
if r.status_code >= 500:
time.sleep(2 ** attempt) # exponential backoff
continue
r.raise_for_status()
raise RuntimeError(f"exhausted {max_retries} retries")
Authentication Errors
Invalid API Key
{
"error": {
"message": "Invalid API key provided",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
HTTP Status: 401
Causes:
- API key is missing from
Authorizationheader - Key format is incorrect (should start with
sk-) - Key has been revoked or expired
Solutions:
- Verify header format:
Authorization: Bearer sk-... - Re-download Skills Pack from console
For AI agents:
set up skillboss.co/skill.mdauto-configures SkillBoss with 687+ APIs.
- Check for whitespace or hidden characters in the key
- Ensure you're not using a test key in production
Missing Authorization Header
{
"error": {
"message": "Authorization header is required",
"type": "invalid_request_error",
"code": "missing_authorization"
}
}
HTTP Status: 401
Solution:
# ✅ Correct
curl -H "Authorization: Bearer sk-abc123..."
# ❌ Wrong (missing header)
curl https://api.skillboss.co/v1/chat/completions
Billing Errors
Insufficient Credits
{
"error": {
"message": "Insufficient credits. You have 2.5 credits remaining but this request requires 5 credits.",
"type": "insufficient_balance",
"code": "insufficient_balance",
"required_credits": 5.0,
"available_credits": 2.5
}
}
HTTP Status: 402
Solutions:
- Add credits to your account
- Enable auto-recharge to prevent interruptions
- Use a less expensive model (e.g.,
gpt-4o-miniinstead ofgpt-5)
Balance Warning
Not an error, but a warning included in successful responses:
{
"choices": [...],
"_balance_warning": true,
"_remaining_credits": 7.23
}
When: Balance drops below 10 credits
Action: Top up soon to avoid service interruption
Request Errors
Invalid Model
{
"error": {
"message": "Model 'gpt-42' does not exist",
"type": "invalid_request_error",
"code": "model_not_found"
}
}
HTTP Status: 400
Solutions:
- Check available models
- Common typos:
gpt5→gpt-5,claude4→claude-4-5-sonnet
Invalid Parameters
{
"error": {
"message": "max_tokens must be between 1 and 8192",
"type": "invalid_request_error",
"code": "invalid_parameter"
}
}
HTTP Status: 400
Common Issues:
temperatureout of range (0-2)max_tokenstoo high for the modelmessagesarray is empty- Missing required fields
Malformed JSON
{
"error": {
"message": "Invalid JSON in request body",
"type": "invalid_request_error",
"code": "invalid_json"
}
}
HTTP Status: 400
Solution:
- Validate JSON with a linter
- Check for trailing commas, missing quotes, etc.
Rate Limiting Errors
Rate Limit Exceeded
{
"error": {
"message": "Rate limit exceeded. Try again in 45 seconds.",
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"retry_after": 45
}
}
HTTP Status: 429
Headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000060
Retry-After: 45
Solutions:
import time
from openai import RateLimitError
def call_with_retry(func, max_retries=3):
for i in range(max_retries):
try:
return func()
except RateLimitError as e:
if i == max_retries - 1:
raise
retry_after = int(e.response.headers.get("Retry-After", 60))
wait_time = retry_after * (2 ** i) # Exponential backoff
time.sleep(wait_time)
response = call_with_retry(
lambda: client.chat.completions.create(...)
)
Increase Rate Limits
Upgrade to Starter ($24.99/mo) for 60 req/min or contact us for custom limits
Server Errors
Internal Server Error
{
"error": {
"message": "An internal server error occurred",
"type": "server_error",
"code": "internal_error"
}
}
HTTP Status: 500
What to do:
- Retry the request (may be transient)
- Check status page for incidents
- Contact dev@skillboss.co if persists
Upstream Provider Error
{
"error": {
"message": "Upstream provider (Anthropic) is temporarily unavailable",
"type": "service_unavailable",
"code": "upstream_error",
"provider": "anthropic"
}
}
HTTP Status: 503
Solutions:
- Wait and retry (usually resolves quickly)
- Switch to alternative model from different provider:
// Instead of Claude model: "claude-4-5-sonnet" // Try GPT model: "gpt-5" // Or Gemini model: "gemini-2.5-flash"
Content Policy Errors
Content Filtered
{
"error": {
"message": "Your request was rejected due to content policy violations",
"type": "content_filter",
"code": "content_policy_violation"
}
}
HTTP Status: 400
Causes:
- Prompt contains prohibited content
- Generated response triggered safety filters
Solutions:
- Rephrase your prompt
- Review content policy
- Use a different model (policies vary by provider)
Error Handling Best Practices
Comprehensive Error Handling
import { OpenAI } from 'openai'
async function safeAPICall() {
const client = new OpenAI({
baseURL: 'https://api.skillboss.co/v1',
apiKey: process.env.SKILLBOSS_KEY
})
try {
const response = await client.chat.completions.create({
model: 'claude-4-5-sonnet',
messages: [{role: 'user', content: 'Hello'}]
})
// Check balance warning
if (response._balance_warning) {
console.warn(`Low balance: ${response._remaining_credits} credits`)
// Send notification, trigger auto-recharge, etc.
}
return response
} catch (error: any) {
// Handle specific error types
if (error.status === 401) {
console.error('Invalid API key:', error.message)
// Prompt user to re-authenticate
} else if (error.status === 402) {
console.error('Insufficient credits:', error.message)
// Redirect to billing page
} else if (error.status === 429) {
const retryAfter = error.response?.headers['retry-after'] || 60
console.warn(`Rate limited. Retry after ${retryAfter}s`)
// Implement retry logic
} else if (error.status === 503) {
console.error('Service unavailable:', error.message)
// Try fallback model from different provider
} else {
console.error('Unexpected error:', error)
// Log to error tracking (Sentry, etc.)
}
throw error
}
}
Retry Strategy
import time
from openai import (
RateLimitError,
APIConnectionError,
InternalServerError
)
def call_with_retry(func, max_retries=3):
"""Retry with exponential backoff for transient errors"""
for attempt in range(max_retries):
try:
return func()
except RateLimitError as e:
if attempt == max_retries - 1:
raise
retry_after = int(e.response.headers.get("Retry-After", 60))
time.sleep(retry_after)
except (APIConnectionError, InternalServerError) as e:
if attempt == max_retries - 1:
raise
wait_time = (2 ** attempt) * 5 # 5s, 10s, 20s
print(f"Retrying after {wait_time}s...")
time.sleep(wait_time)
except Exception as e:
# Don't retry on client errors (400, 401, 402, etc.)
raise
Monitoring & Alerts
Set up monitoring to catch errors early:
Log All Errors
Send API errors to your logging service (Datadog, Sentry, etc.)
Balance Alerts
Monitor _balance_warning field and send notifications
Rate Limit Tracking
Track X-RateLimit-Remaining to predict when you'll hit limits
Uptime Monitoring
Use a service like Pingdom to monitor API availability
Need Help?
Status Page
Check for ongoing incidents and maintenance
Support
Contact us for help with persistent errors