Docs/Error Codes
API Reference

Error Codes

Every error from the SendFleet API follows a consistent shape. Use the machine-readable error field to branch your error-handling logic; use detail to log or surface a human-readable message.

Error response shape

All non-2xx responses (and some 400-level responses) return a JSON body.

Error body
{
  "success": false,
  "error":   "domain_not_verified",
  "detail":  "Domain 'acme.com' is not yet verified by SES. Add the TXT record and click Verify in the dashboard."
}

HTTP 400 - Bad Request

The request body is malformed, missing required fields, or contains invalid values. The response contains field-level validation errors.

400 - Validation errors
{
  "to":      ["Enter a valid email address."],
  "message": ["This field may not be blank."]
}

Fix the fields listed in the response. Do not retry without modifying the request.

HTTP 401 - Unauthorized

The X-API-Key header is missing, the key is invalid, the key has been revoked, or the key has expired.

401 - No credentials
{
  "detail": "Authentication credentials were not provided."
}
CauseFix
Header missing entirelyX-API-Key: YOUR_KEY to every request.
Wrong header nameMust be exactly X-API-Key (case-insensitive on most clients).
Key revokedGenerate a new key in Dashboard → API Keys.
Key expiredGenerate a new key or extend the expiry date.
Key not foundVerify you copied the full secret, including the part after the dot.
After 10 consecutive 401 failures from the same IP within 60 seconds, that IP is temporarily blocked. Back off and investigate before retrying.

HTTP 403 - Forbidden

The API key is valid but the account or domain configuration prevents sending. These errors require action in the dashboard - retrying the same request will always fail.

errorCauseFix
unauthorizedAccount email not verified, account not approved for Shared SES, or no active Growth/Pro subscription.Verify your email address. For Shared SES: submit a verification request and wait for approval. For BYOC: email verification is all that's needed.
domain_not_registeredThe domain in from_email has not been added to your account.Go to Dashboard → Domains → Add domain. Then complete DNS verification.
domain_not_verifiedDomain is registered but SES has not confirmed ownership (TXT record missing or not propagated).Add the TXT ownership record at your DNS provider, wait for propagation (up to 48 h), and click Verify in the domain detail page.
dkim_not_verifiedDomain ownership is verified but the 3 DKIM CNAME records are not confirmed.Add all 3 CNAME records shown in the domain detail page, wait for propagation, and click Verify DKIM.
domain_mode_mismatchThe domain's sending mode (BYOC or Shared) doesn't match your current plan.Either upgrade/change your plan to match, or add a new domain in the correct mode.
sending_suspendedYour account's API sending has been suspended, typically due to high bounce or complaint rates.Contact support to discuss reinstatement. Review your recipient list quality.

HTTP 422 - Unprocessable Entity

errorCauseFix
recipient_suppressedThe to address is on your suppression list due to a previous hard bounce or spam complaint.Remove the address from your recipient list. Do not attempt to resend to suppressed addresses - this will damage your sender reputation. Contact support if a suppression was added in error.
Never retry suppressed addresses. Suppression exists to protect your sender reputation with AWS SES. Repeatedly hitting suppressed addresses accelerates your bounce rate and may trigger account suspension.

HTTP 429 - Too Many Requests

Two distinct rate limits can produce a 429:

errorCauseRetry strategy
usage_limit_exceededMonthly email quota exhausted for your plan (50 for Starter BYOC, 25,000 for Growth, 100,000 for Pro).Upgrade your plan in Dashboard → Billing. Monthly quota resets at the start of each calendar month.
(none - DRF throttle)Per-key rate limit exceeded (60 req/min).Implement exponential backoff. Wait at least 1 second before retrying. Spread burst sends over multiple seconds.

HTTP 502 - Bad Gateway

errorCauseRetry strategy
queue_errorAWS SQS was temporarily unavailable when we tried to enqueue your email.Safe to retry. Use exponential backoff: wait 1 s, 2 s, 4 s, 8 s. After 3–4 retries, treat as a permanent failure and alert.

HTTP 503 - Service Unavailable

errorCauseFix
byoc_not_configuredSendFleet's BYOC relay credentials are not configured on our infrastructure.This is an issue on our end. Contact support. Do not retry indefinitely.

HTTP 500 - Internal Server Error

An unexpected error on our end. These are rare. Retry once with exponential backoff. If the error persists, contact support with the approximate timestamp and your request details.

Retry strategy reference

HTTP statusRetryable?Strategy
400NoFix the request payload first.
401NoFix the API key first.
403NoFix account/domain config in dashboard.
422NoRemove suppressed address from list.
429 (usage)NoUpgrade plan or wait for monthly reset.
429 (rate limit)YesExponential backoff starting at 1 s.
502YesExponential backoff, max 4 attempts.
503NoContact support.
500YesOne retry after 2–5 s, then alert.

Error handling example

import requests, time

def send_email(payload, retries=3):
    for attempt in range(retries):
        resp = requests.post(
            "https://sendfleet.net/api/send/",
            headers={"X-API-Key": "YOUR_KEY"},
            json=payload,
            timeout=10,
        )

        if resp.status_code == 200:
            return resp.json()

        data = resp.json()
        error_code = data.get("error")

        # Non-retryable errors - fix and stop
        if resp.status_code in (400, 401, 403, 422):
            raise ValueError(f"[{error_code}] {data.get('detail')}")

        # Usage limit - upgrade plan
        if error_code == "usage_limit_exceeded":
            raise RuntimeError("Monthly quota exhausted")

        # Retryable - backoff and retry
        if resp.status_code in (429, 502, 500):
            time.sleep(2 ** attempt)
            continue

        break

    raise RuntimeError("Max retries exceeded")