Error Handling
The MailShield API uses conventional HTTP response codes and returns errors in a consistent JSON format.
Error Response Format
All errors follow this structure:
json
{
"error": {
"code": "error_code",
"message": "Human-readable error message",
"details": {} // Optional additional information
}
}HTTP Status Codes
| Code | Description |
|---|---|
200 | Success |
201 | Created (for POST requests) |
400 | Bad Request - Invalid parameters or request body |
401 | Unauthorized - Invalid or missing authentication |
403 | Forbidden - Valid auth but insufficient permissions |
404 | Not Found - Resource doesn't exist |
409 | Conflict - Resource already exists |
429 | Too Many Requests - Rate limit exceeded |
500 | Internal Server Error - Something went wrong on our end |
Error Codes
Authentication Errors
| Code | Status | Description |
|---|---|---|
unauthorized | 401 | Missing or invalid Authorization header |
unauthorized | 401 | Invalid API token format |
unauthorized | 401 | API token has expired |
Authorization Errors
| Code | Status | Description |
|---|---|---|
forbidden | 403 | IP address not allowed for this token |
forbidden | 403 | API access not enabled for organization |
forbidden | 403 | Domain limit reached |
Resource Errors
| Code | Status | Description |
|---|---|---|
not_found | 404 | Domain not found |
conflict | 409 | Domain already exists |
Validation Errors
| Code | Status | Description |
|---|---|---|
validation_error | 400 | Invalid request body or parameters |
Validation errors include a details array with specific field errors:
json
{
"error": {
"code": "validation_error",
"message": "Invalid request body",
"details": [
{
"path": ["domain"],
"message": "Invalid domain format"
},
{
"path": ["testEmailLocalPart"],
"message": "testEmailLocalPart is required for domains that receive email"
}
]
}
}Rate Limiting Errors
| Code | Status | Description |
|---|---|---|
rate_limited | 429 | Too many requests |
Rate limit errors include a Retry-After header with seconds to wait.
Handling Errors
Python Example
python
import requests
def api_request(url, headers):
response = requests.get(url, headers=headers)
if response.ok:
return response.json()["data"]
error = response.json()["error"]
if response.status_code == 401:
raise AuthenticationError(error["message"])
elif response.status_code == 403:
raise PermissionError(error["message"])
elif response.status_code == 404:
raise NotFoundError(error["message"])
elif response.status_code == 429:
retry_after = response.headers.get("Retry-After", 60)
raise RateLimitError(f"Rate limited. Retry after {retry_after}s")
else:
raise APIError(error["message"])Node.js Example
javascript
async function apiRequest(url, headers) {
const response = await fetch(url, { headers });
const body = await response.json();
if (response.ok) {
return body.data;
}
const { code, message, details } = body.error;
switch (response.status) {
case 401:
throw new AuthenticationError(message);
case 403:
throw new PermissionError(message);
case 404:
throw new NotFoundError(message);
case 429:
const retryAfter = response.headers.get('Retry-After');
throw new RateLimitError(message, retryAfter);
case 400:
throw new ValidationError(message, details);
default:
throw new APIError(message);
}
}Debugging Tips
Check the error code - It tells you exactly what went wrong
Read the message - It's human-readable and often actionable
Look at details - For validation errors, check which fields failed
Verify your token - Most 401 errors are token-related
Check IP allowlist - 403 with "IP not allowed" means your IP isn't whitelisted
Review rate limits - 429 means you're making too many requests