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:
- Parse the
details.errors array
- Display field-specific errors to the user
- 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:
- Check that API key is configured correctly
- Verify the key hasn’t been revoked in dashboard
- 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:
- Wait for
retryAfter seconds
- Implement exponential backoff
- 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:
- For duplicates, use the existing resource
- For locked resources, wait and retry
- 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:
- Retry with exponential backoff (up to 3 times)
- If persists, contact support with
requestId
- Log the error for debugging
Error Code Reference
Order Errors
| Code | HTTP | Description |
|---|
PARCEL_NOT_FOUND | 404 | Tracking number doesn’t exist |
PARCEL_DUPLICATE_TRACKING | 409 | Tracking number already in use |
PARCEL_ALREADY_VOIDED | 409 | Order has already been cancelled |
PARCEL_IN_TRANSIT | 409 | Cannot modify order in transit |
PARCEL_DELIVERED | 409 | Cannot modify delivered order |
Manifest Errors
| Code | HTTP | Description |
|---|
MANIFEST_LOCKED | 409 | Manifest already submitted |
MANIFEST_AWB_DUPLICATE | 409 | AWB number already exists |
MANIFEST_VALIDATION_FAILED | 400 | Country validation failed |
Validation Errors
| Code | HTTP | Description |
|---|
VALIDATION_FAILED | 400 | One or more fields failed validation |
VALIDATION_NATIONAL_ID | 400 | Invalid national ID format |
VALIDATION_PHONE | 400 | Invalid phone number |
VALIDATION_ADDRESS | 400 | Invalid address format |
VALIDATION_CHECKSUM | 400 | Product checksum doesn’t match declared value |
Auth Errors
| Code | HTTP | Description |
|---|
AUTH_MISSING_API_KEY | 401 | No API key provided |
AUTH_INVALID_API_KEY | 401 | API key is invalid or revoked |
AUTH_COUNTRY_NOT_ALLOWED | 403 | Key not authorized for this country |
AUTH_IP_NOT_ALLOWED | 403 | Request 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.