Error Handling

Stack0 uses conventional HTTP response codes to indicate the success or failure of API requests.

HTTP Status Codes

CodeStatusDescription
200OKRequest succeeded
201CreatedResource created successfully
400Bad RequestInvalid request parameters
401UnauthorizedInvalid or missing API key
403ForbiddenInsufficient permissions
404Not FoundResource doesn't exist
422Unprocessable EntityValidation error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorSomething went wrong on our end
503Service UnavailableTemporary service issue

Error Response Format

All error responses follow a consistent JSON format:

Error Response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"issues": [
{
"field": "to",
"message": "Invalid email address format"
},
{
"field": "subject",
"message": "Subject is required"
}
]
}
}

Error Codes

CodeHTTPDescription
VALIDATION_ERROR400/422Request validation failed
UNAUTHORIZED401Invalid or missing API key
FORBIDDEN403Not allowed to access resource
NOT_FOUND404Resource not found
RATE_LIMITED429Too many requests
DOMAIN_NOT_VERIFIED400Email domain not verified
QUOTA_EXCEEDED400Plan limit reached
INTERNAL_ERROR500Server error

Handling Errors in Code

Example of proper error handling with the SDK:

error-handling.ts
import { Stack0, Stack0Error } from '@stack0/sdk'
const stack0 = new Stack0({
apiKey: process.env.STACK0_API_KEY!
})
try {
const result = await stack0.mail.send({
from: 'you@yourdomain.com',
to: 'recipient@example.com',
subject: 'Hello!',
html: '<p>Hello world</p>',
})
console.log('Email sent:', result.id)
} catch (error) {
if (error instanceof Stack0Error) {
console.error('Stack0 Error:', error.code)
console.error('Message:', error.message)
// Handle specific errors
switch (error.code) {
case 'VALIDATION_ERROR':
console.error('Invalid input:', error.issues)
break
case 'RATE_LIMITED':
console.error('Too many requests, retry after:', error.retryAfter)
break
case 'DOMAIN_NOT_VERIFIED':
console.error('Please verify your domain in the dashboard')
break
default:
console.error('Unexpected error')
}
} else {
console.error('Network error:', error)
}
}

Retry Strategy

For 429 and 5xx errors, we recommend implementing exponential backoff:

retry-strategy.ts
async function sendWithRetry(
fn: () => Promise<any>,
maxRetries = 3
) {
let lastError: Error | null = null
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn()
} catch (error) {
lastError = error as Error
// Only retry on rate limit or server errors
if (error instanceof Stack0Error) {
if (error.code === 'RATE_LIMITED') {
const delay = error.retryAfter || Math.pow(2, attempt) * 1000
await new Promise(r => setTimeout(r, delay))
continue
}
if (error.status >= 500) {
const delay = Math.pow(2, attempt) * 1000
await new Promise(r => setTimeout(r, delay))
continue
}
}
// Don't retry client errors
throw error
}
}
throw lastError
}
// Usage
const result = await sendWithRetry(() =>
stack0.mail.send({ ... })
)

Tip: Check the Response Headers

For rate limit errors (429), check the Retry-After header to know when you can retry the request.