Error Handling

Comprehensive guide to handling errors and troubleshooting issues with the Magnetite API.

Error Response Format

All error responses follow this consistent structure:

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description",
    "details": {
      // Additional context when available
    }
  }
}

success

Always false for error responses

error.code

Machine-readable error identifier for programmatic handling

error.message

Human-readable description suitable for display

HTTP Status Codes

4xx
Client Errors
Errors caused by the client request
400 Bad RequestInvalid request format
401 UnauthorizedAuthentication failed
404 Not FoundResource not found
409 ConflictDuplicate lead
429 Too Many RequestsRate limit exceeded
5xx
Server Errors
Errors on Magnetite's side
500 Internal Server ErrorUnexpected server error
502 Bad GatewayUpstream service error
503 Service UnavailableTemporary unavailability
504 Gateway TimeoutRequest timeout

Common Error Codes

INVALID_API_KEY
401
The provided API key is invalid or has been revoked.

Possible Causes

  • API key is incorrect or malformed
  • API key has been revoked or expired
  • API key is not properly formatted in the Authorization header

Resolution

Verify your API key in the dashboard and ensure it's properly formatted in the Authorization header as 'Bearer sk_...'

Example Response

{
  "success": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid API key provided"
  }
}
MISSING_AUTHORIZATION
401
No Authorization header was provided with the request.

Possible Causes

  • Authorization header is missing from the request
  • Header name is misspelled
  • API key is missing from the header value

Resolution

Include the Authorization header with your API key: 'Authorization: Bearer sk_your_api_key'

Example Response

{
  "success": false,
  "error": {
    "code": "MISSING_AUTHORIZATION",
    "message": "Authorization header is required"
  }
}
INVALID_REQUEST
400
The request body contains invalid or missing required fields.

Possible Causes

  • Required fields are missing (companyName, contactName, or email)
  • Field values are in the wrong format
  • JSON structure is malformed
  • Email address is invalid

Resolution

Check the API documentation for required fields and ensure all data is properly formatted

Example Response

{
  "success": false,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Missing required field: email",
    "details": {
      "field": "email",
      "expected": "string",
      "received": "undefined"
    }
  }
}
RATE_LIMIT_EXCEEDED
429
Too many requests have been made in the current time window.

Possible Causes

  • Sending requests too frequently
  • Multiple API clients using the same key
  • Bulk operations exceeding rate limits

Resolution

Implement exponential backoff and respect the rate limit headers. Consider batching requests with appropriate delays.

Example Response

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Maximum 10 requests per 60 seconds allowed.",
    "retryAfter": 45
  }
}
CAMPAIGN_NOT_FOUND
404
The specified campaign ID does not exist or is not accessible.

Possible Causes

  • Campaign ID is incorrect or malformed
  • Campaign has been deleted or deactivated
  • API key doesn't have access to the campaign

Resolution

Verify the campaign ID in your dashboard and ensure your API key has the correct permissions

Example Response

{
  "success": false,
  "error": {
    "code": "CAMPAIGN_NOT_FOUND",
    "message": "Campaign not found: abc123def456ghi789jkl012mno345pq"
  }
}
PROSPECT_NOT_FOUND
404
The specified prospect/lead ID does not exist or is not accessible.

Possible Causes

  • Prospect ID is incorrect or malformed
  • Prospect was not created successfully
  • API key doesn't have access to the prospect

Resolution

Verify the prospect ID from the add lead response and ensure it was created successfully

Example Response

{
  "success": false,
  "error": {
    "code": "PROSPECT_NOT_FOUND",
    "message": "Prospect not found: prospect_xyz789"
  }
}
DUPLICATE_LEAD
409
A lead with this email already exists in the campaign.

Possible Causes

  • Email address already added to this campaign
  • Lead was previously submitted via API or dashboard
  • Duplicate submission in batch processing

Resolution

Check if the lead already exists before submitting, or handle the duplicate response gracefully

Example Response

{
  "success": false,
  "error": {
    "code": "DUPLICATE_LEAD",
    "message": "A lead with email john@acme.com already exists in this campaign",
    "existingProspectId": "prospect_abc123"
  }
}
CAMPAIGN_INACTIVE
400
The campaign is not currently accepting new leads.

Possible Causes

  • Campaign is paused or archived
  • Campaign setup is incomplete
  • Campaign has reached its lead limit

Resolution

Ensure the campaign is active in your dashboard before adding leads

Example Response

{
  "success": false,
  "error": {
    "code": "CAMPAIGN_INACTIVE",
    "message": "Campaign is not active. Current status: paused"
  }
}
INTERNAL_SERVER_ERROR
500
An unexpected error occurred on our servers.

