Sending one email in a demo is easy. Sending a hundred thousand a month in production means planning for the moments things go wrong: a revoked key, an expired card, a month that ends sooner than expected, a mail server having a bad day.
This guide explains every response the Inbixio send endpoint returns, what your code should do about each one, and how to watch delivery once you are live.
Two stages: accepted, then delivered
A call to POST /api/send-email/ happens in two stages:
- The API request. Inbixio checks your key, its permissions, your subscription, your monthly usage and the request body. If everything is in order it returns
200and queues the message. - Delivery. A moment later Inbixio connects to your SMTP server and hands the message over. Each message is recorded as sent or failed, and those results feed the figures on your dashboard.
So a 200 means “accepted for delivery”, not “in the inbox”. Problems at stage 1 come back as HTTP errors your code can handle. Problems at stage 2, such as a wrong SMTP password, show up in the dashboard’s failed-delivery rate. Inbixio makes one delivery attempt per message; it doesn’t retry a failed delivery.
Every response, and what to do
| Status | Body | Meaning | What your code should do |
|---|---|---|---|
200 |
{"message": "Email queued successfully."} |
Accepted | Done |
400 |
{"error": "to_email, subject, and message are required."} |
A field is missing or empty | Fix the request; don’t retry |
400 |
{"error": "to_email must be a single valid email address."} |
Not exactly one valid address | Fix the request; don’t retry |
400 |
{"error": "subject must be a single line of at most 500 characters."} |
Subject too long or has a line break | Fix the request; don’t retry |
400 |
{"error": "message must be text of at most 1,000,000 characters."} |
Body too large | Fix the request; don’t retry |
400 |
{"error": "No SMTP configuration found. Please add an email server first."} |
No server connected | Add a server in the dashboard |
401 |
{"detail": "API key missing"} |
No X-API-Key header |
Fix the configuration; don’t retry |
401 |
{"detail": "Invalid API key"} |
Wrong, renewed or deleted key | Update the key; alert your team |
401 |
{"detail": "API key access not allowed"} |
The key has been disabled | Use another key; alert your team |
402 |
{"error": "An active subscription is required to send emails."} |
Subscription inactive | Fix billing; alert your team |
403 |
{"error": "This API key does not have send permission."} |
Key lacks Send | Enable Send or use another key |
429 |
{"error": "Monthly email limit reached for your plan.", "limit": 25000, "sent": 25000} |
Plan limit reached | Upgrade, or hold mail until the next month; don’t retry |
429 |
{"detail": "Request was throttled. Expected available in 12 seconds."} |
Too many requests with this key in a short time (rate limit) | Wait for the Retry-After header, then retry |
503 |
{"error": "Too many emails are waiting to be sent. Please retry in a few seconds."} |
Temporarily busy | Wait for the Retry-After header, then retry |
5xx / timeout |
— | Temporary problem | Retry with backoff |
Error messages arrive under error for most responses and under detail for authentication errors, so read both:
const body = await res.json().catch(() => ({}));
const reason = body.error ?? body.detail ?? res.statusText;
Retry only what can succeed later
Retrying a 401 a hundred times won’t make the key valid; it just fills your logs. Split errors into two groups:
- Retry: network errors, timeouts,
5xxresponses and the rate-limit429(the one with adetailmessage). Wait as long as theRetry-Afterheader says, otherwise use exponential backoff with jitter, and give up after a few attempts. - Don’t retry:
400,401,402,403and the monthly-limit429(the one withlimitandsent). These need a human or a different request. Log them loudly, and alert on401,402and the monthly-limit429because they stop all your email.
// 5xx and the short-term rate limit can succeed later; the monthly limit (body has `limit`) can't.
const retryable = (status, body) => status >= 500 || (status === 429 && body.limit === undefined);
export async function sendWithRetry(payload, attempts = 4) {
for (let i = 0; i < attempts; i++) {
let res;
try {
res = await fetch('https://backend.inbixio.com/api/send-email/', {
method: 'POST',
headers: { 'X-API-Key': process.env.INBIXIO_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
signal: AbortSignal.timeout(15_000),
});
} catch (err) {
if (i === attempts - 1) throw err; // network error or timeout: retry
}
if (res?.ok) return res.json();
if (res) {
const body = await res.json().catch(() => ({}));
if (!retryable(res.status, body)) {
throw new Error(`Inbixio ${res.status}: ${body.error ?? body.detail ?? res.statusText}`);
}
}
const retryAfter = Number(res?.headers.get('Retry-After')) * 1000;
const delay = retryAfter || Math.min(30_000, 1000 * 2 ** i) * (0.5 + Math.random()); // 0.5s–30s with jitter
await new Promise((r) => setTimeout(r, delay));
}
throw new Error('Inbixio: gave up after retries');
}
About duplicates: if a request times out, it may still have been accepted. Retrying could then send the same email twice. For receipts and notifications that’s usually acceptable; for anything where a duplicate would confuse users, record what you sent in your own database and check before retrying.
Monthly limits
Each plan includes a number of emails per month: 25,000 on Starter and 150,000 on Growth. Inbixio counts successfully sent emails in the current calendar month. When you reach the limit, sends return 429 with your limit and current sent count, until the next month starts or you upgrade.
Don’t wait for the 429:
- The API Keys page shows a Usage Limit card with this month’s sent count against your plan limit.
- Set yourself a reminder to check it at around 80% of your limit, and plan an upgrade before a big launch or import.
- For a step-by-step volume estimate, see choosing an email API plan.
Delivery failures (stage 2)
When the API accepted a message but your SMTP server refused it, it counts as a failed delivery. The dashboard shows the Failed Deliveries rate for the last 7 days rather than a list of individual messages, so watch the rate and check your server settings when it rises. The usual causes:
- Authentication failed: the mailbox password changed, or the provider now requires an app password.
- Connection problems: the host or port in your server settings is wrong.
- Provider limits: your mail provider capped the mailbox’s daily sending.
- Rejected recipient: your server refused the address during sending. Bounces that arrive later as emails go to the sending mailbox, not to Inbixio, so check that inbox too.
The fix almost always lives in Email Servers or with your mail provider. SMTP settings explained covers the common cases.
What to monitor in production
Your Inbixio dashboard gives you the essentials:
- Email Sent and the Failed Deliveries rate for the last 7 days, each with the change against the 7 days before.
- Avg Latency: how long your SMTP server takes to accept mail. A sudden jump often comes before failures.
- API and email volume charts across the year, to spot trends and seasonal peaks.
- Logs of every API call: event ID, endpoint, method, status code, response time and timestamp.
On your side, track the number of non-200 responses by status code and alert when 401, 402 or the monthly-limit 429 appear. Those mean all email has stopped, and your users will notice before your logs do.
Checklist
- Treat
200as “queued”; check delivery results on the dashboard - Read both
erroranddetailfrom error responses - Retry only network errors, timeouts,
5xx,503and the rate-limit429, honouringRetry-After - Alert on
401,402and the monthly-limit429 - Keep an eye on the Usage Limit card before launches and imports
- Watch the Failed Deliveries rate and fix SMTP settings promptly
Send it with Inbixio
Connect your own SMTP server, create a scoped API key and send from any language with one HTTPS request.