When an error occurs, the API returns a JSON response with an error object:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email address format",
"details": {
"field": "to",
"value": "invalid-email"
}
},
"meta": {
"timestamp": "2024-01-15T10:30:00.000Z",
"requestId": "req_abc123",
"path": "/api/v1/emails"
}
}
HTTP Status Codes
| Status | Meaning |
|---|
200 | Success |
201 | Created |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid or missing API key |
403 | Forbidden - Insufficient permissions |
404 | Not Found - Resource doesn’t exist |
409 | Conflict - Resource already exists |
422 | Unprocessable Entity - Validation failed |
429 | Too Many Requests - Rate limit exceeded |
500 | Internal Server Error |
Error Codes
Authentication Errors
| Code | HTTP Status | Description |
|---|
UNAUTHORIZED | 401 | Missing or invalid API key |
KEY_REVOKED | 401 | API key has been revoked |
KEY_EXPIRED | 401 | API key has expired |
FORBIDDEN | 403 | API key doesn’t have required permissions |
Validation Errors
| Code | HTTP Status | Description |
|---|
VALIDATION_ERROR | 400 | Request body validation failed |
INVALID_EMAIL | 400 | Email address format is invalid |
MISSING_REQUIRED_FIELD | 400 | Required field not provided |
INVALID_PARAMETER | 400 | Query parameter is invalid |
Domain Errors
| Code | HTTP Status | Description |
|---|
DNS_VERIFICATION_FAILED | 400 | Domain DNS records not verified |
DOMAIN_NOT_VERIFIED | 400 | Domain verification incomplete |
FROM_DOMAIN_MISMATCH | 400 | From address must use verified domain |
DOMAIN_NOT_FOUND | 404 | Domain not found in account |
Email Sending Errors
| Code | HTTP Status | Description |
|---|
SENDING_DISABLED | 403 | Email sending is disabled for domain |
RECIPIENT_SUPPRESSED | 400 | Recipient is on suppression list |
ATTACHMENT_NOT_FOUND | 400 | Referenced attachment doesn’t exist |
ATTACHMENT_TOO_LARGE | 400 | Attachment exceeds size limit |
TEMPLATE_NOT_FOUND | 404 | Template ID doesn’t exist |
TEMPLATE_RENDER_ERROR | 400 | Template variable substitution failed |
| Code | HTTP Status | Description |
|---|
CONTACT_NOT_FOUND | 404 | Contact doesn’t exist |
CONTACT_ALREADY_EXISTS | 409 | Contact with email already exists in list |
LIST_NOT_FOUND | 404 | Contact list doesn’t exist |
INVALID_CUSTOM_FIELD | 400 | Custom field name or value invalid |
Verification Errors
| Code | HTTP Status | Description |
|---|
VERIFICATION_FAILED | 400 | Email verification could not complete |
BATCH_TOO_LARGE | 400 | Batch verification exceeds limit |
INSUFFICIENT_CREDITS | 402 | Not enough verification credits |
Rate Limiting
| Code | HTTP Status | Description |
|---|
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
When rate limited, check response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2024-01-01T12:01:00Z
Retry-After: 60
Handling Errors in SDKs
import { MailBreeze, MailBreezeError } from "mailbreeze";
const mailbreeze = new MailBreeze({ apiKey: "sk_live_xxx" });
try {
await mailbreeze.emails.send({
from: "hello@yourdomain.com",
to: "invalid-email",
subject: "Test",
});
} catch (error) {
if (error instanceof MailBreezeError) {
console.log(error.code); // "INVALID_EMAIL"
console.log(error.message); // "Invalid email address format"
console.log(error.status); // 400
}
}
from mailbreeze import MailBreeze, MailBreezeError
client = MailBreeze(api_key="sk_live_xxx")
try:
await client.emails.send(
from_="hello@yourdomain.com",
to="invalid-email",
subject="Test",
)
except MailBreezeError as e:
print(e.code) # "INVALID_EMAIL"
print(e.message) # "Invalid email address format"
print(e.status) # 400
client := mailbreeze.NewClient("sk_live_xxx")
email, err := client.Emails.Send(ctx, &mailbreeze.SendEmailParams{
From: "hello@yourdomain.com",
To: []string{"invalid-email"},
Subject: "Test",
})
if err != nil {
var mbErr *mailbreeze.Error
if errors.As(err, &mbErr) {
fmt.Println(mbErr.Code) // "INVALID_EMAIL"
fmt.Println(mbErr.Message) // "Invalid email address format"
fmt.Println(mbErr.Status) // 400
}
}
use MailBreeze\MailBreeze;
use MailBreeze\Exceptions\MailBreezeException;
$mailbreeze = new MailBreeze('sk_live_xxx');
try {
$mailbreeze->emails->send([
'from' => 'hello@yourdomain.com',
'to' => 'invalid-email',
'subject' => 'Test',
]);
} catch (MailBreezeException $e) {
echo $e->getCode(); // "INVALID_EMAIL"
echo $e->getMessage(); // "Invalid email address format"
echo $e->getStatus(); // 400
}
use mailbreeze::{Client, Error};
let client = Client::new("sk_live_xxx");
match client.emails().send(SendEmailParams {
from: "hello@yourdomain.com".to_string(),
to: vec!["invalid-email".to_string()],
subject: Some("Test".to_string()),
..Default::default()
}).await {
Ok(email) => println!("Sent: {}", email.id),
Err(Error::Api { code, message, status }) => {
println!("Code: {}", code); // "INVALID_EMAIL"
println!("Message: {}", message); // "Invalid email address format"
println!("Status: {}", status); // 400
}
Err(e) => println!("Other error: {}", e),
}
Retry Strategy
For transient errors (5xx status codes and rate limits), implement exponential backoff:
async function sendWithRetry(params, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await mailbreeze.emails.send(params);
} catch (error) {
if (error.status >= 500 || error.status === 429) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error; // Don't retry client errors
}
}
throw new Error("Max retries exceeded");
}
The official SDKs include automatic retry logic with exponential backoff for transient errors.