Skip to main content

API Error Reference

This document provides a complete reference for all error codes returned by the Spritz API.

Error Response Format

All API errors follow a consistent format:

{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error description"
}
}

Some errors include additional fields:

{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests",
"retryAfter": 60
}
}

HTTP Status Codes

4xx Client Errors

StatusCodeDescriptionAction
400VALIDATION_ERRORInvalid request dataCheck request body and parameters
400INVALID_PARAMETERMissing or invalid parameterReview required parameters
401UNAUTHORIZEDNo valid sessionRe-authenticate with SIWE/SIWS
401SESSION_EXPIREDSession token expiredGenerate new session
401INVALID_SIGNATURESignature verification failedRe-sign the message
402PAYMENT_REQUIREDx402 payment neededSend payment via X-Payment header
403FORBIDDENInsufficient permissionsCheck user permissions
403ACCESS_DENIEDResource not accessibleVerify ownership or visibility
404NOT_FOUNDResource doesn't existVerify resource ID
404AGENT_NOT_FOUNDAgent doesn't existCheck agent ID
404STREAM_NOT_FOUNDStream doesn't existCheck stream ID
404USER_NOT_FOUNDUser doesn't existCheck wallet address
409CONFLICTResource already existsHandle duplicate
409USERNAME_TAKENUsername already claimedChoose different username
429RATE_LIMIT_EXCEEDEDToo many requestsWait and retry

5xx Server Errors

StatusCodeDescriptionAction
500INTERNAL_ERRORServer errorRetry with exponential backoff
500DATABASE_ERRORDatabase operation failedRetry later
502UPSTREAM_ERRORExternal service errorRetry later
503SERVICE_UNAVAILABLEService temporarily downCheck status page
504TIMEOUTRequest timed outRetry with smaller payload

Authentication Errors

SIWE/SIWS Errors

CodeDescriptionResolution
INVALID_NONCENonce expired or already usedRequest new nonce via GET /api/auth/verify?address=...
INVALID_SIGNATURESignature doesn't match addressRe-sign the SIWE message
NONCE_EXPIREDNonce older than 5 minutesRequest new nonce
INVALID_DOMAINDomain mismatchEnsure domain is app.spritz.chat
CHAIN_NOT_SUPPORTEDUnsupported chain IDUse supported chain (see Authentication)

Passkey Errors

CodeDescriptionResolution
PASSKEY_NOT_FOUNDCredential not registeredRegister new passkey
PASSKEY_VERIFICATION_FAILEDWebAuthn verification failedCheck credential ID
CHALLENGE_EXPIREDChallenge older than 5 minutesGet new challenge
INVALID_AUTHENTICATORAuthenticator not recognizedUse registered device
PASSKEY_ALREADY_REGISTEREDPasskey already existsUse existing passkey

Session Errors

CodeDescriptionResolution
SESSION_EXPIREDJWT token expiredRe-authenticate
SESSION_INVALIDToken tampered or invalidRe-authenticate
SESSION_NOT_FOUNDSession doesn't existCreate new session

Agent Errors

CodeDescriptionResolution
AGENT_NOT_FOUNDAgent ID doesn't existVerify agent ID
AGENT_NOT_PUBLICPrivate agent accessed without authUse authenticated endpoint or contact owner
AGENT_LIMIT_REACHEDMaximum agents per user reachedDelete unused agents
KNOWLEDGE_INDEXINGKnowledge base still processingWait and retry
KNOWLEDGE_FAILEDKnowledge indexing failedCheck URL accessibility
X402_NOT_ENABLEDAgent doesn't accept paymentsContact agent owner
X402_PAYMENT_FAILEDPayment verification failedCheck payment parameters
INVALID_MODELUnsupported AI model specifiedUse supported model
CHAT_HISTORY_NOT_FOUNDSession doesn't existStart new session

Agent Visibility Errors

CodeDescriptionResolution
AGENT_PRIVATEAgent is privateMust be owner to access
AGENT_FRIENDS_ONLYAgent limited to friendsAdd owner as friend

Streaming Errors

CodeDescriptionResolution
STREAM_NOT_FOUNDStream doesn't existVerify stream ID
STREAM_NOT_LIVEStream is not currently liveWait for broadcaster
STREAM_ENDEDStream has endedCheck for recordings
LIVEPEER_ERRORLivepeer API errorRetry later
INVALID_STREAM_KEYStream key invalidRegenerate stream
RECORDING_NOT_READYRecording still processingWait for processing

Wallet Errors

CodeDescriptionResolution
WALLET_NOT_FOUNDWallet address not registeredCreate account first
SAFE_NOT_DEPLOYEDSmart wallet not deployedDeploy Safe first
INSUFFICIENT_BALANCENot enough tokensAdd funds
TRANSACTION_FAILEDTransaction revertedCheck parameters
CHAIN_NOT_SUPPORTEDChain not supportedUse supported chain

Rate Limiting Errors

Error Response

{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Try again in 60 seconds."
}
}

Rate Limit Headers

All responses include rate limit information:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705600000
Retry-After: 60

Rate Limit Tiers

TierLimitEndpoints
auth10/min/api/auth/*, /api/passkey/*
strict5/min/api/invites, /api/points/*, /api/streams
contact3/min/api/contact
ai30/min/api/agents/*/chat
messaging60/min/api/channels/*/messages
general100/minAll other endpoints

x402 Payment Errors

402 Response

{
"error": "Payment Required",
"message": "This API requires a payment of $0.01 USDC",
"paymentRequirements": {
"x402Version": 1,
"accepts": [
{
"network": "base-sepolia",
"token": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"amount": "10000",
"recipient": "0x..."
}
]
}
}

Payment Error Codes

CodeDescriptionResolution
PAYMENT_REQUIREDNo payment providedSend X-Payment header
INVALID_PAYMENTPayment verification failedCheck payment format
INSUFFICIENT_PAYMENTPayment amount too lowIncrease payment amount
WRONG_NETWORKPayment on wrong networkUse Base or Base Sepolia
PAYMENT_EXPIREDPayment proof expiredGenerate new payment

Error Handling Best Practices

Exponential Backoff

For 5xx errors, implement exponential backoff:

async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);

if (response.status >= 500) {
throw new Error(`Server error: ${response.status}`);
}

return response;
} catch (error) {
if (i === maxRetries - 1) throw error;

// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, i) * 1000;
await new Promise(r => setTimeout(r, delay));
}
}
}

Rate Limit Handling

async function fetchWithRateLimit(url: string, options: RequestInit) {
const response = await fetch(url, options);

if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 60000;

await new Promise(r => setTimeout(r, waitTime));
return fetch(url, options);
}

return response;
}

Error Type Checking

interface ApiError {
success: false;
error: {
code: string;
message: string;
retryAfter?: number;
};
}

function isApiError(response: unknown): response is ApiError {
return (
typeof response === 'object' &&
response !== null &&
'success' in response &&
response.success === false &&
'error' in response
);
}

// Usage
const data = await response.json();

if (isApiError(data)) {
switch (data.error.code) {
case 'RATE_LIMIT_EXCEEDED':
// Handle rate limiting
break;
case 'UNAUTHORIZED':
// Redirect to login
break;
default:
// Show error message
console.error(data.error.message);
}
}