Errors
All error responses from the MailerDash API follow a unified format inspired by Stripe. This makes programmatic handling straightforward: you can tell what type of error occurred (and how to react) without parsing the text message.
Error response structure
Section titled “Error response structure”{ "error": { "type": "validation_error", "code": "domain_unauthorized", "message": "The from domain is not authorized for this key.", "param": "from.email" }}| Field | Type | Description |
|---|---|---|
type | string | Error category (see table below). Determines the retry strategy. |
code | string | Machine-readable specific code. |
message | string | Human-readable description in English. |
param | string (optional) | Field or parameter that caused the error, when applicable. |
details | array (optional) | List of sub-errors for multiple-validation cases. |
Error types (type)
Section titled “Error types (type)”The type field is categorical and lets you decide on a strategy without reading code or message:
type | HTTP | When it occurs |
|---|---|---|
validation_error | 400 / 422 | The request has invalid or missing fields. Do not retry without fixing. |
authentication_error | 401 | Token missing, malformed, or invalid. Check the API key. |
authorization_error | 403 | Authenticated but lacking permissions. The key does not have the required scope. |
not_found_error | 404 | The requested resource does not exist. |
rate_limit_error | 429 | Rate limit or monthly quota exceeded. Retry with backoff. |
conflict_error | 409 | Conflict with the current state (e.g. resource already exists). |
payload_too_large_error | 413 | The body exceeds the allowed size (e.g. attachments too large). |
idempotency_error | 409 | The idempotency key was already used with a different payload. |
smtp_error | 5xx | Error delivering the email via SMTP. |
api_error | 5xx | Internal server error. Retry with exponential backoff. |
Specific codes (code)
Section titled “Specific codes (code)”Transactional send errors
Section titled “Transactional send errors”code | type | Description |
|---|---|---|
domain_unauthorized | authorization_error | The from domain is not authorized for this API key. |
email_not_verified | authorization_error | The account has not verified its email; sends are blocked until it does. |
free_requires_verified_domain | authorization_error | The free plan requires a verified domain to send. |
monthly_quota_exceeded | rate_limit_error | The plan’s monthly send quota has been reached. |
rate_limit_exceeded | rate_limit_error | The per-minute request limit (rate_per_min) has been exceeded. |
Domain errors
Section titled “Domain errors”code | type | Description |
|---|---|---|
max_domains_exceeded | validation_error | You have reached the domain limit allowed by your plan. |
domain_taken | conflict_error | The domain is already registered under another account on the platform. |
reserved_domain | validation_error | The domain is reserved and cannot be registered by clients. |
cname_delegation_disabled | validation_error | CNAME delegation is not enabled for this account. |
API key errors
Section titled “API key errors”code | type | Description |
|---|---|---|
max_api_keys_exceeded | validation_error | You have reached the API key limit for your plan. |
key_not_linked | authorization_error | The user’s JWT has no linked API key for the requested resource. |
already_revoked | conflict_error | The API key has already been revoked. |
wrong_password | authentication_error | The provided password is incorrect (operations requiring confirmation). |
Billing and plan errors
Section titled “Billing and plan errors”code | type | Description |
|---|---|---|
billing_unavailable | api_error | The billing system is not available at this time. |
no_billing_account | validation_error | The account does not have an active Stripe subscription. |
no_active_subscription | validation_error | There is no active subscription for this operation. |
overage_not_offered | validation_error | The current plan does not offer overage sends. |
package_not_selectable | validation_error | The requested plan is not available for selection. |
already_subscribed | conflict_error | You already have this plan active. |
General errors
Section titled “General errors”code | type | Description |
|---|---|---|
not_found | not_found_error | The resource does not exist. |
unauthorized | authentication_error | Token missing or invalid. |
forbidden | authorization_error | No permission for this operation. |
payload_too_large | payload_too_large_error | Body or attachments too large. |
internal_error | api_error | Internal server error. |
bad_request | validation_error | Malformed request. |
Multiple validation errors
Section titled “Multiple validation errors”When a single request has several invalid fields, the response includes the details array:
{ "error": { "type": "validation_error", "code": "invalid", "message": "Request validation failed", "details": [ { "code": "required", "message": "'subject' is required", "param": "subject" }, { "code": "invalid_format", "message": "'from.email' must be a valid email", "param": "from.email" } ] }}Node.js handling example
Section titled “Node.js handling example”const res = await fetch('https://api.mailerdash.com/v1/mail/send', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILERDASH_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ from: { email: 'noreply@mi-app.com' }, to: [...], subject: '...', text: '...' }),});
if (!res.ok) { const { error } = await res.json();
if (error.type === 'rate_limit_error') { // Retry after a backoff — the Retry-After header may indicate when console.warn('Rate limit alcanzado, reintentando en 60s...', error.code); } else if (error.type === 'validation_error') { // Do not retry — fix the request console.error('Error de validación:', error.code, error.param); } else if (error.type === 'authentication_error') { // Check the API key console.error('API key inválida o sin permisos'); } else { console.error('Error inesperado:', error); }}See the full endpoint reference at /reference/transactional/ and /reference/bulk/.