Skip to main content
When the API encounters an error, it returns a consistent JSON error response with details about what went wrong.

Error Response Format

All error responses follow this structure:
{
  "error": {
    "code": "error_code",
    "message": "Human-readable description of the error",
    "type": "error_type"
  }
}
code
string
A machine-readable error code for programmatic handling
message
string
A human-readable description of what went wrong
type
string
The category of error (validation_error, authentication_error, etc.)

Error Types

Validation Errors (400)

Returned when the request contains invalid data.
{
  "error": {
    "code": "invalid_format",
    "message": "externalId and companyToken are required",
    "type": "validation_error"
  }
}
CodeDescription
invalid_formatRequest body is malformed or missing required fields
invalid_audio_formatUnsupported audio format (use MP3, WAV, OGG, AAC)
invalid_external_idExternal ID not found or not accessible
invalid_parameterA query or path parameter has an invalid value

Authentication Errors (401)

Returned when authentication fails.
{
  "error": {
    "code": "invalid_token",
    "message": "Invalid or expired API token",
    "type": "authentication_error"
  }
}
CodeDescription
invalid_tokenToken is invalid, expired, or revoked
missing_tokenNo X-API-Key header provided
insufficient_permissionsToken lacks required permissions

Resource Errors (404)

Returned when a requested resource doesn’t exist.
{
  "error": {
    "code": "audio_not_found",
    "message": "Audio file not found",
    "type": "resource_error"
  }
}
CodeDescription
audio_not_foundAudio file doesn’t exist or was deleted
endpoint_not_foundThe requested API endpoint doesn’t exist

Conflict Errors (409)

Returned when the request conflicts with current state.
{
  "error": {
    "code": "audio_in_use",
    "message": "Cannot delete audio with pending scheduled sends",
    "type": "conflict_error"
  }
}
CodeDescription
audio_in_useCannot delete audio with pending sends
duplicate_external_idExternal ID is already in use

Payload Errors (413)

Returned when the request body is too large.
{
  "error": {
    "code": "file_too_large",
    "message": "File size exceeds 10MB limit",
    "type": "validation_error"
  }
}

Rate Limit Errors (429)

Returned when you’ve exceeded your rate limit.
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Please retry after 42 seconds.",
    "type": "rate_limit_error",
    "retryAfter": 42
  }
}

Server Errors (5xx)

Returned when something goes wrong on our end.
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred",
    "type": "server_error"
  }
}

Handling Errors

async function makeRequest(url, options) {
  const response = await fetch(url, options);
  
  if (!response.ok) {
    const data = await response.json();
    const error = data.error;
    
    switch (response.status) {
      case 400:
        console.error('Validation error:', error.message);
        break;
      case 401:
        console.error('Authentication failed:', error.message);
        // Refresh token or prompt for re-auth
        break;
      case 404:
        console.error('Resource not found:', error.message);
        break;
      case 429:
        const retryAfter = error.retryAfter || 60;
        console.warn(`Rate limited. Retrying in ${retryAfter}s`);
        await sleep(retryAfter * 1000);
        return makeRequest(url, options); // Retry
      case 500:
      case 503:
        console.error('Server error:', error.message);
        // Implement exponential backoff
        break;
    }
    
    throw new Error(error.message);
  }
  
  return response.json();
}

Best Practices

For 429 and 5xx errors, implement retry with exponential backoff:
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      await new Promise(r => setTimeout(r, delay));
    }
  }
}
Always log the full error response for debugging:
  • Error code and message
  • Request ID (if provided)
  • Timestamp
  • Request details (endpoint, method, params)
  • Show user-friendly messages (not raw error codes)
  • Provide actionable next steps when possible
  • Don’t expose internal error details to end users