Error Handling

Understanding and handling API errors effectively for robust integrations.

Error Response Format

All API errors follow a consistent JSON format:

{
  "error": {
    "type": "VALIDATION_ERROR",
    "message": "The linkedin_url field is required",
    "code": "E1001",
    "details": {
      "field": "linkedin_url",
      "value": null,
      "constraint": "required"
    },
    "request_id": "req_2Xk9Lm3Np",
    "timestamp": "2025-01-20T14:30:00Z"
  }
}

Error Object Fields

Field Type Description
type string Error category for programmatic handling
message string Human-readable error description
code string Specific error code for detailed handling
details object Additional context about the error
request_id string Unique identifier for request tracing

HTTP Status Codes

Sales Webhooks uses standard HTTP status codes to indicate success or failure:

Success Codes

Code Meaning Usage
200 OK Request succeeded GET, PUT, DELETE requests
201 Created Resource created POST requests
204 No Content Success with no response body DELETE requests

Client Error Codes

Code Meaning Common Causes
400 Bad Request Invalid request Malformed JSON, missing fields
401 Unauthorized Authentication failed Invalid or missing API key
403 Forbidden Access denied Insufficient permissions
404 Not Found Resource not found Invalid ID or deleted resource
409 Conflict Resource conflict Duplicate subscription
422 Unprocessable Entity Validation failed Invalid field values
429 Too Many Requests Rate limit exceeded Too many API calls

Server Error Codes

Code Meaning Action
500 Internal Server Error Server error Retry with backoff
502 Bad Gateway Upstream error Retry with backoff
503 Service Unavailable Temporary outage Retry after delay
504 Gateway Timeout Request timeout Retry with smaller batch

Error Types Reference

Common error types and how to handle them:

Authentication Errors

AUTHENTICATION_FAILED

{
  "error": {
    "type": "AUTHENTICATION_FAILED",
    "message": "Invalid API key",
    "code": "E1101",
    "details": {
      "hint": "Check that your API key is correct and active"
    }
  }
}

How to handle:

  • Verify API key is correct
  • Check key hasn't been revoked
  • Ensure using correct environment (live vs test)

Validation Errors

VALIDATION_ERROR

{
  "error": {
    "type": "VALIDATION_ERROR",
    "message": "Invalid LinkedIn URL format",
    "code": "E1201",
    "details": {
      "field": "linkedin_url",
      "value": "not-a-url",
      "constraint": "url_format",
      "expected": "https://linkedin.com/in/username"
    }
  }
}

How to handle:

  • Check field requirements in API docs
  • Validate input before sending
  • Use details object to identify specific issue

Rate Limit Errors

RATE_LIMIT_EXCEEDED

{
  "error": {
    "type": "RATE_LIMIT_EXCEEDED",
    "message": "API rate limit exceeded",
    "code": "E1301",
    "details": {
      "limit": 50,
      "remaining": 0,
      "reset_at": "2025-01-20T15:00:00Z",
      "retry_after": 234
    }
  }
}

How to handle:

  • Check retry_after seconds
  • Implement exponential backoff
  • Use X-RateLimit-* headers proactively

Resource Errors

RESOURCE_NOT_FOUND

{
  "error": {
    "type": "RESOURCE_NOT_FOUND",
    "message": "Subscription not found",
    "code": "E1401",
    "details": {
      "resource": "subscription",
      "id": "sub_9Yx7Kj2Mn"
    }
  }
}

How to handle:

  • Verify resource ID is correct
  • Check if resource was deleted
  • Ensure resource belongs to your account

Handling Errors in Code

JavaScript/Node.js

async function createSubscription(linkedinUrl) {
  try {
    const subscription = await client.subscriptions.create({
      entity_type: 'contact',
      linkedin_url: linkedinUrl
    });
    return subscription;
  } catch (error) {
    // Check if it's an API error
    if (error.response) {
      const apiError = error.response.data.error;
      
      switch (apiError.type) {
        case 'VALIDATION_ERROR':
          console.error(`Invalid data: ${apiError.message}`);
          // Show validation error to user
          break;
          
        case 'RATE_LIMIT_EXCEEDED':
          const retryAfter = apiError.details.retry_after;
          console.log(`Rate limited. Retrying in ${retryAfter}s`);
          await sleep(retryAfter * 1000);
          return createSubscription(linkedinUrl); // Retry
          
        case 'DUPLICATE_RESOURCE':
          console.log('Subscription already exists');
          // Handle duplicate gracefully
          break;
          
        default:
          console.error(`API Error: ${apiError.message}`);
          throw error;
      }
    } else {
      // Network or other error
      console.error('Network error:', error.message);
      throw error;
    }
  }
}

Python

from saleswebhooks import SalesWebhooks, APIError
import time

def create_subscription_with_retry(client, linkedin_url, max_retries=3):
    """Create subscription with automatic retry on rate limits"""
    
    for attempt in range(max_retries):
        try:
            subscription = client.subscriptions.create(
                entity_type='contact',
                linkedin_url=linkedin_url
            )
            return subscription
            
        except APIError as e:
            if e.error_type == 'RATE_LIMIT_EXCEEDED':
                retry_after = e.details.get('retry_after', 60)
                print(f"Rate limited. Waiting {retry_after}s...")
                time.sleep(retry_after)
                continue
                
            elif e.error_type == 'VALIDATION_ERROR':
                print(f"Validation error: {e.message}")
                print(f"Field: {e.details.get('field')}")
                raise
                
            elif e.error_type == 'DUPLICATE_RESOURCE':
                print("Subscription already exists")
                return None
                
            else:
                print(f"API Error: {e.message}")
                raise
                
        except Exception as e:
            print(f"Unexpected error: {e}")
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # Exponential backoff
            else:
                raise

Error Recovery Strategies

💡 Best Practices

  • Implement retry logic - Use exponential backoff for transient errors
  • Log request IDs - Include in support tickets for faster resolution
  • Handle specific errors - Don't treat all errors the same
  • Validate inputs - Catch errors before making API calls
  • Monitor error rates - Track patterns to identify issues

Common Error Scenarios

Webhook Delivery Failures

{
  "error": {
    "type": "WEBHOOK_DELIVERY_FAILED",
    "message": "Failed to deliver webhook after 4 attempts",
    "code": "E1501",
    "details": {
      "webhook_id": "whd_3Zk1No5Pr",
      "endpoint_url": "https://your-app.com/webhook",
      "last_error": "Connection timeout",
      "attempts": 4,
      "first_attempt": "2025-01-20T14:00:00Z",
      "last_attempt": "2025-01-20T14:00:36Z"
    }
  }
}

Subscription Limits

{
  "error": {
    "type": "LIMIT_EXCEEDED",
    "message": "Subscription limit reached for your plan",
    "code": "E1601",
    "details": {
      "limit": 1000,
      "current": 1000,
      "plan": "professional",
      "upgrade_url": "https://console.saleswebhooks.com/billing/upgrade"
    }
  }
}

LinkedIn Data Errors

{
  "error": {
    "type": "LINKEDIN_ERROR",
    "message": "LinkedIn profile is private or doesn't exist",
    "code": "E1701",
    "details": {
      "linkedin_url": "https://linkedin.com/in/private-user",
      "reason": "profile_private",
      "suggestion": "Ensure the profile is public and URL is correct"
    }
  }
}

Troubleshooting Guide

🔍 Debug Checklist

  • 1. Check the exact error message and type
  • 2. Note the request ID for support
  • 3. Verify API key and permissions
  • 4. Check rate limit headers
  • 5. Validate request payload format
  • 6. Test with minimal example

Getting Help

If you continue to experience errors: