Error Codes
PostMTA surfaces errors at two layers: the SMTP layer (during message delivery) and the REST API layer (when your application calls the PostMTA API). This page documents every error code you may encounter and how to resolve each class of error.
SMTP Error Codes
Overview
When PostMTA delivers a message to a recipient MTA, the recipient MTA responds with an SMTP reply code. PostMTA classifies these codes as described below. The table covers codes most commonly returned by major inbox providers and relay servers.
SMTP Reply Code Reference
| Code | Enhanced Code | Meaning | PostMTA Action |
|---|---|---|---|
211 | — | System status / help reply | Information only; continue |
214 | — | Help message | Information only; continue |
220 | — | Service ready (greeting) | Expected greeting; continue |
221 | — | Service closing channel | Normal close; retry later |
250 | — | Requested mail action okay (delivered) | Delivered; record event |
251 | — | User not local; will forward | Treated as delivered |
354 | — | Start mail input | Expected; send body |
421 | 4.3.2 | Service not available — try again later | Soft bounce; enter retry schedule |
450 | 4.1.0–4.1.8 | Mailbox unavailable (temp: full, disabled, etc.) | Soft bounce; enter retry schedule |
451 | 4.2.0–4.2.2 | Requested action aborted: local error | Soft bounce; enter retry schedule |
452 | 4.3.1 | Requested action not taken: insufficient storage | Soft bounce; enter retry schedule |
455 | — | Server cannot accommodate parameters | Soft bounce; retry |
500 | 5.5.2 | Syntax error — command not recognised | Hard bounce; do not retry |
501 | 5.5.2–5.5.4 | Syntax error in parameters or arguments | Hard bounce; do not retry |
502 | 5.5.1 | Command not implemented | Hard bounce; do not retry |
503 | 5.5.1 | Bad sequence of commands | Hard bounce; do not retry |
504 | 5.5.3 | Parameter not implemented | Hard bounce; do not retry |
550 | 5.1.1–5.7.1 | Requested action not taken: mailbox unavailable | Hard bounce; classify by sub-code |
551 | 5.1.6 | User not local; try forwarding path | Treated as delivered; forwarding |
552 | 5.3.0–5.3.4 | Requested mail action aborted: exceeded storage allocation | Soft bounce; retry (mailbox full) |
553 | 5.1.1–5.1.7 | Requested action not taken: mailbox name not allowed | Hard bounce; invalid address |
554 | 5.0.0–5.7.1 | Transaction failed (spam, policy, etc.) | Hard bounce; classify by sub-code |
Enhanced Status Codes
The enhanced status code (e.g., 5.1.1, 4.5.3) provides a machine-readable sub-classification of the SMTP reply code. PostMTA maps these to internal bounce reasons:
| Enhanced Code | Sub-Class | Human Readable |
|---|---|---|
5.1.0 | Other address status | Address parsing issue |
5.1.1 | Bad destination mailbox address | Mailbox does not exist |
5.1.2 | Bad destination system address | Domain does not resolve or MX missing |
5.1.6 | Destination mailbox has moved | Mailbox moved (forward not set up) |
5.1.7 | Bad destination mailbox address syntax | Malformed address |
5.2.1 | Mailbox disabled, not accepting messages | Mailbox disabled/inactive |
5.2.2 | Mailbox full | Storage quota exceeded |
5.2.3 | Message length exceeds administrative limit | Message too large for recipient |
5.3.0 | Other or undefined media error | General media error |
5.4.1 | Invalid destination route (no MX) | No MX record for domain |
5.5.3 | Too many recipients | Too many recipients per message |
5.7.1 | Delivery not authorised — message refused | Policy rejection (spam, content, etc.) |
REST API Error Responses
Error Response Schema
All PostMTA REST API errors return a JSON body with a consistent structure:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "You have exceeded the rate limit of 1000 requests per minute.",
"status": 429,
"details": {
"limit": 1000,
"window": "1m",
"retry_after_seconds": 42
},
"request_id": "req_01HXABC123DEF456"
}
}Error Code Reference
| HTTP Status | Error Code | Description | Resolution |
|---|---|---|---|
400 | BAD_REQUEST | Malformed request body or missing required field | Fix request JSON; check field names and types |
400 | VALIDATION_ERROR | Request passed schema validation but failed business-rule validation | Read the message field for the specific violation |
401 | AUTHENTICATION_REQUIRED | No API key provided or key is missing from the Authorization header | Add Authorization: Bearer pmta_l...xxxx header |
401 | INVALID_API_KEY | API key does not exist or has been revoked | Generate a new API key in the dashboard |
403 | INSUFFICIENT_PERMISSIONS | API key does not grant the required permission scope | Use a key with the correct scopes or regenerate with more scopes |
403 | WORKSPACE_SUSPENDED | Workspace has been suspended due to policy violation | Contact support to resolve the suspension |
404 | NOT_FOUND | The requested resource does not exist or is not accessible with this key | Verify the resource ID; check that the key has access to the owning workspace |
409 | CONFLICT | Request conflicts with existing resource state | Read the message field; usually requires a different operation order |
422 | UNPROCESSABLE_ENTITY | Request is well-formed but semantically invalid | Check the details field for field-level errors |
429 | RATE_LIMIT_EXCEEDED | Too many requests per minute or per day | Implement exponential backoff; see Retry-After header |
429 | CREDIT_LIMIT_EXCEEDED | Workspace has insufficient credits to send | Top up credits via dashboard or API |
500 | INTERNAL_ERROR | Unexpected server error | Retry with exponential backoff; report to support if persistent |
503 | SERVICE_UNAVAILABLE | PostMTA is temporarily unavailable (maintenance or overload) | Retry after the interval indicated in the Retry-After header |
Common Error Scenarios and Resolution
Connection Timeout
PostMTA will close a connection that receives no data for 30 seconds during the SMTP transaction. If you see timeout errors:
- Check whether the recipient MTA is reachable (
telnet mx.example.com 25) - Verify DNS MX resolution is working correctly
- Reduce message size if sending very large HTML bodies with many images
- Check whether a firewall is dropping packets to port 25
TLS Handshake Failure
If delivery attempts fail with a TLS negotiation error:
SSL alert: certificate verify failed (code: 20)
TLS connection could not be establishedResolution steps:
- Verify the recipient MX certificate is not expired
- Ensure your PostMTA instance has an up-to-date CA certificate bundle
- Check whether the recipient requires a specific TLS version (TLS 1.2 minimum is enforced)
SPF / DKIM Failure at Recipient
When a recipient server reports SPF or DKIM failure:
- SPF: verify your sending domain's DNS has an
SPFrecord that includes all PostMTA egress IPs - DKIM: verify the DKIM record is published in DNS and the private key is correctly configured in PostMTA
- DMARC: align your
Fromdomain with theSPFandDKIMselector domains
Rate Limit Errors (429)
Rate Limit Types
| Limit Type | Default | Scope |
|---|---|---|
| API requests | 1,000 req/min | Per API key |
| Messages injected | Varies by plan | Per workspace |
| Webhook deliveries | 200 deliveries/min | Per endpoint URL |
| Suppression imports | 1 import/hour | Per workspace |
Handling Rate Limits
When you receive a 429 response, read the Retry-After header (integer seconds) and delay accordingly. Implement exponential backoff for resilience:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1750272030Burst vs. Sustained Limits
PostMTA applies both burst (per-second) and sustained (per-minute/per-day) limits. Even if your per-minute average is within limit, a short burst above the burst threshold will return 429. Distribute message injection evenly rather than sending in large batches.
Authentication Errors (401 / 403)
Missing API Key
curl https://api.postmta.com/v1/keys
# Returns: 401 {"error": {"code": "AUTHENTICATION_REQUIRED", ...}}Always include the Authorization header:
curl https://api.postmta.com/v1/keys \
-H "Authorization: Bearer pmta_l...xxxx"Wrong Key Scope
Using a key with insufficient scopes returns 403 INSUFFICIENT_PERMISSIONS. Available scopes:
keys:read— list and view API keysdomains:manage— add, verify, and remove sending domainsmessages:send— inject messagesanalytics:read— read analytics datawebhooks:manage— create and update webhook endpoints
Key Rotation
API keys can be rotated without downtime by creating a new key, updating your application, and then revoking the old key once traffic has switched over.
Quota Exceeded Errors
Credit Limit Exceeded (429)
{
"error": {
"code": "CREDIT_LIMIT_EXCEEDED",
"message": "Workspace has insufficient credits. Required: 150. Available: 23.",
"status": 429,
"details": {
"required": 150,
"available": 23,
"top_up_url": "/app/credits"
}
}
}Top up via the dashboard at /app/credits or via the API:
curl -X POST https://api.postmta.com/v1/credits/topup \
-H "Authorization: Bearer pmta_l...xxxx" \
-H "Content-Type: application/json" \
-d '{"amount": 10000, "payment_method_id": "pmt_01HXABC123DEF456"}'Sending Domain Quota
Each workspace plan includes a maximum number of verified sending domains. Adding a new domain beyond this limit returns:
{
"error": {
"code": "DOMAIN_QUOTA_EXCEEDED",
"message": "Plan limit of 5 sending domains reached. Upgrade to Growth to add more.",
"status": 400
}
}IP Pool Quota
Each workspace plan includes a maximum number of dedicated IPs. Exceeding this returns:
{
"error": {
"code": "IP_POOL_QUOTA_EXCEEDED",
"message": "Plan limit of 2 dedicated IPs reached.",
"status": 400
}
}