Possible Causes

  • Temporary server issue
  • Database connectivity problem
  • Unexpected system error

Resolution

This is usually temporary. Wait a moment and try again. If the issue persists, contact support.

Example Response

{
  "success": false,
  "error": {
    "code": "INTERNAL_SERVER_ERROR",
    "message": "An unexpected error occurred. Please try again later."
  }
}

Error Handling Best Practices

1. Always Check HTTP Status Codes

Don't rely solely on the response body. HTTP status codes provide important context:

async function handleApiResponse(response) {
  if (!response.ok) {
    const errorData = await response.json();

    switch (response.status) {
      case 401:
        throw new AuthenticationError(errorData.error.message);
      case 404:
        throw new NotFoundError(errorData.error.message);
      case 409:
        throw new DuplicateLeadError(errorData.error.message);
      case 429:
        throw new RateLimitError(errorData.error.message, errorData.error.retryAfter);
      case 500:
        throw new ServerError(errorData.error.message);
      default:
        throw new ApiError(errorData.error.message, response.status);
    }
  }

  return response.json();
}
2. Implement Retry Logic

Some errors are transient and should be retried with exponential backoff:

async function makeRequestWithRetry(url, options, maxRetries = 3) {
  const retryableStatuses = [429, 500, 502, 503, 504];

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await fetch(url, options);

      if (response.ok) {
        return await response.json();
      }

      if (!retryableStatuses.includes(response.status)) {
        throw new Error('Non-retryable error: ' + response.status);
      }

      if (attempt === maxRetries - 1) {
        throw new Error('Max retries exceeded');
      }

      const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
      console.log(`Retrying in ${delay}ms (attempt ${attempt + 1})`);
      await new Promise(resolve => setTimeout(resolve, delay));

    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
    }
  }
}
3. Log Errors Appropriately

Different error types require different logging levels:

function logApiError(error, context) {
  const logData = {
    timestamp: new Date().toISOString(),
    context,
    error: {
      code: error.code,
      message: error.message,
      status: error.status
    }
  };

  switch (error.status) {
    case 401:
      // Authentication issues - warn level
      console.warn('API authentication error:', logData);
      break;
    case 429:
      // Rate limiting - info level (expected during high usage)
      console.info('API rate limit hit:', logData);
      break;
    case 500:
    case 502:
    case 503:
    case 504:
      // Server errors - error level
      console.error('API server error:', logData);
      break;
    default:
      // Client errors - debug level
      console.debug('API client error:', logData);
  }
}
4. Provide User-Friendly Messages

Convert technical error messages into user-friendly ones:

function getUserFriendlyMessage(error) {
  const friendlyMessages = {
    INVALID_API_KEY: 'There was an authentication issue. Please check your API configuration.',
    CAMPAIGN_NOT_FOUND: 'The specified campaign could not be found. Please verify the campaign ID.',
    DUPLICATE_LEAD: 'This contact has already been added to the campaign.',
    RATE_LIMIT_EXCEEDED: 'You\'re sending requests too quickly. Please wait a moment before trying again.',
    CAMPAIGN_INACTIVE: 'The campaign is not currently accepting new leads.',
    INTERNAL_SERVER_ERROR: 'Something went wrong on our end. Please try again in a few moments.'
  };

  return friendlyMessages[error.code] || 'An unexpected error occurred. Please try again or contact support if the issue persists.';
}
5. Handle Duplicate Leads Gracefully

When importing leads in bulk, handle duplicates without failing the entire batch:

async function importLeadsWithDuplicateHandling(leads, campaignId, apiKey) {
  const results = {
    added: [],
    duplicates: [],
    errors: []
  };

  for (const lead of leads) {
    try {
      const response = await fetch(
        `https://magnetite.ai/api/campaigns/${campaignId}/leads`,
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${apiKey}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(lead)
        }
      );

      const data = await response.json();

      if (response.ok) {
        results.added.push({ lead, prospectId: data.prospectId });
      } else if (data.error?.code === 'DUPLICATE_LEAD') {
        // Handle duplicate gracefully
        results.duplicates.push({
          lead,
          existingProspectId: data.error.existingProspectId
        });
      } else {
        results.errors.push({ lead, error: data.error });
      }
    } catch (error) {
      results.errors.push({ lead, error: error.message });
    }

    // Respect rate limits
    await new Promise(resolve => setTimeout(resolve, 1000));
  }

  return results;
}

Need More Help?

If you're experiencing errors not covered here or need additional assistance: