Handling errors
import { Ewyn, EwynApiError } from '@ewyn/client';
try {
await client.send({ /* ... */ });
} catch (error) {
if (error instanceof EwynApiError) {
console.error('API Error:', error.status, error.message);
console.error('Details:', error.details);
if (error.status === 408) {
// Request timed out (error.code === 'TIMEOUT')
}
if (error.isRetryable()) {
// 429 or 5xx — the SDK already retried this automatically
}
}
}| Property / method | Meaning |
|---|---|
| status | HTTP status code; 408 indicates a client-side timeout |
| code | Optional error code, e.g. 'TIMEOUT' or 'NETWORK_ERROR' |
| details | Additional payload from the API (e.g. validation issues) |
| isClientError() | true for 4xx responses |
| isServerError() | true for 5xx responses |
| isRetryable() | true for 429 and 5xx — what the SDK retries automatically |
Automatic retries
The SDK retries on:
429Too Many Requests5xxserver errors- Network errors
Retries use exponential backoff with a 1-second base delay. With the default maxRetries: 3 the SDK makes up to 3 total attempts (initial + 2 retries) with delays of 1s and 2s. Timeouts (408) and other 4xx errors are not retried.
Idempotency
Provide an idempotencyKey on sends so retries — yours or the SDK's — can never produce duplicate emails:
await client.send({
to: 'user@example.com',
subject: 'Your receipt',
templateId: 'template-version-uuid',
idempotencyKey: `order-confirmation-${order.id}`,
variables: { total: order.total },
});A repeated idempotencyKey within 24 hours returns the original response instead of sending again. If the original send terminally failed, the retry re-queues it — you never get silent loss.
Wondering what the server enforces (rate limits, validation, status codes)? See API errors & rate limits.