Skip to content

API Error Codes & Troubleshooting

Last updated 11 October 2026

Every error body carries a docs link back to this page, anchored to the exact code. If you followed one here, the row you want is highlighted.

Public Beta: SadaSend REST APIs, Port 587 ESMTP submission relay, hosted MCP tools for AI agents, and official zero-dependency TypeScript and Python SDKs are live. Every account receives 6,000 monthly transactional emails with strict server-enforced guardrails.

The envelope

Every failure returns the same three fields, at every status. error is stable and safe to branch on; message is written for a human reading a log and may change.

Some errors add fields — a rejected batch item carries its index, a throttled account carries the rate that caused it. Treat anything beyond the three below as advisory.

JSON
{
  "error": "recipient_not_allowlisted",
  "message": "Key allowlist is [@example.com]. Recipient is outside it.",
  "docs": "https://www.sadasend.com/docs/errors#recipient_not_allowlisted"
}

Authentication and scope

CodeStatusCause & Step-by-Step Remediation
missing_api_key401No Authorization header provided. Fix: Pass Authorization: Bearer sada_live_sk_... or set SADASEND_API_KEY in your environment.
invalid_api_key401The API key does not exist, or was permanently revoked. Fix: Check key prefix (sada_live_sk_...) or generate a new key under /app/keys. Keys cannot be recovered once shown.
unauthenticated401A dashboard session cookie was required and none was present. Fix: Sign in through /login to obtain an active session cookie. API keys do not authenticate dashboard-internal routes.
insufficient_scope403The key is valid but its permission scope set excludes this operation. Fix: Scopes are immutable after creation. Mint a new key with send, messages:read, or full_access under /app/keys.
api_key_expired401The API key has passed its expiration timestamp (expires_at). Fix: Mint a new key or update the expiration date in the dashboard under /app/keys.
ip_not_allowed403The client IP address is not permitted by this key’s CIDR IP allowlist (ip_allowlist). Fix: Add your server egress IPv4/IPv6 address or subnet (e.g. 198.51.100.1/32) to the key settings in /app/keys.
forbidden_origin403Browser Origin header from an untrusted origin was rejected. Fix: Invoke the API from backend application servers or authorized domains.
key_revoked409The key that originally sent this message has since been revoked, so it cannot be used to resend it. Fix: Re-dispatch the message using an active API key.
plan_tier_insufficient403Feature requires an upgraded plan tier (e.g. Slack 1-Click Interactive Approvals requires Starter or higher). Fix: Upgrade your plan tier under /app/billing.

Sending

The codes worth handling explicitly. A send that fails with any of these has not been queued and will not be retried for you.

CodeStatusCause & Step-by-Step Remediation
missing_from400Missing from sender email. Fix: Provide a valid sender address belonging to a verified domain on your account (e.g. "from": "billing@yourdomain.com").
missing_to400Missing to recipient. Fix: Provide at least one valid recipient address as a string or array of strings.
invalid_from400The from address is syntactically invalid, or its domain is not configured on your account. Fix: Verify domain spelling or register the domain under /app/domains.
invalid_source400Header X-SadaSend-Source or JSON source field must be one of: agent, backend, automation, or system.
invalid_agent_name400Header X-SadaSend-Agent or JSON agent_name exceeds 128 characters. Fix: Use a concise bot identifier (e.g. "SupportBot").
domain_not_found403Sending domain is not registered on this account. Fix: Add the domain under /app/domains (POST /domains) before sending.
domain_not_verified403Domain exists on your account but SPF, DKIM, or DMARC DNS records have not verified. Fix: Publish the required DNS records from /app/domains and run a DNS check.
recipient_not_allowlisted403The API key enforces a recipient allowlist and this address is outside it. Fix: Add the recipient or wildcard domain (*@example.com) to allowlist in /app/keys, or test with permitted addresses.
sandbox_limit_reached403The shared onboarding domain (sadasend.net) lifetime send cap was reached. Fix: Add and verify your own custom domain in /app/domains to lift all sandbox limits.
recipient_suppressed422Recipient address is on your suppression list due to a prior hard bounce, spam complaint, or unsubscribe. Fix: Check status and manually remove under /app/suppressions if delivery is now safe.
attachment_too_large422An individual attachment exceeds the 10MB limit. Fix: Compress the file or host externally and provide a download link in the email body.
attachments_too_large422Combined attachments exceed the 25MB total limit per message. Fix: Reduce total attachment payload size below 25MB.
invalid_attachment422Attachment object is missing required filename or content, or base64 data is malformed. Fix: Validate base64 string format and provide filename.
quota_exceeded402Monthly email credit quota exhausted on your current plan. Fix: Upgrade your plan or purchase on-demand elastic credit packs under /app/billing.
account_suspended403Account is suspended due to excessive complaint rate (>0.25%) or security policy violation. Fix: Contact support at support@sadasend.com to review deliverability metrics.
payload_too_large413The HTTP request body exceeds maximum allowed size (25MB for mail dispatches, 1MB for API metadata). Fix: Reduce message body or attachment payload size.
account_throttled429Account complaint or bounce rate exceeded the warning threshold (0.10%). Transactional mail continues at reduced dispatch velocity. Fix: Review bounce forensics in /app/logs.
rate_limit_exceeded429API key or client IP exceeded burst rate limits. Fix: Inspect the Retry-After response header and implement exponential backoff retry logic.
daily_limit_reached429Free plan daily recipient limit reached. Fix: Wait for the rolling 24-hour window indicated by retryAfterSeconds, or upgrade to Starter ($12/mo) for unmetered daily sends.
idempotency_key_reused409Header Idempotency-Key UUID was previously used for a different request payload. Fix: Reuse the key only to retry identical sends; generate a fresh UUID for new emails.
idempotency_key_in_progress409The initial request with this Idempotency-Key is currently being processed. Fix: Wait 500ms and retry without minting a new key.
attachment_storage_failed503Transient storage cluster timeout saving attachment payload. Fix: The send was not accepted into queue; retrying with exponential backoff is safe.
dlp_secret_detected422Outbound Data Loss Prevention (DLP) scanner blocked email containing exposed cryptographic private keys, database credentials, or API tokens. Fix: Check preview and location in the error response to remove credentials, or pass "dlp": false in JSON for trusted diagnostic scripts.
dlp_violation403Pre-flight Data Loss Prevention policy violation: sensitive credentials or PII detected in outgoing message content. Fix: Remove matched credentials or credit card numbers from body text or HTML, or pass "dlp": false in JSON for trusted diagnostic tools.
duplicate_content_loop_detected429Autonomous loop circuit breaker tripped: identical body content hash sent to the exact same recipient within 5 minutes. Fix: Resolve agent loop / retry bug. For legitimate re-sends, vary content or wait 300 seconds.
prompt_injection_flagged422Inbound email classified with high prompt injection score (>0.85) containing jailbreak instructions or delimiter overrides. Fix: Quarantine message before agent context ingestion, or parse with /inbound/parse to extract clean Markdown.

Remediating autonomous guardrail & security errors

Detailed diagnostic breakdowns and resolution recipes for the built-in safety controls and autonomous agent error conditions.

Outbound DLP secret detection (dlp_secret_detected & dlp_violation)

Autonomous AI agents running code, analyzing data, or querying internal APIs frequently handle sensitive runtime credentials (API keys, database strings, tokens, private keys). If an agent dumps debug context or runtime memory into an outgoing email, the Outbound DLP scanner intercepts it before network transmission.

The scanner inspects subject, text, and raw html (including HTML attributes, data properties, URLs, and comments) for:

• Provider API Secrets: Anthropic Claude (sk-ant-api03-...), OpenAI (sk-...), AWS Access Key IDs (AKIA...), GitHub PATs (ghp_... and github_pat_...), Stripe live keys (sk_live_..., rk_live_...), Slack bot tokens (xoxb-...), Google Cloud, SendGrid, and Resend.

• Cryptographic Private Keys: RSA, EC, OpenSSH, DSA, and encrypted private key blocks (-----BEGIN ... PRIVATE KEY-----).

• Payment Card Industry (PCI) PII: Visa, Mastercard, American Express, and Discover formats verified via Luhn mod 10 checksum validation to eliminate false positives on serial numbers.

• Government IDs: US Social Security Numbers (SSNs) with valid area/group structure (rejecting 000, 666, and 900+ series).

When intercepted, the API returns HTTP 422 with a cryptographically safe redacted preview and the exact location where the secret was found:

  • Step 1 — Identify the leaked variable: Check location (subject, text, or html) and search your template or agent prompt for the token prefix shown in preview.
  • Step 2 — Scrub runtime environment variables: Ensure debug variables (process.env, os.environ, API client responses) are not serialized into the prompt context or email body template.
  • Step 3 — Diagnostic Opt-Out: If you are intentionally running internal diagnostic tests or penetration tests, pass "dlp": false in the request body to bypass the pattern matcher.
JSON
{
  "error": "dlp_secret_detected",
  "message": "Outbound DLP scanner blocked email containing exposed Anthropic API token",
  "preview": "sk-ant***890",
  "location": "html",
  "docs": "https://www.sadasend.com/docs/errors#dlp_secret_detected"
}

Autonomous loop circuit breaker (duplicate_content_loop_detected)

Autonomous AI agents operating in agentic tool-use loops can enter infinite execution cycles due to retry bugs, unhandled exceptions, or looping reasoning models. Repeatedly dispatching identical emails burns customer quotas and damages sender reputation.

SadaSend computes a deterministic rolling SHA-256 fingerprint over sorted recipients, subject, and normalized body text with a 5-minute (300-second) sliding window:

• Transparent Deduplication: If an identical email is re-sent to the same recipient within 5 minutes, SadaSend transparently deduplicates the send, returning { ok: true, deduplicated: true } and echoing the original messageId without consuming monthly quota or queuing duplicate spam.

• Circuit Breaker Trip: If rapid runaway bursts continue beyond the deduplication threshold, HTTP 429 duplicate_content_loop_detected is returned.

• Broadcast Safety: The fingerprint binds strictly to the recipient set. Sending identical newsletters or transactional receipts to different customers is never deduplicated.

  • Fix: Investigate your agent’s termination condition or exception handling. Ensure your agent checks for existing delivery IDs before re-issuing tool calls.
JSON
{
  "error": "duplicate_content_loop_detected",
  "message": "Circuit breaker tripped: autonomous loop detected after matching 5 identical body content hashes within a 5-minute rolling window",
  "docs": "https://www.sadasend.com/docs/errors#duplicate_content_loop_detected"
}

Inbound prompt injection threat (prompt_injection_flagged)

When autonomous agents process incoming emails, adversaries can embed prompt injections, system prompt overrides, or jailbreak instructions within reply text.

SadaSend runs edge heuristic classifiers on inbound emails. If the risk score exceeds 0.85, the payload is flagged with HTTP 422 prompt_injection_flagged:

  • Fix: Ingest inbound emails through POST /inbound/parse to extract token-efficient sanitized Markdown (cleanMarkdown) stripped of quoted chains and mobile signatures, and evaluate security.quarantineRecommended before passing text to LLM context.
JSON
{
  "error": "prompt_injection_flagged",
  "message": "Inbound email classified with high prompt injection score (>0.85) containing jailbreak instructions or delimiter overrides",
  "docs": "https://www.sadasend.com/docs/errors#prompt_injection_flagged"
}

Batches, logs and message actions

CodeStatusCause & Step-by-Step Remediation
empty_batch400The batch request body contained an empty emails array. Fix: Ensure your payload contains at least 1 message before submitting POST /v1/batch.
batch_too_large400The batch exceeds the maximum per-call limit of 100 messages. Fix: Split larger dispatches into chunks of 100 or fewer messages.
missing_emails400The batch JSON body was missing the required emails array. Fix: Wrap your message objects in a top-level emails array: {"emails": [...]}.
invalid_status400Unrecognized status filter provided to GET /v1/logs?status=.... Fix: Filter by supported statuses: queued, delivered, bounced, failed, held, or cancelled.
invalid_date400A date filter parameter (from or to) could not be parsed. Fix: Use standard ISO 8601 timestamps (e.g. 2026-10-25T00:00:00Z).
invalid_cursor400The pagination cursor is malformed, expired, or generated from a different query. Fix: Omit the cursor query parameter to restart pagination from page 1.
message_not_found404No message matching the given ID exists on this account. Fix: Verify the id returned from POST /v1/send and confirm you are querying with the matching workspace API key.
not_cancellable409The message has already completed dispatch or departed relay queues. Fix: Only scheduled future sends currently in queued status can be cancelled via DELETE /v1/messages/:id.
body_unavailable409The rendered message body has aged past your plan retention window (30 to 90 days). Fix: Upgrade your plan under /app/billing for extended retention, or archive critical message content in your application storage.
no_originating_key409The original API key that queued the message was revoked or deleted. Fix: Re-submit the send request using an active API key.

Domains, keys and approvals

CodeStatusCause & Step-by-Step Remediation
invalid_domain400The domain name is syntactically invalid or uses an unsupported format. Fix: Provide a fully-qualified domain name without protocols or paths (e.g. domain.com or mail.domain.com).
domain_exists409This domain is already registered on your account or another workspace. Fix: Verify domain ownership or delete the domain from any test workspace before adding.
domain_quota_exceeded403The maximum registered sending domains limit for your current subscription plan has been reached. Fix: Remove unused domains or upgrade your plan tier under /app/billing.
history_not_found404Deleted domain record was not found in audit history for 1-click restore. Fix: Re-add the domain as a new record and configure fresh DKIM DNS records.
restore_failed500Database error while restoring deleted domain and its preserved DKIM keys. Fix: Retry domain restoration in /app/domains or re-register the domain anew.
invalid_scope400Unknown permission scope requested during API key creation. Fix: Specify valid scopes: mail:send, mail:batch, messages:read, domains:read, analytics:read, or webhooks:write.
invalid_mode400Key execution mode is unrecognized. Fix: Set mode to live (immediate dispatch), test (sandbox dry run), or approval (holds all outgoing messages for human dashboard sign-off).
invalid_rate_limit_max400rate_limit_max must be an integer between 1 and 1,000,000. Fix: Supply a positive integer limit or leave empty to inherit default account throughput.
invalid_rate_limit_window400rate_limit_window is unrecognized. Fix: Choose one of the supported rate limit duration windows: 1s, 1m, 1h, or 1d.
invalid_expires_at400Key expiration date is invalid or in the past. Fix: Provide a valid future ISO 8601 timestamp (e.g. 2027-01-01T00:00:00Z).
invalid_ip_allowlist400IP allowlist contains malformed addresses or CIDR masks. Fix: Provide valid IPv4, IPv6 addresses, or CIDR blocks (e.g. 198.51.100.1 or 198.51.100.0/24).
invalid_default_source400Default source identifier is unrecognized. Fix: Specify one of: agent, backend, automation, or system.
invalid_allowlist400Recipient allowlist was passed as an empty array. Fix: Provide at least one recipient email address/domain pattern (e.g. *@company.com), or omit property to permit all destinations.
key_not_found404No API key matching the provided ID exists on this account. Fix: Check active keys under /app/keys or mint a new key.
invalid_decision400Approval decision was unrecognized. Fix: Submit {"decision": "approve"} or {"decision": "reject"} when resolving held messages at POST /v1/approvals/:id.
already_decided409The held send has already been approved or rejected. Fix: Decisions are immutable. Submit a new send if resubmission is required.

