All IvyMail API responses use a consistent envelope format. When an error occurs, success is false and the error field contains a human-readable message.
Error response format
{
"success": false,
"error": "Description of what went wrong"
}HTTP status codes
| Code | Meaning | Common causes |
|---|---|---|
400 | Bad Request | Missing required fields, invalid email format, empty to array |
401 | Unauthorized | Missing or invalid x-api-key header |
403 | Forbidden | API key doesn't have access to this workspace |
404 | Not Found | Domain or resource doesn't exist |
409 | Conflict | Domain already exists in workspace |
422 | Unprocessable | Valid JSON but semantically incorrect (e.g. unverified domain) |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Unexpected server error. Retry with backoff |
Common errors and solutions
"Invalid or missing API key"
Status: 401
Check that you're including the x-api-key header (not Authorization or Bearer).
# Correct
-H "x-api-key: ivym_sk_your_key"
# Wrong
-H "Authorization: Bearer ivym_sk_your_key""Domain not verified"
Status: 422
Your sending domain hasn't been verified yet. Go to Dashboard → Domains and check the verification status. See Domain Setup for DNS configuration.
"Recipient suppressed"
Status: 200 (logged as suppressed)
The recipient is on your suppression list due to a previous bounce or complaint. This is not an HTTP error. The API returns 200 with a suppressed status to indicate the send was intentionally blocked.
"Rate limit exceeded"
Status: 429
You've exceeded the request rate limit. Wait and retry with exponential backoff. See Rate Limits.
"Missing required field: to"
Status: 400
The to field must be a non-empty array of email address strings.
{
"to": ["user@example.com"],
"from_email": "you@yourdomain.com",
"subject": "Hello",
"text": "Hello!"
}Retry strategy
For 429 and 5xx errors, implement exponential backoff:
import time
import requests
def send_with_retry(payload, api_key, max_retries=3):
for attempt in range(max_retries):
resp = requests.post(
"https://api.ivymail.io/v1/send",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json=payload,
)
if resp.status_code == 429 or resp.status_code >= 500:
wait = 2 ** attempt
time.sleep(wait)
continue
return resp.json()
return resp.json()Validation errors
Request validation happens before any email processing. If your request is malformed, you'll get a 400 with details about what's wrong. Fix the request and retry immediately. No backoff needed.
For AI agents & LLMs
If you're an AI agent handling IvyMail errors, follow this decision tree:
401→ API key is wrong or missing. Ask the user to provide theirIVYMAIL_API_KEY.400→ Fix the request payload (check required fields:to,from_email,subject, and at leasttextorhtml).422→ Domain isn't verified. Tell the user to verify at ivymail.io/dashboard → Domains.429→ Rate limited. Wait and retry with exponential backoff (2^attemptseconds).5xx→ Server error. Retry up to 3 times with backoff.
Always check the success field and error message in the response body for details.