YourMail

API reference

Errors

{ error: { type, message } }

All API errors use a consistent JSON shape. Every error has a machine-readable type you can switch on and a human-readable message safe to display.

Why use this

Things occasionally go wrong — a bad address, a rate limit, an unverified domain. Because every error comes back in the same predictable shape with a clear type, your code can react to each case on purpose rather than guessing from a message. This page is the reference for what each error means and how to handle it.

For example

When you're sending too fast you get a 429 with a Retry-After value — so your code can wait that many seconds and retry, instead of guessing or giving up.

Response shape

// Every error response body has this shape:
{
  "error": {
    "type": "rate_limited",
    "message": "Rate limit exceeded. Retry after 2 seconds."
  }
}

// On 429 rate_limited, the response also includes:
// Retry-After: 2   (seconds until the rate bucket refills)

Handling errors with the SDK

The SDK throws a YourMailError for any non-2xx response. It exposes status, type, message, and — when the type is rate_limited retryAfter (seconds, from the Retry-After response header).

import { YourMail, YourMailError } from "yourmail";

const yourmail = new YourMail(process.env.YOURMAIL_API_KEY!, {
  baseUrl: process.env.YOURMAIL_BASE_URL!,
});

try {
  await yourmail.send({ ... });
} catch (err) {
  if (err instanceof YourMailError) {
    console.error(err.status);    // HTTP status, e.g. 429
    console.error(err.type);      // machine-readable type, e.g. "rate_limited"
    console.error(err.message);   // human-readable description

    // Only present on rate_limited (from the Retry-After response header)
    if (err.retryAfter !== undefined) {
      console.log(`Retry after ${err.retryAfter}s`);
    }
  }
}

Error reference

All 10 error types and their typical HTTP status codes:

400validation_error(400, 413, 422)

Missing or invalid field; body, attachment, or total size too large; batch too large.

What to doFix the request. Check the message for the specific field or limit.

401authentication_error(401, 403)

401: API key is absent, malformed, or not recognised. 403: the key is valid but send-only and this endpoint needs full access, or sending is paused for the account.

What to doFor a 401, check you're sending Authorization: Bearer <key> with a valid key from the dashboard. For a 403, use a full-access key — or, if sending is paused, see deliverability_blocked below.

403domain_error

The from address uses a domain that is not verified or is not owned by this account — or the domain is verified but suspended because your plan allows fewer domains than your account holds.

What to doVerify the domain in the dashboard before sending from it. If it's suspended by plan, upgrade or reactivate it in the dashboard (swapping it for another domain on your current plan).

429quota_exceeded

Daily or monthly send quota is exhausted.

What to doWait until the quota period resets. retryAfter is not set — retry after midnight UTC or the monthly reset.

429rate_limited

Per-second send rate exceeded, or a daily cap was hit. The rate bucket is metered per message, not per request, so a batch of 50 spends 50 tokens: Free is 2 messages/sec with a burst of 10, all paid tiers 14/sec (the SES account rate) with a burst of 50. This is a reputation guard, not a billing limit — it's separate from your monthly quota, which returns quota_exceeded. Two daily caps also return this type: the 20/day test-mode cap, and the account-wide SES daily capacity guard.

What to doRead the Retry-After response header (or err.retryAfter in the SDK) and wait that many seconds before retrying. The SDK retries a per-second rate limit for you by default; a daily cap answers with the seconds until UTC midnight, which it surfaces rather than waiting out.

403deliverability_blocked

Your account's sending has been paused by the deliverability circuit breaker due to a high bounce or complaint rate.

What to doCheck the Deliverability page in the dashboard. Reduce bounces and complaints; sending resumes automatically when rates recover. Contact support if you need urgent assistance.

422sandbox_recipient

You sent from the sandbox sender (no-reply@yourmail.dev, which puts you in test mode) to a recipient other than your signup email or an AWS SES simulator address. This is narrower than it sounds: the server normally rewrites a disallowed recipient to your signup inbox rather than rejecting it, so this specific error only fires when your account has no signup email on file to rewrite to. The separate 20/day test-mode cap is a 429 rate_limited, not this error.

What to doAdd a signup email to your account (or use the dashboard's "Send test email" button, which already has one), or send to an SES simulator address instead. To go live, send from an address on your verified domain instead of the sandbox sender.

422suppressed_recipient

The recipient is on this account's suppression list (prior hard bounce or complaint from your account).

What to doRemove the address from your sends. Check the suppressions section in the dashboard.

404not_found

No email with the given ID exists in this account.

What to doCheck the ID. IDs are account-scoped — a valid ID from another account returns 404.

500internal_error

Unexpected server error.

What to doRetry with exponential backoff. If it persists, contact support.

Retry-After header — only present on rate_limited responses. The value is the number of seconds until the rate-limit bucket refills. The SDK surfaces this as err.retryAfter (a number). It is not set for quota_exceeded.