Webhooks

CodeStatusCause & Step-by-Step Remediation
missing_url400Webhook endpoint definition missing required url property. Fix: Provide a secure destination URL (e.g. https://api.yourdomain.com/webhooks).
invalid_url400Destination URL is malformed or could not be parsed. Fix: Ensure the URL includes protocol and a valid hostname.
insecure_url400Destination URL uses unencrypted HTTP. Fix: Endpoints must use TLS (https://) to protect cryptographically signed delivery events in transit.
no_events400The event subscriptions array was empty. Fix: Subscribe to at least one event (e.g. ['email.delivered', 'email.bounced']).
unknown_event400One or more subscribed event names are unrecognized. Fix: Use supported event names: email.sent, email.delivered, email.bounced, email.complained, email.opened, email.clicked, or email.held.
invalid_id400Webhook identifier format is invalid. Fix: Verify webhook ID format returned during endpoint registration.
invalid_json400The request payload was not valid JSON. Fix: Validate JSON syntax and ensure Content-Type: application/json is sent.
missing_target400Neither webhookId nor url was provided for test event dispatch. Fix: Provide an existing webhook endpoint ID or an explicit test URL.
unsupported_protocol400Simulation target URL does not use HTTP or HTTPS. Fix: Use http://localhost:PORT/webhook (local testing) or https:// endpoint.
invalid_range400Event replay window is malformed or exceeds maximum historical limit (30 days). Fix: Specify valid from and to timestamps within the last 30 days.
webhook_not_found404No webhook endpoint matching that ID exists on this account. Fix: Review registered endpoints under /app/webhooks.
webhook_disabled409Endpoint was automatically disabled after repeated consecutive delivery failures. Fix: Fix your receiving endpoint, verify HTTP 200 response, and re-enable the endpoint under /app/webhooks before replaying events.
signature_mismatch401HMAC-SHA256 signature verification failed on Slack or webhook callback. Fix: Verify that your account signing secret matches the secret configured in your receiving handler.
timestamp_out_of_range401Request timestamp difference exceeds the 5-minute replay defense tolerance window. Fix: Ensure your server clock is synchronized with NTP.

Templates and suppressions

CodeStatusCause & Step-by-Step Remediation
missing_name400Template creation payload missing required name property. Fix: Provide a unique template identifier name.
name_too_long400Template or API key name exceeds 100 characters. Fix: Shorten the descriptive name to under 100 characters.
missing_subject400Template missing required default subject line. Fix: Add a subject line (supporting Liquid variable interpolation).
missing_content400Template contains neither html nor text content. Fix: Provide at least one rendered content format (html or text).
template_not_found404No template with that ID or name exists on this account. Fix: Check template catalog under /app/templates.
template_too_large413Template source exceeds the 512KB size limit. Fix: Optimize inline CSS and externalize large media assets to CDN URLs.
template_render_failed422Liquid template syntax error or missing mandatory variable. Fix: Inspect the error message identifying the exact missing tag or syntax error.
invalid_address400Email address provided for suppression is malformed. Fix: Supply a standard RFC 5322 compliant email address (e.g. user@example.com).
invalid_tag400Message tag exceeds 50 characters or contains disallowed characters. Fix: Use alphanumeric characters, dots, underscores, colons, and hyphens ([a-zA-Z0-9._:-]).
too_many_tags400Message specifies more than 10 tags. Fix: Reduce tags to 10 or fewer per message.
unsupported_engine400SadaSend server-side templates render with Liquid. Fix: Use Liquid syntax for server-rendered templates, or pre-render React Email templates client-side (see /docs/react-email).
suppression_not_found404The specified email address is not in your account suppression list. Fix: Check active suppressions under /app/suppressions.

Account, team and support

These come from the dashboard-facing routes rather than from an integration, but they use the same envelope.

CodeStatusCause & Step-by-Step Remediation
invalid_body400Request body was not a valid JSON object. Fix: Ensure payload is formatted as a JSON object with Content-Type: application/json.
nothing_to_update400PATCH request contains no recognized or modifiable properties. Fix: Check API schema for allowed fields on the target resource.
invalid_name400Account or user name is empty or exceeds character limits. Fix: Provide a valid name string (1–100 characters).
missing_org400Organization name is required. Fix: Specify an organization name when provisioning team workspaces.
org_too_long400Organization name exceeds 100 characters. Fix: Shorten organization name under 100 characters.
invalid_country400Unrecognized country code. Fix: Use standard 2-letter ISO 3166-1 alpha-2 country codes (e.g. US, GB, DE).
invalid_phone400Phone number could not be parsed or validated. Fix: Use E.164 international format (e.g. +14155552671).
invalid_website400Organization website is not a valid URL. Fix: Provide a valid URL including protocol (e.g. https://company.com).
invalid_industry400Selected industry is not recognized. Fix: Select a valid industry option from the account settings dropdown.
invalid_referral400Selected referral source is not recognized. Fix: Choose a recognized referral channel from onboarding options.
invalid_account_type400Account type must be individual or team. Fix: Specify a valid account type value.
invalid_email400Email address format is invalid. Fix: Supply a valid RFC 5322 email address.
disposable_email400Address belongs to a disposable or temporary email provider. Fix: Use a permanent work or business email domain.
owner_only403The requested action requires workspace Owner privileges. Fix: Request the workspace owner to perform the action or adjust your role permissions under /app/team.
account_not_found404No account matching the identifier exists. Fix: Check account session or sign in again.
seat_quota_reached402Maximum team seats reached for your current plan tier. Fix: Upgrade to Scale plan or remove inactive team members under /app/team.
webhook_quota_reached402Maximum registered webhooks reached for your current plan. Fix: Delete unused endpoints under /app/webhooks or upgrade plan tier.
template_quota_reached402Maximum custom template limit reached. Fix: Upgrade your plan or archive obsolete templates under /app/templates.
request_limit_reached429Account-wide daily API request quota reached. Fix: Inspect Retry-After header or upgrade plan under /app/billing for higher burst and daily API allowances.
already_a_member409The invited user is already an active member of this workspace. Fix: Manage member permissions directly under /app/team.
invitation_not_found404Invitation has expired, been revoked, or already accepted. Fix: Issue a fresh invitation link from /app/team.
missing_message400Support ticket request lacks message body. Fix: Provide a descriptive message explaining your issue.
message_too_long400Support ticket message exceeds 5,000 characters. Fix: Summarize ticket details and attach diagnostic logs if necessary.
ticket_not_found404No support ticket with that ID exists on this account. Fix: Verify ticket ID in /app/support.

Free tools and sign-in

The tool checks are unauthenticated and rate-limited by source. The sign-in codes surface in the browser rather than in an integration.

CodeStatusCause & Step-by-Step Remediation
unknown_check404The requested diagnostic check type is not supported. Fix: Supported tools include spf, dkim, dmarc, mx, bimi, smtp-check, and blacklist-check.
invalid_selector400The DKIM selector syntax is malformed. Fix: Provide a valid DKIM selector name (e.g. s1, sada1, google).
too_many_checks429Rate limit exceeded for unauthenticated free tool diagnostic lookups. Fix: Wait 60 seconds before issuing further DNS diagnostics, or sign in for unmetered checks.
missing_token400Magic sign-in link lacked the authentication token query parameter. Fix: Click the complete link received in your email or request a new login link.
no_identity400Third-party OAuth provider did not return a verified email identity. Fix: Ensure your GitHub or Google account has a verified primary email address.
state_invalid400OAuth state token mismatch (cross-site request forgery prevention). Fix: Clear cookies and initiate sign-in again from the official login page.
unsupported_provider400Unrecognized authentication provider. Fix: Sign in using email (magic link), github, or google.
oauth_not_configured501The requested OAuth provider is not enabled on this environment. Fix: Use email magic link authentication.