Overview
The Inbound API uses conventional HTTP status codes to indicate the success or failure of requests. Error responses include detailed messages to help you debug issues.
Error Response Structure
All error responses follow a consistent JSON structure:
{
"error": "Human-readable error message",
"code": "OPTIONAL_ERROR_CODE",
"details": "Additional context (optional)"
}
Human-readable error message describing what went wrong
Optional machine-readable error code for specific error types
Optional additional context or validation details
HTTP Status Codes
The API uses standard HTTP status codes:
Success Codes
Request succeeded. Used for GET, PATCH, and DELETE operations.
Resource successfully created. Used for POST operations.
Client Error Codes
Invalid request data, missing required fields, or validation errors.
Missing or invalid authentication credentials.
Authenticated but not authorized to perform the action (e.g., plan limits).
Requested resource does not exist or you donβt have access to it.
Request conflicts with existing resource (e.g., duplicate domain).
Server Error Codes
500 Internal Server Error
Unexpected error on the server. If this persists, contact support.
Service temporarily unavailable (e.g., rate limiting service down).
Error Examples
400 Bad Request
Missing Required Field:
{
"error": "Domain is required"
}
Invalid Format:
{
"error": "Invalid domain format"
}
Validation Error with Details:
{
"error": "Invalid email format",
"details": "The 'from' field must be a valid email address"
}
Invalid Request Body:
{
"error": "Invalid request body: expected string for 'scheduled_at'"
}
401 Unauthorized
Missing Authentication:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="API", charset="UTF-8"
Content-Type: application/json; charset=utf-8
{
"error": "Unauthorized",
"message": "Authentication required. Provide a valid session cookie or Bearer token.",
"statusCode": 401
}
Invalid API Key:
{
"error": "Unauthorized",
"message": "Authentication required. Provide a valid session cookie or Bearer token.",
"statusCode": 401
}
403 Forbidden
Plan Limit Reached:
{
"error": "Domain limit reached. Please upgrade your plan to add more domains."
}
Sending Limit Exceeded:
{
"error": "Daily sending limit exceeded. Upgrade your plan for higher limits."
}
404 Not Found
Resource Not Found:
{
"error": "Domain not found"
}
Email Address Not Found:
{
"error": "Email address not found or you don't have access to it"
}
409 Conflict
Duplicate Domain (Own):
{
"error": "You have already added this domain to your account"
}
Duplicate Domain (Another User):
{
"error": "This domain is already registered on our platform. If you believe this is an error or you need to transfer ownership, please contact our support team.",
"code": "DOMAIN_ALREADY_REGISTERED"
}
Duplicate Email Address:
{
"error": "Email address already exists"
}
Cannot Delete (Has Dependencies):
{
"error": "Cannot delete domain. It has 5 associated email addresses. Delete them first."
}
429 Too Many Requests
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705315201000
Retry-After: 3
Content-Type: application/json; charset=utf-8
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Maximum 10 requests per second. Retry after 3 seconds.",
"statusCode": 429
}
500 Internal Server Error
General Server Error:
{
"error": "Failed to create domain"
}
AWS Service Error:
{
"error": "Failed to send email"
}
503 Service Unavailable
HTTP/1.1 503 Service Unavailable
Retry-After: 60
Content-Type: application/json; charset=utf-8
{
"error": "Service Unavailable",
"message": "Rate limiting service is temporarily unavailable. Please try again later.",
"statusCode": 503
}
Error Codes
Specific error scenarios include machine-readable codes:
| Code | Description |
|---|
DOMAIN_ALREADY_REGISTERED | Domain is already claimed by another user |
VALIDATION_ERROR | Request failed validation |
LIMIT_EXCEEDED | Account limit reached |
Handling Errors
JavaScript/TypeScript
try {
const response = await fetch('https://inbound.new/api/e2/domains', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ domain: 'example.com' })
})
if (!response.ok) {
const error = await response.json()
switch (response.status) {
case 400:
console.error('Validation error:', error.error)
break
case 401:
console.error('Authentication failed - check your API key')
break
case 403:
console.error('Forbidden:', error.error)
break
case 404:
console.error('Not found:', error.error)
break
case 409:
if (error.code === 'DOMAIN_ALREADY_REGISTERED') {
console.error('Domain already registered by another user')
}
break
case 429:
const retryAfter = response.headers.get('Retry-After')
console.error(`Rate limited. Retry after ${retryAfter} seconds`)
break
case 500:
console.error('Server error:', error.error)
break
}
throw new Error(error.error)
}
const data = await response.json()
console.log('Success:', data)
} catch (error) {
console.error('Request failed:', error)
}
Using the SDK
The SDK automatically throws typed errors:
import { Inbound } from 'inboundemail'
const inbound = new Inbound(process.env.INBOUND_API_KEY)
try {
const domain = await inbound.domains.create({
domain: 'example.com'
})
console.log('Domain created:', domain.id)
} catch (error) {
if (error.status === 409) {
console.error('Domain already exists')
} else if (error.status === 400) {
console.error('Invalid domain:', error.message)
} else {
console.error('Error:', error.message)
}
}
Error Logging
The API logs errors server-side for debugging:
// Server-side logging (visible in console)
console.log("π Checking if domain already exists")
console.log("β Domain already exists for current user:", domain)
console.error("β Error creating domain:", error)
Emojis in logs help identify log types:
- π Information/debugging
- β
Success
- β Errors
- β οΈ Warnings
- π Metrics/stats
Best Practices
1. Always Check Status Codes
if (!response.ok) {
const error = await response.json()
throw new Error(`API error: ${error.error}`)
}
2. Handle Specific Error Codes
if (error.code === 'DOMAIN_ALREADY_REGISTERED') {
// Show specific UI for domain conflicts
} else if (response.status === 403) {
// Prompt user to upgrade plan
}
3. Implement Retry Logic
async function withRetry(fn: () => Promise<any>, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
if (error.status === 429 || error.status >= 500) {
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 1000)
)
continue
}
throw error // Don't retry client errors
}
}
}
4. Log Errors for Debugging
catch (error) {
console.error('API Error:', {
status: error.status,
message: error.message,
code: error.code,
timestamp: new Date().toISOString()
})
// Report to error tracking service
Sentry.captureException(error)
}
RFC Compliance
Inboundβs error handling follows:
- RFC 7807: Problem Details for HTTP APIs (error format)
- RFC 7231: Standard HTTP status code semantics
- RFC 7235: WWW-Authenticate header for 401 responses
- RFC 6585: 429 status code and Retry-After header