Reference
Error Codes
PostMTA uses standard HTTP status codes and structured error objects with machine-readable codes.
Error Response Format
{"error":{"code":"INVALID_RECIPIENT","message":"The recipient address is not valid.","status":400,"details":{"field":"to[0].address","value":"bad@x"},"docs_url":"https://docs.postmta.com/errors/INVALID_RECIPIENT"}}HTTP Status Codes
| Code | Meaning |
|---|---|
200 | OK (includes suppression cases) |
201 | Created |
400 | Bad Request - invalid JSON or missing required field |
401 | Unauthorized - missing or invalid API key |
403 | Forbidden - valid key but insufficient scopes |
404 | Not Found - resource does not exist |
422 | Unprocessable Entity - validation failure |
429 | Too Many Requests - rate limit exceeded |
500 | Internal Server Error |
503 | Service Unavailable - retry with backoff |
API Error Codes
| Code | Status | Description |
|---|---|---|
INVALID_API_KEY | 401 | API key is invalid or revoked |
INSUFFICIENT_SCOPES | 403 | Key lacks required scope |
INVALID_RECIPIENT | 400 | Recipient address is malformed |
UNVERIFIED_DOMAIN | 400 | Sending domain not verified |
DOMAIN_ALREADY_EXISTS | 409 | Domain already added |
RATE_LIMIT_EXCEEDED | 429 | Request rate limit hit |
VOLUME_LIMIT_EXCEEDED | 429 | Monthly or daily volume quota exceeded |
INVALID_TEMPLATE | 400 | Template ID not found |
MESSAGE_TOO_LARGE | 400 | Message exceeds 25 MB limit |
Handling Errors
async function sendWithRetry(message, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await postmta.messages.send(message);
} catch (err) {
if (err.status >= 500) {
await new Promise(r => setTimeout(r, Math.min(1000 * 2 ** attempt, 30000)));
continue;
}
throw err;
}
}
}