Error Handling
Stack0 uses conventional HTTP response codes to indicate the success or failure of API requests.
HTTP Status Codes
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Invalid or missing API key |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 422 | Unprocessable Entity | Validation error |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Something went wrong on our end |
| 503 | Service Unavailable | Temporary 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
| Code | HTTP | Description |
|---|---|---|
| VALIDATION_ERROR | 400/422 | Request validation failed |
| UNAUTHORIZED | 401 | Invalid or missing API key |
| FORBIDDEN | 403 | Not allowed to access resource |
| NOT_FOUND | 404 | Resource not found |
| RATE_LIMITED | 429 | Too many requests |
| DOMAIN_NOT_VERIFIED | 400 | Email domain not verified |
| QUOTA_EXCEEDED | 400 | Plan limit reached |
| INTERNAL_ERROR | 500 | Server 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 errorsswitch (error.code) {case 'VALIDATION_ERROR':console.error('Invalid input:', error.issues)breakcase 'RATE_LIMITED':console.error('Too many requests, retry after:', error.retryAfter)breakcase 'DOMAIN_NOT_VERIFIED':console.error('Please verify your domain in the dashboard')breakdefault: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 = nullfor (let attempt = 0; attempt < maxRetries; attempt++) {try {return await fn()} catch (error) {lastError = error as Error// Only retry on rate limit or server errorsif (error instanceof Stack0Error) {if (error.code === 'RATE_LIMITED') {const delay = error.retryAfter || Math.pow(2, attempt) * 1000await new Promise(r => setTimeout(r, delay))continue}if (error.status >= 500) {const delay = Math.pow(2, attempt) * 1000await new Promise(r => setTimeout(r, delay))continue}}// Don't retry client errorsthrow error}}throw lastError}// Usageconst 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.