Reference
Error Codes
Complete reference of error codes returned by the Unosend API and how to handle them.
Error Response Format
When an error occurs, the API returns a JSON response with the following structure:
error-response.json
{
"error": {
"code": "invalid_api_key",
"message": "The API key provided is invalid or has been revoked.",
"status": 401,
"details": {
"api_key_prefix": "un_xxx..."
}
}
}| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error identifier |
message | string | Human-readable error description |
status | number | HTTP status code |
details | object | Additional context (optional) |
HTTP Status Codes
2xx
Success
Request was successful
4xx
Client Error
Something was wrong with the request
5xx
Server Error
Something went wrong on our end
Authentication Errors
| Code | Status | Description |
|---|---|---|
missing_api_key | 401 | No API key provided in request |
invalid_api_key | 401 | API key is invalid or revoked |
api_key_expired | 401 | API key has expired |
insufficient_permissions | 403 | API key lacks required permissions |
How to Fix
terminal
# Correct format: Include API key in Authorization header
curl -X POST "https://www.unosend.co/api/v1/emails" \
-H "Authorization: Bearer un_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"from": "hello@yourdomain.com", "to": "user@example.com", "subject": "Test", "html": "<p>Hello</p>"}'Common mistake: Make sure you're using Bearer prefix before your API key. The format should be Authorization: Bearer un_xxxxxxxx.
Validation Errors
| Code | Status | Description |
|---|---|---|
validation_error | 400 | Request body failed validation |
missing_required_field | 400 | Required field is missing |
invalid_email_address | 400 | Email address format is invalid |
invalid_from_address | 400 | From address domain not verified |
invalid_template_variables | 400 | Template variables don't match schema |
attachment_too_large | 400 | Attachment exceeds size limit (10MB) |
invalid_json | 400 | Request body is not valid JSON |
Example Validation Error
validation-error.json
{
"error": {
"code": "validation_error",
"message": "Request validation failed",
"status": 400,
"details": {
"errors": [
{
"field": "to",
"message": "must be a valid email address",
"value": "not-an-email"
},
{
"field": "subject",
"message": "is required",
"value": null
}
]
}
}
}Handling Validation Errors
handle-validation.ts
const response = await fetch('https://www.unosend.co/api/v1/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (response.status === 400) {
const error = await response.json();
if (error.error.code === 'validation_error') {
// Log each field error
error.error.details.errors.forEach((e: any) => {
console.error(`Field '${e.field}': ${e.message}`);
});
}
}Resource Errors
| Code | Status | Description |
|---|---|---|
not_found | 404 | Resource doesn't exist |
email_not_found | 404 | Email ID not found |
domain_not_found | 404 | Domain not found in workspace |
template_not_found | 404 | Template ID not found |
audience_not_found | 404 | Audience not found |
contact_not_found | 404 | Contact not found |
webhook_not_found | 404 | Webhook not found |
Example 404 Response
not-found.json
{
"error": {
"code": "email_not_found",
"message": "Email with ID 'em_abc123' not found",
"status": 404,
"details": {
"email_id": "em_abc123"
}
}
}Rate Limit Errors
| Code | Status | Description |
|---|---|---|
rate_limit_exceeded | 429 | Too many requests per second |
daily_limit_exceeded | 429 | Daily email limit reached |
quota_exceeded | 429 | Monthly quota exceeded |
Rate Limit Response
rate-limit.json
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry after 60 seconds.",
"status": 429,
"details": {
"retry_after": 60,
"limit": 100,
"remaining": 0,
"reset_at": "2024-01-15T10:30:00Z"
}
}
}See the Rate Limits documentation for handling strategies and retry logic examples.
Domain Errors
| Code | Status | Description |
|---|---|---|
domain_not_verified | 400 | Domain has not been verified yet |
domain_verification_failed | 400 | DNS records not found or incorrect |
domain_already_exists | 409 | Domain already registered in another workspace |
dkim_verification_failed | 400 | DKIM record not found or invalid |
spf_verification_failed | 400 | SPF record not found or invalid |
See Domain Verification guide for setting up DNS records correctly.
Server Errors
| Code | Status | Description |
|---|---|---|
internal_server_error | 500 | Unexpected server error |
service_unavailable | 503 | Service temporarily unavailable |
gateway_timeout | 504 | Request timed out |
email_delivery_failed | 500 | Failed to deliver email to mail server |
5xx errors are rare and usually temporary. Implement retry logic with exponential backoff for these errors.
Complete Error Handling Example
JavaScript/TypeScript
error-handling.ts
interface UnosendError {
error: {
code: string;
message: string;
status: number;
details?: Record<string, any>;
};
}
async function sendEmail(payload: EmailPayload): Promise<EmailResponse> {
const response = await fetch('https://www.unosend.co/api/v1/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
const error: UnosendError = await response.json();
switch (error.error.code) {
case 'invalid_api_key':
case 'missing_api_key':
throw new Error('Authentication failed. Check your API key.');
case 'validation_error':
const fields = error.error.details?.errors
?.map((e: any) => `${e.field}: ${e.message}`)
.join(', ');
throw new Error(`Validation failed: ${fields}`);
case 'rate_limit_exceeded':
const retryAfter = error.error.details?.retry_after || 60;
// Implement retry logic
await sleep(retryAfter * 1000);
return sendEmail(payload); // Retry once
case 'domain_not_verified':
throw new Error('Verify your domain before sending emails.');
case 'quota_exceeded':
throw new Error('Monthly email quota exceeded. Upgrade your plan.');
case 'internal_server_error':
case 'service_unavailable':
// Retry with exponential backoff
throw new Error('Service temporarily unavailable. Please retry.');
default:
throw new Error(error.error.message);
}
}
return response.json();
}Python
error_handling.py
import requests
import time
class UnosendError(Exception):
def __init__(self, code, message, status, details=None):
self.code = code
self.message = message
self.status = status
self.details = details or {}
super().__init__(message)
def send_email(payload):
api_key = "un_xxxxxxxx"
response = requests.post(
"https://www.unosend.co/api/v1/emails",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
},
json=payload
)
if not response.ok:
error = response.json()["error"]
if error["code"] == "rate_limit_exceeded":
retry_after = error.get("details", {}).get("retry_after", 60)
time.sleep(retry_after)
return send_email(payload) # Retry
if error["code"] == "validation_error":
errors = error.get("details", {}).get("errors", [])
fields = ", ".join([f"{e['field']}: {e['message']}" for e in errors])
raise UnosendError(
error["code"],
f"Validation failed: {fields}",
error["status"],
error.get("details")
)
raise UnosendError(
error["code"],
error["message"],
error["status"],
error.get("details")
)
return response.json()cURL Error Checking
terminal
# Check HTTP status code and response
response=$(curl -s -w "\n%{http_code}" -X POST "https://www.unosend.co/api/v1/emails" \
-H "Authorization: Bearer un_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"from": "hello@yourdomain.com", "to": "user@example.com", "subject": "Test", "html": "<p>Hello</p>"}')
status=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
if [ "$status" -ge 400 ]; then
echo "Error ($status): $body"
else
echo "Success: $body"
fiQuick Reference
Retryable Errors
- •
429Rate limit exceeded - •
500Internal server error - •
503Service unavailable - •
504Gateway timeout
Non-Retryable Errors
- •
400Validation error - •
401Authentication error - •
403Permission denied - •
404Resource not found