Error Response Formats

Error response formats for the Shipping, Queries, and Geocodes APIs — with real examples and a helper to normalize all three.

Each Envia API has its own error response shape. This page documents the real formats returned by the Shipping, Queries, and Geocodes APIs so you can handle them correctly in your integration.

Error response formats

Each Envia API returns errors in its own format. Your error-handling code should account for these differences.

Shipping API

The Shipping API wraps errors inside a meta / error object:

{
  "meta": "error",
  "error": {
    "code": 400,
    "description": "Invalid Option",
    "message": "The origin.postalCode field is required."
  }
}
FieldTypeDescription
metastring"error" for error responses
error.codeintNumeric error code
error.descriptionstringShort error category (e.g., "Invalid Option")
error.messagestringHuman-readable detail of the issue
⚠️

Important: Some Shipping API errors return HTTP 200 with "meta": "error" in the body instead of a 4xx status code. Always check the meta field in the response — do not rely solely on the HTTP status code.

Authentication errors on the Shipping API return a plain-text string instead of JSON:

"Authentication error."

Handle this case in your code by checking the Content-Type header or wrapping JSON parsing in a try/catch.

Queries API

The Queries API uses a different structure:

{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Invalid or expired token."
}
FieldTypeDescription
statusCodeintHTTP status code
errorstringError name (e.g., "Unauthorized", "Not Found")
messagestringHuman-readable description of the issue

Geocodes API

The Geocodes API does not require authentication. For error responses (e.g., invalid postal code), it follows the same structure as the Queries API:

{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Postal code not found for the given country."
}

Handling all three formats

Since each API has its own error shape, a robust integration should normalize errors into a common internal format. Here is a simple approach:

function parseEnviaError(response, body) {
  // Shipping API — plain-text auth error
  if (typeof body === "string") {
    return { code: response.status, message: body };
  }
  // Shipping API — meta/error wrapper
  if (body.meta === "error") {
    return { code: body.error?.code, message: body.error?.message };
  }
  // Queries API (Geocodes uses same shape but does not require auth)
  if (body.statusCode) {
    return { code: body.statusCode, message: body.message };
  }
  return { code: response.status, message: "Unknown error" };
}
💡

Tip: Some carrier-level errors include an additional carrier_message field with the carrier's own error text — useful when debugging carrier-specific rejections.

Retry strategy

Not all errors should be retried. Use this table to decide how your application should respond:

ScenarioHTTP StatusActionRetry?
Validation error (missing field, bad format)400 / 422Fix the request and resendNo
Authentication error (expired or wrong token)401Refresh or replace the API keyNo
Rate limit exceeded429Wait and retry after the indicated delayYes
Server error500 / 502 / 503Transient issue on Envia's sideYes
Timeout (no response)Network or server overloadYes
Shipping API returns HTTP 200 with "meta": "error"200Read error.message — usually a validation or carrier errorNo
💡

Recommended retry pattern: For retryable errors, use exponential backoff — wait 1s, then 2s, then 4s, up to a maximum of 3 retries. If the error persists after 3 attempts, log the error and alert your team.

Getting help

If an error persists after checking the response format:

  1. Capture the full JSON error response including carrier_message if present
  2. Note the endpoint URL, HTTP method, and timestamp
  3. Open a support ticket with these details
📚

What to read next: