Skip to main content

Error Response Format

All API errors follow a consistent format:
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message in English",
    "message_es": "Mensaje legible en español",
    "details": { },
    "requestId": "req_abc123xyz"
  }
}

Error Categories

Validation Errors (400)

Returned when request data fails validation.
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Request validation failed",
    "details": {
      "errors": [
        {
          "field": "receiver.nationalId",
          "message": "National ID is required for Ecuador shipments"
        },
        {
          "field": "parcelDetails.weightLbs",
          "message": "Weight must be positive"
        }
      ]
    }
  }
}
How to handle:
  1. Parse the details.errors array
  2. Display field-specific errors to the user
  3. Do NOT retry automatically

Authentication Errors (401)

Returned when API key is invalid or missing.
{
  "success": false,
  "error": {
    "code": "AUTH_INVALID_API_KEY",
    "message": "The provided API key is invalid or has been revoked"
  }
}
How to handle:
  1. Check that API key is configured correctly
  2. Verify the key hasn’t been revoked in dashboard
  3. Contact support if issue persists

Rate Limit Errors (429)

Returned when you exceed request limits.
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests. Please wait before retrying.",
    "retryAfter": 30
  }
}
How to handle:
  1. Wait for retryAfter seconds
  2. Implement exponential backoff
  3. Consider request batching

Conflict Errors (409)

Returned when a resource conflict occurs.
{
  "success": false,
  "error": {
    "code": "PARCEL_DUPLICATE_TRACKING",
    "message": "A parcel with this tracking number already exists"
  }
}
How to handle:
  1. For duplicates, use the existing resource
  2. For locked resources, wait and retry
  3. Check idempotency key for accidental duplicates

Server Errors (500)

Returned when an unexpected error occurs.
{
  "success": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred. Please try again.",
    "requestId": "req_abc123xyz"
  }
}
How to handle:
  1. Retry with exponential backoff (up to 3 times)
  2. If persists, contact support with requestId
  3. Log the error for debugging

Error Code Reference

Order Errors

CodeHTTPDescription
PARCEL_NOT_FOUND404Tracking number doesn’t exist
PARCEL_DUPLICATE_TRACKING409Tracking number already in use
PARCEL_ALREADY_VOIDED409Order has already been cancelled
PARCEL_IN_TRANSIT409Cannot modify order in transit
PARCEL_DELIVERED409Cannot modify delivered order

Manifest Errors

CodeHTTPDescription
MANIFEST_LOCKED409Manifest already submitted
MANIFEST_AWB_DUPLICATE409AWB number already exists
MANIFEST_VALIDATION_FAILED400Country validation failed

Validation Errors

CodeHTTPDescription
VALIDATION_FAILED400One or more fields failed validation
VALIDATION_NATIONAL_ID400Invalid national ID format
VALIDATION_PHONE400Invalid phone number
VALIDATION_ADDRESS400Invalid address format
VALIDATION_CHECKSUM400Product checksum doesn’t match declared value

Auth Errors

CodeHTTPDescription
AUTH_MISSING_API_KEY401No API key provided
AUTH_INVALID_API_KEY401API key is invalid or revoked
AUTH_COUNTRY_NOT_ALLOWED403Key not authorized for this country
AUTH_IP_NOT_ALLOWED403Request IP not in whitelist

Retry Strategy

Implement exponential backoff for retryable errors:
async function apiCallWithRetry(fn, maxRetries = 3) {
  let lastError;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // Don't retry validation or auth errors
      if (error.status === 400 || error.status === 401) {
        throw error;
      }
      
      // Don't retry conflict errors (409) except for rate limits
      if (error.status === 409 && error.code !== 'RATE_LIMIT_EXCEEDED') {
        throw error;
      }
      
      // Calculate delay with exponential backoff
      const delay = error.retryAfter 
        ? error.retryAfter * 1000 
        : Math.min(1000 * Math.pow(2, attempt), 30000);
      
      console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms`);
      await sleep(delay);
    }
  }
  
  throw lastError;
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Localization

Error messages are provided in both English (message) and Spanish (message_es). To request errors in Spanish, add the Accept-Language header:
curl -H "Accept-Language: es" ...
Always save the requestId from error responses. Support can use this to investigate issues quickly.