Docs/API reference/Errors & rate limits

Errors & rate limits

Every error is a JSON payload with a stable shape, and rate limits are enforced per API key and per IP.

v1 · beta

Error shape

json
{
  "error": "Invalid request",
  "details": { /* validation issues, when applicable */ }
}

Errors you can act on programmatically also carry a stable code:

json
{
  "code": "ATTACHMENT_TYPE_BLOCKED",
  "error": "Attachments of type .exe are not allowed",
  "attachment": { "index": 1, "filename": "setup.exe" }
}

Status codes

StatusMeaningRetry?
400Validation failed — the details field lists the offending inputsno — fix the request
401Missing, malformed, or revoked API keyno
404Resource not found in this workspace (template version, contact, topic)no
410Idempotent replay of a send whose attachments have expiredno — send again with a new key
413Attachment or request body too largeno — shrink or switch to url attachments
422Recipient blocked for this type of email, unknown topic, or a url attachment could not be fetchedno
429Rate limited, or a plan quota exhaustedyes for rate limits — after backoff
5xxTransient server erroryes — with backoff

Error codes

CodeStatusMeaning
RECIPIENT_SUPPRESSED422Recipient is blocked for this type of email; the message is recorded but not sent. details says why — see below
TOPIC_NOT_FOUND422 / 404topic names no active topic in this workspace (unknown or archived). 422 from send, 404 from contacts/unsubscribe and resubscribe
QUOTA_EXCEEDED429Workspace email quota for the billing period is exhausted
ATTACHMENT_INVALID400Bad base64, non-https url, blocked host, duplicate contentId, or empty file
ATTACHMENT_TYPE_BLOCKED400Executable, script, HTML or SVG attachment
ATTACHMENT_TOO_LARGE413Inline or total attachment size limit exceeded
REQUEST_TOO_LARGE413Request body over 4 MB (inline attachments count toward it)
ATTACHMENTS_EXPIRED410Replay of a failed send whose attachments were deleted after 6 months
ATTACHMENT_FETCH_FAILED422A url attachment returned an error, timed out, redirected too often, or was empty
ATTACHMENT_QUOTA_EXCEEDED429Workspace attachment byte quota for the billing period is exhausted

RECIPIENT_SUPPRESSED carries the resolved type and topic of the send, the recorded messageId, and a details object:

json
{
  "code": "RECIPIENT_SUPPRESSED",
  "error": "maya@example.com unsubscribed from all marketing email. ...",
  "reason": "OPT_OUT_ALL",
  "details": { "kind": "opt_out", "reason": "OPT_OUT_ALL" },
  "type": "marketing",
  "messageId": "msg_..."
}

details.kind is suppression (a delivery block: HARD_BOUNCE, MANUAL, or COMPLAINT) or opt_out (OPT_OUT_TOPIC, with details.topic naming the topic slug, or OPT_OUT_ALL). Opt-outs and complaints block marketing sends only; hard bounces and manual suppressions block every send. Which block applies to which type is tabled in Subscriptions and unsubscribe.

Attachment errors include an attachment object with the index and filename of the rejected file. None of them are retried by the SDK. Bodies over 4.5 MB are rejected by the platform before reaching the API and return a plain 413 without a code.

Rate limits

  • 100 requests / minute per API key
  • 1,000 requests / hour per IP address

Exceeding either returns 429 with standard draft-6 rate limit headers (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset) so clients can back off precisely.

The SDK backs off for you.

@ewyn/client retries 429 and 5xx automatically with exponential backoff, and idempotencyKey guarantees retries never double-send.

Timeouts

The SDK aborts requests after 60 seconds by default and surfaces them as an EwynApiError with status 408 and code TIMEOUT. Timeouts are not retried automatically — pair sends with an idempotency key and retry on your side if delivery is critical.

Was this page helpful?