Skip to content

Error Handling

Error Classes

All errors thrown by the SDK extend OutboundError. Each error has:

  • message — Human-readable error message
  • statusCode — HTTP status code (0 for network/timeout errors)
  • details — Additional error details from the API (if any)
  • requestId — Request ID for debugging (if returned by the API)
ts
import { Outbound, OutboundError, RateLimitError } from '@masters-union/outbound-sdk';

try {
  await outbound.email.send({ ... });
} catch (err) {
  if (err instanceof OutboundError) {
    console.error(`Error ${err.statusCode}: ${err.message}`);
    console.error('Details:', err.details);
  }
}

Error Types

ClassStatusWhen
BadRequestError400Invalid parameters, missing required fields
AuthenticationError401Invalid or missing API key
ForbiddenError403Account disabled, tenant suspended
NotFoundError404Resource not found (job, template, webhook)
ConflictError409Duplicate resource (e.g., email already suppressed) or idempotency key in-flight
RateLimitError429Too many requests (auto-retried by default)
ServerError5xxServer-side error (auto-retried by default)
TimeoutErrorRequest exceeded timeout
NetworkErrorNetwork connectivity failure

Catching Specific Errors

ts
import {
  BadRequestError,
  AuthenticationError,
  NotFoundError,
  RateLimitError,
} from '@masters-union/outbound-sdk';

try {
  await outbound.email.send(params);
} catch (err) {
  if (err instanceof BadRequestError) {
    // Fix your request parameters
    console.error('Bad request:', err.message);
  } else if (err instanceof AuthenticationError) {
    // Check your API key
    console.error('Auth failed:', err.message);
  } else if (err instanceof NotFoundError) {
    // Resource doesn't exist
    console.error('Not found:', err.message);
  } else if (err instanceof RateLimitError) {
    // Only thrown after all retries exhausted
    console.error(`Rate limited. Retry after: ${err.retryAfter}s`);
  }
}

Rate Limit Errors

RateLimitError has an additional retryAfter property (in seconds):

ts
try {
  await outbound.email.bulk({ ... });
} catch (err) {
  if (err instanceof RateLimitError) {
    console.log(`Retry after ${err.retryAfter} seconds`);
    // Note: The SDK already retried 3 times before throwing this.
    // This error means all retries were exhausted.
  }
}

TIP

By default, the SDK retries 429 errors 3 times with exponential backoff. You only see a RateLimitError if all retries fail. Adjust with maxRetries in the constructor.

What causes a 429?

Two things can return 429 from a send endpoint (/v1/email/send, /v1/email/bulk, /v1/templates/send, /v1/templates/bulk):

  1. Your per-second request rate limit.
  2. The global AWS SES account 24-hour sending quota. Message: "AWS SES daily sending quota exhausted (X/Y). Try again later."

Your tenant daily/monthly quotas do not return 429 — they are tracked for reporting and billing only. See Quotas in Getting Started.

Released under the MIT License.