Errors
The Arsel API uses standard HTTP status codes and returns errors in a consistent JSON format.
Error Response Format
All errors follow this structure:
{
"status_code": 422,
"name": "validation_error",
"message": "to must be an array, each value in to must be an email"
}
| Field | Type | Description |
|---|---|---|
status_code | number | The HTTP status code |
name | string | A machine-readable error identifier in snake_case |
message | string | A human-readable description of what went wrong |
Status Codes
| Code | Name | When It Happens |
|---|---|---|
| 202 | Accepted | The request was accepted and the message is queued for delivery |
| 200 | OK | The request succeeded (used for GET requests) |
| 400 | bad_request | The request body is malformed, the sender email is not on a verified domain, or an account-level rule rejects the send. See Common Send Errors below. |
| 401 | unauthorized | The API key is missing, invalid, expired, or revoked |
| 403 | forbidden | The API key does not have permission for this action |
| 404 | not_found | The requested resource does not exist |
| 409 | conflict | The request conflicts with the current state of the resource |
| 422 | validation_error | The request body failed validation (e.g., invalid email format) |
| 429 | rate_limit_exceeded | You have exceeded the per-second request limit |
| 429 | monthly_quota_exceeded | Your organization has used its monthly email (or SMS) quota. See Common Send Errors. |
| 500 | internal_server_error | An unexpected error occurred on our end |
Common Send Errors
A successful authentication and well-formed request can still be rejected at send time — for example, because the sender domain isn't verified or the organization's quota is exhausted. These are returned with 400 / name: bad_request unless stated otherwise.
| Scenario | Status | name | Example message |
|---|---|---|---|
| Sender domain not verified | 400 | bad_request | Sender email domain is not verified. Verify the domain in Settings > Domains. |
| Sender email format invalid or not allowed | 400 | bad_request | Sender email is required / Invalid sender email |
| Recipient list empty or address invalid | 400 | bad_request | At least one recipient is required. |
Total recipients (to + cc + bcc) exceeds 50 | 400 | bad_request | Too many recipients. Maximum 50 allowed, but N provided. |
| Attachment MIME type blocked or extension blocked | 400 | bad_request | Attachment type 'application/x-msdownload' is not allowed |
| Organization suspended (high bounce/complaint rate) | 400 | bad_request | Email sending is suspended for this organization due to high bounce/complaint rates. Please contact an admin to review and unsuspend. |
| Monthly email quota exhausted | 429 | monthly_quota_exceeded | Monthly email quota exceeded |
monthly_quota_exceeded is distinct from rate_limit_exceeded. The first means you've used your plan's allotment for the month; the second means you're sending faster than the per-second limit. Retrying the quota error won't help until the next billing period or a plan upgrade.
Validation Errors
When a request fails validation, the message field contains a comma-separated list of all validation issues:
{
"status_code": 422,
"name": "validation_error",
"message": "from must be an email, to should not be empty, subject must be shorter than or equal to 998 characters"
}
Handling Errors
Check status_code first to determine the category of error. Use name for programmatic error handling, and message for logging or displaying to users.
const response = await fetch("https://api.arsel.sa/v1/email/send", options);
const data = await response.json();
if (response.status >= 400) {
switch (data.name) {
case "unauthorized":
// Refresh or rotate your API key
break;
case "validation_error":
// Fix the request payload
break;
case "rate_limit_exceeded":
// Back off and retry
break;
default:
// Log and alert
console.error(`API error: ${data.name} — ${data.message}`);
}
}