Skip to main content

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)"
}
error
string
required
Human-readable error message describing what went wrong
code
string
Optional machine-readable error code for specific error types
details
string
Optional additional context or validation details

HTTP Status Codes

The API uses standard HTTP status codes:

Success Codes

200 OK
success
Request succeeded. Used for GET, PATCH, and DELETE operations.
201 Created
success
Resource successfully created. Used for POST operations.

Client Error Codes

400 Bad Request
error
Invalid request data, missing required fields, or validation errors.
401 Unauthorized
error
Missing or invalid authentication credentials.
403 Forbidden
error
Authenticated but not authorized to perform the action (e.g., plan limits).
404 Not Found
error
Requested resource does not exist or you don’t have access to it.
409 Conflict
error
Request conflicts with existing resource (e.g., duplicate domain).
429 Too Many Requests
error
Rate limit exceeded. See Rate Limits.

Server Error Codes

500 Internal Server Error
error
Unexpected error on the server. If this persists, contact support.
503 Service Unavailable
error
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:
CodeDescription
DOMAIN_ALREADY_REGISTEREDDomain is already claimed by another user
VALIDATION_ERRORRequest failed validation
LIMIT_EXCEEDEDAccount 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

Build docs developers (and LLMs) love