Error Codes
MoMail uses standard HTTP response codes and provides detailed error information in the response body.
Response Format
Section titled “Response Format”All errors follow a consistent format:
{ "success": false, "error": { "code": "ERROR_CODE", "message": "Human-readable error description", "details": {} }}HTTP Status Codes
Section titled “HTTP Status Codes”| Status | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request parameters or JSON |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Insufficient permissions or plan quota |
| 404 | Not Found | Resource does not exist |
| 409 | Conflict | Resource already exists (e.g. mailbox email taken) |
| 422 | Unprocessable Entity | Semantic validation failed (e.g. mailbox local part rules) |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
Error Codes Reference
Section titled “Error Codes Reference”Authentication Errors
Section titled “Authentication Errors”UNAUTHORIZED (401)
Section titled “UNAUTHORIZED (401)”Missing or invalid API key.
{ "success": false, "error": { "code": "UNAUTHORIZED", "message": "Invalid or missing API key" }}Resolution: Check that your API key is included in the X-API-Key header and is valid.
FORBIDDEN (403)
Section titled “FORBIDDEN (403)”Insufficient permissions for the requested operation.
{ "success": false, "error": { "code": "FORBIDDEN", "message": "You do not have permission to access this resource" }}Resolution: Verify your API key has the necessary permissions or upgrade your plan.
Resource Errors
Section titled “Resource Errors”NOT_FOUND (404)
Section titled “NOT_FOUND (404)”The requested resource does not exist.
{ "success": false, "error": { "code": "NOT_FOUND", "message": "Domain not found" }}Resolution: Verify the resource ID is correct and the resource exists in your account.
CONFLICT (409)
Section titled “CONFLICT (409)”The resource already exists or there is a state conflict (for example, registering a domain that is already on your account).
Mailbox email address collisions use the code EMAIL_TAKEN with the same HTTP status 409; see Mailboxes.
{ "success": false, "error": { "code": "CONFLICT", "message": "Domain already exists" }}Resolution: Use a different identifier or update the existing resource.
Validation Errors
Section titled “Validation Errors”VALIDATION_ERROR (400)
Section titled “VALIDATION_ERROR (400)”Request parameters are invalid.
{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Invalid request parameters", "details": { "domain": "Invalid domain format", "email": "Invalid email address" } }}Resolution: Check the details field for specific validation failures and correct your request.
Mailbox creation (POST /v1/api/mailboxes)
Section titled “Mailbox creation (POST /v1/api/mailboxes)”Mailbox endpoints return machine-readable error.code values together with an appropriate HTTP status. Full tables, request fields, and examples are documented in Mailboxes API.
Summary:
| HTTP | Typical error.code | Meaning |
|---|---|---|
| 400 | MISSING_FIELDS, BAD_REQUEST | Missing domain / local_part / email_address, invalid JSON, or bad email_address shape |
| 403 | MAILBOX_QUOTA | Plan mailbox limit reached |
| 409 | EMAIL_TAKEN | Full email address already exists globally |
| 422 | LOCAL_TOO_SHORT, INVALID_LOCAL, DISALLOWED_LOCAL | Local part rules or reserved-name list |
| 422 | DOMAIN_MISMATCH, DOMAIN_NOT_ALLOWED | Domain does not match body or is not allowed for the account |
Preflight without creating: GET /v1/api/mailboxes/check-email?local_part=…&domain=… (same auth as other v1 routes).
VERIFICATION_FAILED (400)
Section titled “VERIFICATION_FAILED (400)”Domain verification failed.
{ "success": false, "error": { "code": "VERIFICATION_FAILED", "message": "TXT record not found or does not match verification token", "details": { "expected_txt_record": "momail-verify=abc123...", "instructions": "Add a TXT record to your DNS with the above value" } }}Resolution: Add the required TXT record to your DNS and wait for propagation (can take up to 24 hours).
Rate Limit Errors
Section titled “Rate Limit Errors”RATE_LIMIT_EXCEEDED (429)
Section titled “RATE_LIMIT_EXCEEDED (429)”Too many requests in the current time window.
{ "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded. Please try again later.", "details": { "limit": 20, "window": "minute", "retry_after": 45 } }}Resolution: Wait for the Retry-After period before making more requests. Consider upgrading your plan for higher limits.
Server Errors
Section titled “Server Errors”INTERNAL_ERROR (500)
Section titled “INTERNAL_ERROR (500)”An unexpected server error occurred.
{ "success": false, "error": { "code": "INTERNAL_ERROR", "message": "Failed to process request" }}Resolution: Retry the request after a short delay. If the error persists, contact support.
SERVICE_UNAVAILABLE (503)
Section titled “SERVICE_UNAVAILABLE (503)”The service is temporarily unavailable.
{ "success": false, "error": { "code": "SERVICE_UNAVAILABLE", "message": "Service temporarily unavailable. Please try again later." }}Resolution: Wait a few moments and retry your request.
Handling Errors
Section titled “Handling Errors”Basic Error Handling
Section titled “Basic Error Handling”async function makeApiRequest(url, options) { try { const response = await fetch(url, options); const data = await response.json();
if (!data.success) { console.error(`Error ${data.error.code}: ${data.error.message}`);
if (data.error.details) { console.error('Details:', data.error.details); }
throw new Error(data.error.message); }
return data; } catch (error) { console.error('Request failed:', error); throw error; }}Retry Logic with Exponential Backoff
Section titled “Retry Logic with Exponential Backoff”import timeimport requests
def api_request_with_retry(url, headers, max_retries=3): for attempt in range(max_retries): try: response = requests.get(url, headers=headers) data = response.json()
if not data.get('success'): error_code = data['error']['code']
# Don't retry client errors if response.status_code in [400, 401, 403, 404, 409, 422]: raise Exception(f"Client error: {error_code}")
# Retry server errors and rate limits if response.status_code in [429, 500, 503]: wait_time = min(2 ** attempt, 60) time.sleep(wait_time) continue
return data
except requests.exceptions.RequestException: if attempt == max_retries - 1: raise time.sleep(2 ** attempt)
raise Exception("Max retries exceeded")Getting Help
Section titled “Getting Help”If you encounter errors you cannot resolve:
- Check the status page for known issues
- Contact support at support@momail.io